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

时间:2018-07-27 17:34:07

标签: android kotlin android-source android-package-managers silent-installer

不建议使用installPackage来检查Android(AOSP)的来源,并检查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)

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

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

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

2 个答案:

答案 0 :(得分:1)

以下代码段应为您工作(我没有对其进行测试,但是欢迎您进一步调试和编辑任何必要的更改)。

方法参数:

  • 上下文-应用程序的上下文
  • 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();
            }
        }
    }
}

答案 1 :(得分:0)

您不能将null传递给session.commit()。请参阅Android Developers上的文档。您需要的是一个IntentSender,它是从PendingIntent中获得的,而该PendingIntent中应该包含一个您的应用程序可以接收的意图(可以在Activity,Service或BroadcastReceiver中)。

提交会话后,结果将作为额外内容包括在收到的意图中。