I'm developing an android app in Flutter for a tablet that will be attached to some hardware that it will control. Everything works fine so far, but I'm a bit stuck on the automatic update.
So to start off. The app is:
- Device owner
- Set to restart on device boot
- Running in kiosk mode
- Set as launcher (so if you somehow manage to get to the launcher it's just the app restarting)
All this works great.
I also implemented silent OTA update. So a new version of the app is downloaded and silently installed with no user interaction needed. This is not using Play Store in any way, I'm side-loading the APK from the device.
Here's where the crux starts for me.
Once the app is installed I can't get it to restart automatically. The device reverts to the system Launcher, and it gives me a notification that admin installed the app.
I can then manually start the app and it will put itself in kiosk mode and set itself as launcher.
The apk being installed is built from the same source, with just the version number updated. For testing. So it's all the same package and everything.
I'm not good at all with Kotlin. So after a lot of reading and some ChatGPT help I have this in my MainActivity.kt:
private fun silentInstallApk(apkFilePath: String) {
Log.e("MainActivity", "silentInstallApk called with: $apkFilePath")
val apkFile = File(apkFilePath)
if (!apkFile.exists()) {
Log.e("MainActivity", "APK file not found")
return
}
val packageInstaller = packageManager.packageInstaller
val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
val sessionId = packageInstaller.createSession(params)
val session = packageInstaller.openSession(sessionId)
FileInputStream(apkFile).use { fis ->
session.openWrite("COSU", 0, -1).use { out ->
fis.copyTo(out)
session.fsync(out)
}
}
// Specify FLAG_IMMUTABLE for the PendingIntent
val intent = Intent("android.intent.action.MAIN")
val pendingIntent = PendingIntent.getBroadcast(this, sessionId, intent, PendingIntent.FLAG_IMMUTABLE)
session.commit(pendingIntent.intentSender)
session.close()
}
And my AppUpdateReceiver.kt looks like this:
class AppUpdateReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_MY_PACKAGE_REPLACED) {
// Start the main activity
val restartIntent = Intent(context, MainActivity::class.java)
restartIntent.addFlags(FLAG_ACTIVITY_NEW_TASK)
context.startActivity(restartIntent)
}
}
}
My logs tell me that the silentInstallApk is called correctly, and cince the app is installed it obviously is.
But I'm guessing there is something that messes up the AppUpdateReceiver
My manifest has this (only pasting the stuff I assume is relevant)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xx.xxx.xxx">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
...
<receiver
android:name=".AppUpdateReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<data
android:path="xx.xxxxx.xxxx"
android:scheme="package"/>
</intent-filter>
</receiver>
</application>
</manifest>
So how would I go about to get the app to restart after update?
Am I perhaps doing something so the app doesn't consider itself updated, but rather uninstalled, and then my silentInstall it's considered a new app?