Android 后台静默安装App

Published on with 1,210 views

在项目中要用到在用户无感知的情况下,后台下载并静默安装App,但是随着Android的发展,对App后台静默安装要求的权限要求也越来越严格。

import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.annotation.WorkerThread
import java.io.*
import java.lang.reflect.Constructor
import java.lang.reflect.Method

/**
 *   静默安装
 *   @param apkPath 需要安装的apk所在位置
 */

@WorkerThread
fun silentInstall(context: Context, apkPath: String): Boolean {
    return if (Build.VERSION.SDK_INT < 28) {
        installBelow28(context.packageManager, apkPath)
    } else {
        installOver28(context, apkPath)
    }
}



//静默安装 9.0以下
private fun installBelow28(packageManager: PackageManager, apkPath: String?): Boolean {
    val pmClz: Class<*> = packageManager.javaClass
    try {
        if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 28) {
            val aClass = Class.forName("android.app.PackageInstallObserver")
            val constructor: Constructor<*> = aClass.getDeclaredConstructor()
            constructor.isAccessible = true
            val installObserver: Any = constructor.newInstance()
            val method: Method = pmClz.getDeclaredMethod("installPackage",
                Uri::class.java,
                aClass,
                Int::class.javaPrimitiveType,
                String::class.java)
            method.isAccessible = true
            method.invoke(packageManager, Uri.fromFile(File(apkPath)), installObserver, 2, null)
        } else {
            val method: Method = pmClz.getDeclaredMethod("installPackage",
                Uri::class.java,
                Class.forName("android.content.pm.IPackageInstallObserver"),
                Int::class.javaPrimitiveType,
                String::class.java)
            method.isAccessible = true
            method.invoke(packageManager, Uri.fromFile(File(apkPath)), null, 2, null)
        }
        return true
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
    }
    return false
}

//静默安装 9.0及以上
private fun installOver28(context: Context, apkPath: String?): Boolean {
    val packageInstaller = context.packageManager.packageInstaller
    val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
    val pkgName: String = getPkgName(context, apkPath) ?: return false
    params.setAppPackageName(pkgName)
    try {
        val allowDowngrade =
                PackageInstaller.SessionParams::class.java.getMethod("setAllowDowngrade",
                    Boolean::class.javaPrimitiveType)
        allowDowngrade.isAccessible = true
        allowDowngrade.invoke(params, true)
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
    }
    var os: OutputStream? = null
    var `is`: InputStream? = null
    try {
        val sessionId = packageInstaller.createSession(params)
        val session = packageInstaller.openSession(sessionId)
        os = session.openWrite(pkgName, 0, -1)
        `is` = FileInputStream(apkPath)
        val buffer = ByteArray(1024)
        var len: Int
        while (`is`.read(buffer).also { len = it } != -1) {
            os.write(buffer, 0, len)
        }
        session.fsync(os)
        os.close()
        os = null
        `is`.close()
        `is` = null
        session.commit(PendingIntent.getBroadcast(context,
            sessionId,
            Intent(Intent.ACTION_MAIN),
            0).intentSender)
    } catch (e: java.lang.Exception) {
        return false
    } finally {
        if (os != null) {
            try {
                os.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        if (`is` != null) {
            try {
                `is`.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }
    return true
}

/**
 * 获取apk的包名
 */
private fun getPkgName(context: Context, apkPath: String?): String? {
    val pm = context.packageManager
    val info = pm.getPackageArchiveInfo(apkPath!!, 0)
    return info?.packageName
}


标题:Android 后台静默安装App
作者:yanjing
地址:https://blog.yanjingtp.cn/articles/2022/03/01/1646117388911.html