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.