I'm working on an IoT smart module that runs on Android 13 Go, and it doesn't have Google Play Services. I am trying to implement an in-app update functionality where the app can be updated on a button click. Currently, I am able to download the APK from a URL successfully, but I'm facing issues with the installation process using the PackageInstaller. After downloading, I don't receive any response from the installApk function, and the app doesn't seem to be updated.
Code
private fun downloadAndInstallApk(apkUrl: String) {
val request = DownloadManager.Request(Uri.parse(apkUrl))
.setTitle("Sensor")
.setDescription("Downloading")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "sensor.apk")
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request)
// Register a BroadcastReceiver to listen for the download completion
val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
registerReceiver(downloadReceiver, filter)
}
private val downloadReceiver = object : BroadcastReceiver() {
@SuppressLint("Range")
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) {
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val query = DownloadManager.Query().setFilterById(downloadId)
val cursor = downloadManager.query(query)
if (cursor.moveToFirst()) {
val columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
val uri =
Uri.parse(cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)))
Log.i(TAG, "onReceive: $uri")
// Install the APK directly without copying to cache
installApk(uri)
} else {
Toast.makeText(this@LandingActivity, "Download failed", Toast.LENGTH_SHORT)
.show()
}
}
cursor.close()
}
}
}
private fun installApk(uri: Uri) {
Log.i(TAG, "installApk: $uri")
val packageInstaller = packageManager.packageInstaller
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = packageInstaller.createSession(params)
val session = packageInstaller.openSession(sessionId)
try {
val inputStream = contentResolver.openInputStream(uri)
val outputStream = session.openWrite("package", 0, -1)
inputStream.use { input ->
outputStream.use { output ->
input!!.copyTo(output)
}
}
val intent = Intent(this, javaClass)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
session.commit(pendingIntent.intentSender)
} finally {
session.close()
}
}
Manifest
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
I've implemented a downloadAndInstallApk function that successfully downloads the APK and triggers the installApk function. However, after the download, the installation doesn't seem to proceed as expected. I've used a BroadcastReceiver to listen for the download completion and initiate the installation process.
Specific Concerns:
Is there an issue with how I'm using the PackageInstaller? Am I missing any critical steps in the installation process? I've included the necessary permissions and a FileProvider in the manifest. Is there anything else needed for proper file access? Additional Information:
I'm targeting Android 13 Go without Google Play Services. I'm using a FileProvider for file access. Any insights or suggestions on how to resolve this issue would be greatly appreciated. Thank you!