Here's me.

标签 静默安装 下的文章

先说说一般情况下如何进行静默安装:
①具有框架签名的应用或具有特权权限(root/shell 或其他)的应用直接调用系统方法来安装。eg 内置应用市场、Google Play 和 小黑屋 等。
②root 或 shell 权限,使用指令 “pm install” eg 酷安 刷机精灵 等。本质上也是第一种,会慢一点。
③使用无障碍功能,模拟用户手指点击下一步来安装。

Flyme MIUI 等 ROM 已经开启对第二条静默安装方法的验证,如果是通过 root 或 shell 进程开启的安装任务,可能需要在手机上二次确认。而无障碍选项在一些ROM上,会被自动关闭,无法正常使用。

第一种安装方法被用在各机型的内置应用市场上。但是一般情况下,如果没有系统框架签名或是特权权限的应用无法调用的。那么,小黑屋的静默安装功能是怎么实现的呢?

让我们看一下这条 commit:https://android.googlesource.com/platform/frameworks/base/+/39fb7fd%5E!/

Allow silent package install for device owner.

Allow the device owner to silently install and remove packages using the
PackageInstaller APIs. Show notifications to the user after the
installation / deletion was successful.

具体看 代码 https://android.googlesource.com/platform/frameworks/base/+/39fb7fd730dc2113ced7e663d7a35e48a4c6b1ae/services/core/java/com/android/server/pm/PackageInstallerSession.java#213

        // Device owners are allowed to silently install packages, so the permission check is
        // waived if the installer is the device owner.
        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
                Context.DEVICE_POLICY_SERVICE);
        mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
        if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
                == PackageManager.PERMISSION_GRANTED)
                || (installerUid == Process.ROOT_UID)
                || mIsInstallerDeviceOwner) {
            mPermissionsAccepted = true;
        } else {
            mPermissionsAccepted = false;
        }

这里 我们可以看到,PackageInstaller.Session 会判断应用是否具有 android.Manifest.permission.INSTALL_PACKAGE 权限 或是应用 UID 是否为 ROOT 或是设备管理员应用。如果是,就会将 mPermissionsAccepted 置于 true,就能通过验证来安装。我们现在知道了这一点就好办了,可以新建一个设备管理员应用,然后写一个 demo:

        int sessionId = getPackageManager().getPackageInstaller().createSession(new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL));
        PackageInstaller.Session session = getPackageManager().getPackageInstaller().openSession(sessionId);
        session.fsync(outputStream); //写apk数据
        session.commit(new IntentSender());

编译安装试验,发现应用成功安装,且状态栏成功出现一条应用已安装的通知。

当然了,通过阅读上面的 commit,我们也能得知设备管理员应用也可以静默卸载应用。有兴趣的话可以试试。