Android - 使用 PackageInstaller 和 PackageInstaller.Session 静默安装 APK

pho*_*bus 5 android android-source silent-installer android-package-managers kotlin

查看 Android (AOSP) 的来源,installPackage标记为已弃用并检查 Android 的 PackageManager 应用程序,它使用PackageInstaller该类创建一个PackageInstaller.Session实例来执行 APK 的安装。

我正在尝试在我的应用程序中做同样的事情。我使用系统密钥签名,并且我确实INSTALL_PACKAGES在清单中包含了权限。

这是我的代码:

    val packageName = "com.spotify.music"
    val inputStream = File(filesDir, "spotify.apk").inputStream()

    // ...

    val packageInstaller = context.packageManager.packageInstaller
    val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
    params.setAppPackageName(packageName)

    val sessionId = packageInstaller.createSession(params)
    val session = packageInstaller.openSession(sessionId)
    val out = session.openWrite("COSU", 0, -1)
    inputStream.copyTo(out)
    session.fsync(out)
    inputStream.close()
    out.close()
    session.commit(null)
Run Code Online (Sandbox Code Playgroud)

但是,我收到以下奇怪的空指针异常:

Error while installing: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.AppOpsManager.checkPackage(int, java.lang.String)' on a null object reference

有人知道这里出了什么问题吗?

Dav*_*Lev 1

以下代码片段应该适合您(我没有测试它,但更欢迎您调试它并编辑任何必要的更改)。

方法参数:

  • context - 您的应用程序的上下文
  • installSessionId - 用于标识安装会话的字符串
  • packageName - 您要安装的包名称(例如 com.my.package)
  • apkStream - 保存 APK 文件数据的输入流。

这是代码:

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class PackageInstaller {

    public static void installPackage(Context context, String installSessionId,
                                      String packageName,
                                      InputStream apkStream) throws IOException {
        PackageManager packageManger = context.getPackageManager();
        android.content.pm.PackageInstaller packageInstaller = 
                packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        android.content.pm.PackageInstaller.Session session = null;
        try {
            int sessionId = packageInstaller.createSession(params);
            session = packageInstaller.openSession(sessionId);
            OutputStream out = session.openWrite(installSessionId, 0, -1);
            byte buffer[] = new byte[1024];
            int length;
            int count = 0;
            while ((length = apkStream.read(buffer)) != -1) {
                out.write(buffer, 0, length);
                count += length;
            }
            session.fsync(out);
            out.close();

            Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED);

            session.commit(PendingIntent.getBroadcast(context, sessionId,
                    intent, PendingIntent.FLAG_UPDATE_CURRENT).getIntentSender());
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)