Update a pinned device owner managed android app silently (in kiosk mode)

184 Views Asked by At

I have created a device owner app that pins the app when launches, I want to update the Kiosk launcher app from the apk which is hosted in GitHub, not from PlayStore, I have successfully managed to download the apk file, but when tried to install the apk programmatically confirmation popup coming for the update. Is it possible to update the app silently without user confirmation?

I tried with this code

override fun doInBackground(vararg p0: String?): Boolean {
    var flag = false

    try {
        val path =
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/"
        var outputFile = File("$path$fileName.apk")
        var repetition = 1
        while (outputFile.exists()) {
            outputFile = File("$path$fileName ($repetition).apk")
            repetition++
        }

        val directory = File(path)
        if (!directory.exists()) {
            directory.mkdirs()
        }

        val url = URL(downloadUrl)
        val c = url.openConnection() as HttpURLConnection
        c.requestMethod = "GET"
        c.connect()

        val fos = FileOutputStream(outputFile)
        val inputStream = c.inputStream
        val totalSize = c.contentLength.toFloat() // size of apk

        val buffer = ByteArray(1024)
        var len1: Int
        var per: Float
        var downloaded = 0f
        while (inputStream.read(buffer).also { len1 = it } != -1) {
            fos.write(buffer, 0, len1)
            downloaded += len1
            per = (downloaded * 100 / totalSize)
            publishProgress(per.toInt())
        }
        fos.close()
        // inputStream.close()
        // openNewVersion(outputFile.path)
        installPackage(context, inputStream, "com.nurse.carenurse")
        flag = true
    } catch (e: MalformedURLException) {
        Log.e("DownloadApk", "Update Error: " + e.message)
        flag = false
    } catch (e: IOException) {
        e.printStackTrace()
    }

    return flag
}

fun installPackage(context: Context, `in`: InputStream, packageName: String?): Boolean {
    val packageInstaller = context.packageManager.packageInstaller
    val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
    params.setAppPackageName(packageName)
    // set params
    val sessionId = packageInstaller.createSession(params)
    val session = packageInstaller.openSession(sessionId)
    val out = session.openWrite("TestDPC", 0, -1)
    val buffer = ByteArray(65536)
    var c: Int
    while (`in`.read(buffer).also { c = it } != -1) {
        out.write(buffer, 0, c)
    }
    session.fsync(out)
    `in`.close()
    out.close()
    session.commit(createIntentSender(context, sessionId))
    return true
}

private fun createIntentSender(context: Context, sessionId: Int): IntentSender {
    val ACTION_INSTALL_COMPLETE = "com.nurse.carenurse.ACTION_INSTALL_COMPLETE"
    val pendingIntent = PendingIntent.getBroadcast(
        context,
        sessionId,
        Intent(ACTION_INSTALL_COMPLETE),
        PendingIntent.FLAG_IMMUTABLE
    )
    return pendingIntent.intentSender
}
1

There are 1 best solutions below

1
Denoy On

I have been dealing with same issue and came here.

I have tried couple solutions including similar to your, but nothing worked (always got popup with Cancel and Install buttons) except SimpleInstaller version 5.0.0 (I know it's archived and moved to Ackpine, but Ackpine showed me popup as well)

First of all as a device owner I have disabled Play Protect as I don't have my app on google play and play protect is completely blocking to update my app despite the fact I have device owner rights.

gradle:

dependencies: {
    implementation "io.github.solrudev:simpleinstaller:5.0.0"
}

I am updating the app inside CoroutineWorker

import io.github.solrudev.simpleinstaller.PackageInstaller
import io.github.solrudev.simpleinstaller.data.ConfirmationStrategy
import io.github.solrudev.simpleinstaller.data.InstallResult
import io.github.solrudev.simpleinstaller.installPackage


val apk = File(applicationContext.filesDir.absolutePath + "/app-release.apk")

val installResult = PackageInstaller.installPackage(apk) {
    confirmationStrategy = ConfirmationStrategy.IMMEDIATE
}

if(installResult is InstallResult.Success) {
    // don't expect this to be called like your app is gonna be killed during update
}else if(installResult is InstallResult.Failure) {
    val errorMessage = installResult.cause
    // ...
}