一、<=Api 23 时 相信你们都是经过installPackage这个类反射来静默安装apk的。可是呢api24及其以后就没有这个方法了,你再PackageManagerService或IPackageManager都是没有这个方法了。因此咱们只能经过installPacakageAsUser来实现静默安装了。代码以下:
java
/** * 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; }
二、声明安装权限并进行系统签名来静默安装android
当咱们选择手动安装应用时,会跳转到应用安装界面,这个界面就是系统的 PackageInstaller 提供,专门用来让用户有感知地安装应用。api
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 表示可选的安装来源,好比应用宝之类的。bash
public abstract void installPackage( Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName);
ApplicationPackageManager 里面 mPM 是一个 IPackageManager 类型的对象,它会执行具体的安装任务。app
try { mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName, verificationParams, null); } catch (RemoteException ignored) { }
ContextImpl 的 getPackageManager 方法,经过 ActivityThread 获取 IPackageManager 对象用来构造 ApplicationPackageManager,而后返回 ApplicationPackageManager。ui
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 方法,其实就是获取系统服务的过程。this
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 方法就好了,下面看代码:code
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,一样使用反射处理便可。server
最后声明权限 <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
,还要使用系统签名,这个很是关键,要否则就会出现异常: java.lang.SecurityException: Neither user 10052 nor current process has android.permission.INSTALL_PACKAGES.
。对象
结论:经过调用系统 API 静默安装,终于能够堂堂正正地搞事情了!虽然这是官方提供的接口,可是为了避免让你随心所欲,强制使用系统签名,因此对于第三方应用采用的可能性是零。