1

I want to update the APP automatically when a new version is available without user interaction, suppose a new version is available and it is downloaded to Downloads directory in android.

I followed the following example.

Java Sample: https://stackoverflow.com/a/51705614

Kotlin Sample: https://www.sisik.eu/blog/android/dev-admin/update-app

facing this exception

system_process W/ActivityManager: Permission Denial: not allowed to send broadcast android.intent.action.PACKAGE_ADDED from pid=-1, uid=10191
system_process W/ActivityManager: Unable to send startActivity intent
    java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.PACKAGE_ADDED from pid=-1, uid=10191
        at com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:21323)
        at com.android.server.am.ActivityManagerService.broadcastIntentInPackage(ActivityManagerService.java:21974)
        at com.android.server.am.PendingIntentRecord.sendInner(PendingIntentRecord.java:372)
        at com.android.server.am.PendingIntentRecord.sendWithResult(PendingIntentRecord.java:245)
        at com.android.server.am.ActivityManagerService.sendIntentSender(ActivityManagerService.java:8446)
        at android.content.IntentSender.sendIntent(IntentSender.java:191)
        at android.content.IntentSender.sendIntent(IntentSender.java:155)
        at com.android.server.pm.PackageInstallerService$PackageInstallObserverAdapter.onUserActionRequired(PackageInstallerService.java:888)
        at android.app.PackageInstallObserver$1.onUserActionRequired(PackageInstallObserver.java:28)
        at com.android.server.pm.PackageInstallerSession.commitLocked(PackageInstallerSession.java:951)
        at com.android.server.pm.PackageInstallerSession.access$200(PackageInstallerSession.java:120)
        at com.android.server.pm.PackageInstallerSession$3.handleMessage(PackageInstallerSession.java:294)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:193)
        at android.os.HandlerThread.run(HandlerThread.java:65)

AndroidManifest.xml

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AutoUpdateAppPractice"
        android:usesCleartextTraffic="true">

        <receiver
            android:name="UpdateReceiver"
            android:enabled="true"
            android:exported="true">

            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_REPLACED" />
                <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
            </intent-filter>

        </receiver>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <action android:name="android.intent.action.INSTALL_PACKAGE" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

CustomPackageInstaller.java

package com.odine.autoupdateapppractice;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class CustomPackageInstaller {

    public static void installPackage(Context context, String installSessionId, String packageName, InputStream apkStream) {

        PackageManager packageManger = context.getPackageManager();
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();

        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);

        params.setAppPackageName(packageName);
        PackageInstaller.Session session = null;

        try {
            Log.e(TAG, "installPackage: try");

            int sessionId = packageInstaller.createSession(params);
            session = packageInstaller.openSession(sessionId);
            OutputStream out = session.openWrite(installSessionId, 0, -1);
            byte buffer[] = new byte[1024];
            int length;
            int count = 0;
            while ((length = apkStream.read(buffer)) != -1) {
                out.write(buffer, 0, length);
                count += length;
            }
            session.fsync(out);
            out.close();

            Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED);
            session.commit(PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT).getIntentSender());


        } catch (Exception ex) {
            Log.e(TAG, "installPackage: catch");
            ex.printStackTrace();

        } finally {
            Log.e(TAG, "installPackage: finally");

            if (session != null) {
                session.close();
            }
        }

    }

}

and inside my MainActivity.java on button click calling CustomPackageInstaller.installPackage() written in above class

 File file= new File(filePath);
 InputStream targetStream = new FileInputStream(file);

 CustomPackageInstaller.installPackage(
                MainActivity.this,
                "2",
                "com.odine.autoupdateapppractice",
                targetStream);

1 Answers1

0

The error message you are getting pretty much tells you what the problem is. You have requested that the package installer notify you on completion by sending a broacast Intent with ACTION = android.intent.action.PACKAGE_ADDED. This broadcast Intent can only be sent by the Android framework. Regular apps cannot send this broadcast Intent.

You should use an explicit Intent, where you specify the component (package name and class name) for this purpose. You can have the package installer launch an Activity or a BroadcastReceiver for this purpose.


NOTE: Your app must be device owner to be able to do this without user interaction.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • Thanks for your answer, I got your point, suppose if I do `Intent intent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); session.commit(PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT).getIntentSender());` it is not updating my app and there is no error in the logs. – imMoazzamAli May 24 '21 at 06:34
  • You need to use `PendingIntent.getActivity()` instead of `PendingIntent.getBroadcast()`. If that doesn't work you can use the broadcast `Intent` mechanism, but you'll need to define a custom action like `"my.package.name.UPDATED"` and then put that ACTION in the manifest for your `BroadcastReceiver`. Your biggest problem is that you cannot use a system-defined broadcast ACTION for this purpose. – David Wasser May 24 '21 at 06:52
  • `PendingIntent.getActivity()` worked but it still not update the app. Am I doing something wrong in `session.commit()`? Is it necessary for the app to be system APP or have Device Owner permission? – imMoazzamAli May 24 '21 at 07:21
  • You should be able to update your own app. If you look at logcat what messages do you see? Don't filter the logcat because you might miss something important. See if there are any errors related to the installation – David Wasser May 24 '21 at 07:24
  • The new APK must have the same package name and the same signature as the installed version. You cannot update a debug app to a release app or vice-versa like this. – David Wasser May 24 '21 at 07:25
  • If the app is not device owner then this will require user intervention. – David Wasser May 24 '21 at 07:32
  • I am updating my own app(debug build) with just updated `versionCode 2 versionName "1.0.2"` I am seeing no error log in logcat, no filters added. but has the following log `D/SurfaceFlinger: duplicate layer name: changing com.odine.autoupdateapppractice/com.odine.autoupdateapppractice.MainActivity to com.odine.autoupdateapppractice/com.odine.autoupdateapppractice.MainActivity#1 ` – imMoazzamAli May 24 '21 at 07:40
  • And the installer is not launched? You don't see the UI for the installer? – David Wasser May 24 '21 at 12:32
  • Did you check out the sample code in the api demos that comes with the android sdk? ApiDemos/src/com/example/android/apis/content/InstallApk*.java. – David Wasser May 24 '21 at 12:37
  • I don't want to launch installer, I want app to be updated automatically without user interaction, the functionality of showing UI and code from this link `ApiDemos/src/com/example/android/apis/content/InstallApk*.java` required user interactin to TAP on INSTALL/CANCLE button of installer UI, but i want it in background – imMoazzamAli May 24 '21 at 13:18
  • not possible unless you are device owner. – David Wasser May 24 '21 at 13:26
  • yes i think this is the issue, that's why i asked earlier **Is it necessary for the app to be system APP or have Device Owner permission?** Thanks for your help. kindly edit your answer with this message. **app must be device owner if it want to update without user interaction** – imMoazzamAli May 24 '21 at 14:07