3

My Android activity/app is using DownloadManager and PackageInstaller to update itself. I'll post the relevant code here, but the bottom line is the file downloads successfully but does not install and there's no useful information in LogCat to help me figure out why.

If there's something wrong with my code I'd love to know that, but as far as I know I'm doing things the way the examples all say to. I've tried doing it with and without setting DeviceOwner, and see the same behavior either way. Nothing happens and there's nothing in LogCat to indicate what went wrong.

I tried hooking up a session callback method, but I can't get any information because my 'sessionInfo' is always NULL. How is that even possible?

pkgInstaller.registerSessionCallback(new PackageInstaller.SessionCallback() {
    PackageInstaller.SessionInfo sessionInfo = pkgInstaller.getSessionInfo(sID);
}

The download part is working successfully, so I'll just show the relevant line here.

File downloadFile = new File(getExternalFilesDir(null),"myapp-update.apk");

The apk was built in Android Studio using "Build -> Build Bundle(s) -> APK(s) -> Build APK(s)", which spits out a "debug" APK (the same as if I build & deploy to device from the IDE). It was built with a NEWER VersionID and VersionName, then uploaded to my file server. My Android app downloads it using DownloadManager, and it's working.

Here's what happens after the APK is downloaded.

private BroadcastReceiver onDownloadComplete = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        // Get our download record, verify it, etc.
        // ......

        // Get URI of downloaded file
        int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
        String apkStr = cursor.getString(uriIndex);

        // Install APK update
        Uri apkUri = Uri.parse(apkStr);
        installApk(Objects.requireNonNull(apkUri.getPath()));
    }
}

Here is the method that installs the APK after download.

public static final String ACTION_INSTALL_COMPLETE = BuildConfig.APPLICATION_ID + ".INSTALL_COMPLETE";
private void installApk(String apkFilePath)
{
    try
    {
        // Create package manager installation params
        PackageInstaller pkgInstaller = getBaseContext().getPackageManager().getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(this.getPackageName());

        // Create package manager installation session
        int sessionId = pkgInstaller.createSession(params);
        PackageInstaller.Session session = pkgInstaller.openSession(sessionId);

        // Get size of downloaded APK
        long sizeBytes = 0;
        final File file = new File(apkFilePath);
        if (file.isFile())
            sizeBytes = file.length();

        // Stream downloaded APK file to package manager session
        InputStream in = new FileInputStream(apkFilePath);
        OutputStream out = session.openWrite("konektv_update_session", 0, -1);
        byte[] buffer = new byte[65536];
        int bytesRead = in.read(buffer);
        while (bytesRead != -1)
        {
            out.write(buffer, 0, bytesRead);
            bytesRead = in.read(buffer);
        }

        // Close streams
        session.fsync(out);
        out.close();
        in.close();

        // Close session
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, sessionId, new Intent(ACTION_INSTALL_COMPLETE),0);
        session.commit(pendingIntent.getIntentSender());
        session.close();

    }
    catch (Exception ex)
    {
       ex.printStackTrace();
    }
}

My custom broadcast receiver for APK installation is as follows. It never gets called and the downloaded APK is not installed. I can tell because nothing happens, but even if it did I display the version number and it doesn't change.

public class UpdateReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        final String LOG_TAG = "myapp-updated";
        if (intent == null)
            return;

        String action = intent.getAction();
        if ((action == null) || !action.equalsIgnoreCase(Intent.ACTION_MY_PACKAGE_REPLACED))
            return;

        // Start main activity
        Intent intentMain = new Intent(context, MainActivity.class);
        intentMain.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intentMain.putExtra(KonekTvApplication.IS_BOOT_AUTOSTART, true);
        context.startActivity(intentMain);
    }
}

Here are the relevant bits from my AndroidManifest.xml file.

    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

    <application
        android:testOnly="true"
        android:name=".MyApplication">

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

The only other code is related to DeviceOwner, but this methodology is supposed to work regardless of device owner. The only difference is whether the update is silent or requires user interaction.

public class DeviceOwnerReceiver extends DeviceAdminReceiver
{
    private static final String LOG_TAG = "my-owner-receiver";

    @Override
    public void onEnabled(Context context, Intent intent) {
        Log.w(LOG_TAG,"Device owner enabled");
    }

    public static ComponentName getComponentName(Context context) {
        return new ComponentName(context.getApplicationContext(), DeviceOwnerReceiver.class);
    }
}

Here's the device owner stuff from AndroidManifest.xml.

    <receiver
        android:name=".DeviceOwnerReceiver"
        android:label="@string/app_name"
        android:permission="android.permission.BIND_DEVICE_ADMIN">
        <meta-data
            android:name="android.app.device_admin"
            android:resource="@xml/device_admin" />
        <intent-filter>
            <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
        </intent-filter>
    </receiver>

And here's the device_admin.xml file.

<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
    </uses-policies>
</device-admin>

Thanks so much for any help you can provide.

Bungles
  • 1,969
  • 2
  • 25
  • 54
  • Hi @bungles. Do you have found a solution to your problem? On some devices I have the same situation: I use PackageInstaller but the broadcast is never reached. Let me know, thanks – Alessandro Caliaro Apr 22 '21 at 10:47
  • I don't believe I ever addressed the issue, then Covid, so probably not getting back to it. – Bungles Apr 22 '21 at 16:37

0 Answers0