您的位置:首页 > 移动开发 > Android开发

Android PackageManager源码浅析以及静默安装实现方式

2016-01-05 20:26 246 查看

Aandroid应用管理

/article/11906369.htmlskay整理。2016了本篇成了我的开年之博文,距上次做静默安装和辅助服务已经半年多了,最近一直在做项目中的插件功能,也一直没时间整理平时接触的东西,甚至年终总结,今天就从经常用到的知识来开始2016的道路吧。
Aandroid的应用管理主要由PMS(PackageManagerService)来负责管理;上层上来由PackageManager来进行管理,通过PM我们可以得到设备上的所有安装包信息,包括未安装和安装过的,未安装的包信息采用反射和未暴露的API也可以进行深度解析得到我们想要的信息。而应用的的安装和卸载也有PM负责。
今天我们主要说一下PackageManager,至于PMS来说和上层有Binder进行交互,PMS在实际开发中我们很少直接用到,但是我们上层通过PM来获取的一些基础信息,都需要PMS来调用底层,当通过看源码得知PackageManager沿用了Android面向接口编程的风格,比如viewRoot,WindowManger,ActivityManager都采用了面向接口编程,这些Mgr为我们提供了一些基础的功能接口,具体都由各自的Service来动态注入Impl,就是我们通常说的热插拔,至于这么写的好处吗这里稍微说两句,在接触过java编程久的朋友都知道面向接口的可扩展性很强,因为安卓源码也需要升级,谷歌工程师在下一个版本中或许就会新增一些api,那么这样设计的理念也便于源码的维护和升级,我们平时开发中也可以借鉴这种优雅的面向协议编程,当然iOS同样适用。

PackageManager

