Android 8.1 静默安装

1、<=Api 23 时 相信大家都是通过installPackage这个类反射来静默安装apk的。但是呢api24及其之后就没有这个方法了,你再PackageManagerService或IPackageManager都是没有这个方法了。所以我们只能通过installPacakageAsUser来实现静默安装了。代码如下:
 

   /**
     * void installPackageAsUser(in String originPath,
     * in IPackageInstallObserver2 observer,
     * int flags,
     * in String installerPackageName,
     * int userId);
     * @param installPath
     */
    private void installApkInSilence(String installPath,String packageName) {
        Class pmService;
        Class activityTherad;
        Method method;
        try {
            activityTherad = Class.forName("android.app.ActivityThread");
            Class paramTypes[] = getParamTypes(activityTherad, "getPackageManager");
            method = activityTherad.getMethod("getPackageManager", paramTypes);
            Object PackageManagerService = method.invoke(activityTherad);
            pmService = PackageManagerService.getClass();
            Class paramTypes1[] = getParamTypes(pmService, "installPackageAsUser");
            method = pmService.getMethod("installPackageAsUser", paramTypes1);
            method.invoke(PackageManagerService, installPath, null, 0x00000040, packageName, getUserId(Binder.getCallingUid()));//getUserId
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
 
    private Class[] getParamTypes(Class cls, String mName) {
        Class cs[] = null;
        Method[] mtd = cls.getMethods();
        for (int i = 0; i < mtd.length; i++) {
            if (!mtd[i].getName().equals(mName)) {
                continue;
            }
 
            cs = mtd[i].getParameterTypes();
        }
        return cs;
    }
    public static final int PER_USER_RANGE = 100000;
    public static int getUserId(int uid) {
        return uid / PER_USER_RANGE;
    }

 

2、声明安装权限并进行系统签名来静默安装

    当我们选择手动安装应用时,会跳转到应用安装界面,这个界面就是系统的 PackageInstaller 提供,专门用来让用户有感知地安装应用。

    Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri = Uri.fromFile(new File("/sdcard/news.apk")));
    intent.setDataAndType(uri, "application/vnd.android.package-archive");
    startActivity(intent);

分析 PackageInstaller 的源码,我们发现它会通过 PackageManager 调用 installPackage 方法,这是个隐藏的抽象方法,实现类是 ApplicationPackageManager。主要看一下四个参数:packageURI 就是 apk 的路径;observer 是安装的监听器,应用安装完成时会被回调,不能为 null;flags 是标志位,指定安装的参数;installersPackageName 表示可选的安装来源,比如应用宝之类的。

        public abstract void installPackage(
            Uri packageURI, PackageInstallObserver observer,
            int flags, String installerPackageName);

ApplicationPackageManager 里面 mPM 是一个 IPackageManager 类型的对象,它会执行具体的安装任务。

try {
            mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                    verificationParams, null);
        } catch (RemoteException ignored) {
        }

ContextImpl 的 getPackageManager 方法,通过 ActivityThread 获取 IPackageManager 对象用来构造 ApplicationPackageManager,然后返回 ApplicationPackageManager。

 public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }
        return null;
    }

ActivityThread 的 getPackageManager 方法,其实就是获取系统服务的过程。

public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
    }

通过以上分析,我们通过 PackageManager 调用 installPackage 方法就行了,下面看代码:

public static boolean silentInstall(PackageManager packageManager, String apkPath) {
        Class pmClz = packageManager.getClass();
        try {
            if (Build.VERSION.SDK_INT >= 21) {
                Class aClass = Class.forName("android.app.PackageInstallObserver");
                Constructor constructor = aClass.getDeclaredConstructor();
                constructor.setAccessible(true);
                Object installObserver = constructor.newInstance();
                Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, aClass, int.class, String.class);
                method.setAccessible(true);
                method.invoke(packageManager, Uri.fromFile(new File(apkPath)), installObserver, 2, null);
            } else {
                Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, Class.forName("android.content.pm.IPackageInstallObserver"), int.class, String.class);
                method.setAccessible(true);
                method.invoke(packageManager, Uri.fromFile(new File(apkPath)), null, 2, null);
            }
            return true;
        } catch (Exception e) {
            log.error(e);
        }
        return false;
    }

由于 PackageManager 在不同版本上的 installPackage 方法参数不一致,所以我们根据编译版本做了处理。在 API 21 及以上,需要传递一个非 null 的 PackageInstallObserver,这个类是不可见 的,我们就用反射创建一个 observer 对象,flags 指定 INSTALL_REPLACE_EXISTING,用常量表示就是 2。在 API 21 以下,observer 类型是IPackageInstallObserver,同样使用反射处理即可。

最后声明权限 ,还要使用系统签名,这个非常关键,要不然就会出现异常: java.lang.SecurityException: Neither user 10052 nor current process has android.permission.INSTALL_PACKAGES.

结论:通过调用系统 API 静默安装,终于可以堂堂正正地搞事情了!虽然这是官方提供的接口,但是为了不让你为所欲为,强制使用系统签名,所以对于第三方应用采用的可能性是零。

你可能感兴趣的:(Android开发)