PackageManager在android.content.pm包下,它主要来负责应用的解析,和APK的安装,卸载和更新,那么我们可以清晰得看到此类的以下方法
1)负责安装privateabstractvoidinstallPackage(UripackageURI,IPackageInstallObserverobserver,intflags,2)卸载privateabstractvoiddeletePackage(StringpackageName,IPackageDeleteObserverobserver,intflags);
安卓系统通过以上api和底层的pms交互进行安装,我们的普通apk无法直接安装的第三方应用的,因为源码没有开放其方法,只有我们发送一条安装意图才可以交友pms来安装apk,具体由系统级别的apk(包名com.android.packageinstaller)来进行处理。
Intentintent=newIntent(Intent.ACTION_VIEW);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setAction(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");context.startActivity(intent);
以上安全限制并非是谷歌的撒手锏,除了用未开放安装的api,在调用此方法前系统也会进行权限动态监测,我们可以看看pm的又一重要方法:
publicabstractintcheckPermission(StringpermName,StringpkgName);
这就为何证明了普通应用为何没有安装的权限,其内部会对调用此api的进行权限监测,如果是普通应用那么返回int值为1的返回值,在这里我们要和分析下安卓apk的级别内置应用和普通应用
预装程序(即相机,日历和浏览器等)保存在/system/app/中。用户安装程序(APIDemo,Any.do等)保存在/data/app/中。当然目前安卓4.4以后内置预装程序的app/下又会新增了pri-app/和/app,用来个用户提供卸载内置程序的入口,那么在pri下的apk无法卸载的,除非我们root后才能卸载。
**

实现静默安装

**通过了解了上面pm的简单工作原理,我们就可以想到静默安装的途径

一采用伪造系统PM(PackageManger)

通过伪造自己的Pm实现开放的api,并且采用自己的IPackageInstallObserver,说道这里你估计会不明白此类用来干嘛的。此观察者是用来检测apk是否安装的的回调,那么卸载同样有自己的观者这,此通过aidl和pms进行通讯,我们可以从源码copy一份到自己的项目下面注意的是包名和路劲必须和源码保持一致。
伪造安装所需要的observer和PM后在我们的代码里直接掉用pm.installPackager()即可,但是又会来到权限的问题,那么怎么做到绕过权限呢,我通过改checkPermission()方法,但是没用,即使我返回0也无法达到绕过权限的问题,那么,今天的静默安装也到此无法达到大家期望的普通静默安装的效果,但是在root后或者app为系统apk的时候,我们是可以做到静默安装的,至于安装成功后你需要显示什么view我们同样可以在回调中进行处理。

二运行PM命令

我们可以直接在拿到系统builder写入pm命令,加入到系统进程中执行install方法进行安装代码如下:finalResultBuilderbuilder=Result.newBuilder();String[]args={"pm","install","-r",getPackageUri(path).getPath()};Stringresult=null;ProcessBuilderprocessBuilder=newProcessBuilder(args);Processprocess=null;InputStreamerrIs=null;InputStreaminIs=null;try{ByteArrayOutputStreambaos=newByteArrayOutputStream();intread=-1;process=processBuilder.start();errIs=process.getErrorStream();while((read=errIs.read())!=-1){baos.write(read);}baos.write('\n');inIs=process.getInputStream();while((read=inIs.read())!=-1){baos.write(read);}byte[]data=baos.toByteArray();result=newString(data);}catch(IOExceptione){e.printStackTrace();Stringlog=e.toString()+":"+"fileiswriteex!";Log.e("",log);builder.setCustomMessage(log);}
当然此方法也不是完美的,也需要root权限或者系统界别的apk

三反射方法

我们也可以采用反射方法,通过java反射原理,反射出PM然后执行insatallPackage(),PM并非直接可以反射,它是需要ActivityTherad进行提供支持,我们通过对系统总线程的反射出PM,接着反射出installPackage方法即可
Class<?>pmService;Class<?>activityTherad;Methodmethod;activityTherad=Class.forName("android.app.ActivityThread");Class<?>paramTypes[]=getParamTypes(activityTherad,"getPackageManager");method=activityTherad.getMethod("getPackageManager",paramTypes);ObjectPackageManagerService=method.invoke(activityTherad);pmService=PackageManagerService.getClass();Class<?>paramTypes1[]=getParamTypes(pmService,"installPackage");method=pmService.getMethod("installPackage",paramTypes1);method.invoke(PackageManagerService,getPackageUri(Path),null,0,null);

总结

注意:实现静默安装都需要系统级别权限才能执行,具体是否系统给予权限<permissionandroid:name="android.permission.INSTALL_PACKAGES"/>其实root也是拥有系统级别权限的一种方式,本质是系统权限才有运行install的权限,我们可以请求用户root代码:/***checkrootPerssion.*@return*/privatestaticbooleanhasRootPerssion(){PrintWriterPrintWriter=null;Processprocess=null;try{process=Runtime.getRuntime().exec("su");PrintWriter=newPrintWriter(process.getOutputStream());PrintWriter.flush();PrintWriter.close();intvalue=process.waitFor();returnreturnResult(value);}catch(Exceptione){e.printStackTrace();}finally{if(process!=null){process.destroy();}}returnfalse;}所以实现静默安装的前提必须内置或者root,其他情况无法做到静默安装。那么是不是我们无法做到除了以上内置的静默安装了呢其实也未必目前我们可以采用辅助功能(Accessibility)实现自动安装,用来代替用户点击,监控在com.android.packageinstaller包的界面元素来遍历出所需要的按钮文本,来执行安装操作,微信抢红包插件也是利用此原理,但是采用辅助依旧会显示安装界面的,我们可以在原有的系统界面上添加一个view浮层来伪装静默安装功能。除此之外我们也可以采用动态加载来实现一个apk的安装,其实真正意义上并非静默安装,这需要一个apk来做宿主,只是将我们的apk解析出所用的组件信息,保存到本地,再将宿主的上下文直接注入插件apk中,至于插件实现apk免安装的知识有很多方式,下期再介绍我的一种实现原理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: