Android PackageManager源码浅析以及静默安装实现方式
Aandroid应用管理
>http://blog.csdn.net/sk719887916/article/details/50314017 skay整理。
>2016了 本篇成了我的开年之博,距上次做静默安装和辅助服务已经有半年之多,最近一直在做项目中的插件功能,也一直没时间整理平时接触的东西,甚至年终总结,今天就从经常用到的知识来开始2016的道路吧。(写在2016年初)
Aandroid的应用管理主要由PMS(PackageManagerService)来负责管理;上层上来由PackageManager来进行管理,通过PM我们可以得到设备上的所有安装包信息,包括未安装和安装过的, 未安装的包信息采用反射和未暴露的API也可以进行深度解析得到我们想要的信息。而应用的的安装和卸载也有PM负责。
PMS
今天我们主要说一下PackageManager,至于PMS来说和上层有Binder进行交互, PMS在实际开发中我们很少直接用到,但是我们上层通过PM来获取的一些基础信息,都需要PMS来调用底层,当通过看源码得知PackageManager沿用了Android面向接口编程的风格,比如`viewRoot`,`WindowManger`,`ActivityManager` 都采用了面向接口编程,这些Mgr为我们提供了一些基础的功能接口,具体都由各自的Service来动态注入Impl,就是我们通常说的热插拔,至于这么写的好处吗 这里稍微说两句,在接触过java编程久的朋友都知道面向接口的可扩展性很强,因为安卓源码也需要升级,谷歌工程师在下一个版本中或许就会新增一些api,那么这样设计的理念也便于源码的维护和升级,我们平时开发中也可以借鉴这种优雅的面向协议编程,当然iOS同样适用。
PackageManager
PackageManager 在android.content.pm包下,它主要来负责应用的解析,和APK的安装,卸载和更新,那么我们可以清晰得看到此类的以下方法
1)负责安装
private abstract void installPackage(
Uri packageURI, IPackageInstallObserver observer, int flags,)
2)卸载
private abstract void deletePackage(
String packageName, IPackageDeleteObserver observer, int flags);
安卓系统通过以上api和底层的pms交互进行安装,我们的普通apk无法直接安装的第三方应用的, 因为源码没有开放其方法,只有我们发送一条安装意图才可以交友pms来安装apk,具体由系统级别的apk(**包名com.android.packageinstaller**)来进行处理。
Intent intent = new Intent(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的又一重要方法:
`public abstract int checkPermission(String permName, String pkgName);`
这就为何证明了普通应用为何没有安装的权限,其内部会对调用此api的进行权限监测,如果是普通应用那么返回int值为1的返回值,在这里我们要和分析下安卓apk的级别内置应用和普通应用
>预装程序(即相机,日历和浏览器等)保存在/system/app/中。
用户安装程序(APIDemo,Any.do等)保存在/data/app/中。
当然目前安卓4.4以后内置预装程序的app/下又会新增了pri-app/ 和/app,用来个用户提供卸载内置程序的入口,那么在pri下的apk无法卸载的,除非我们root后才能卸载。
实现静默安装
通过了解了上面pms的简单工作原理,我们就可以想到静默安装的途径
一 采用伪造系统PM(PackageManger)
通过伪造自己的Pm实现开放的api,并且采用自己的IPackageInstallObserver,说道这里你估计会不明白此类用来干嘛的。此观察者是用来检测apk是否安装的的回调,那么卸载同样有自己的观者这,此通过aidl和pms进行通讯,我们可以从源码copy一份到自己的项目下面 注意的是包名和路劲必须和源码保持一致。
伪造安装所需要的observer和PM后 在我们的代码里直接掉用pm.installPackager()即可,但是又会来到权限的问题,那么怎么做到绕过权限呢,我通过改checkPermission()方法,但是没用,即使我返回0也无法达到绕过权限的问题,那么,今天的静默安装也到此无法达到大家期望的普通静默安装的效果,但是在root后或者app为系统apk的时候,我们是可以做到静默安装的,至于安装成功后你需要显示什么view 我们同样可以在回调中进行处理。
二 运行PM命令
我们可以直接在拿到系统`builder`写入pm命令,加入到系统进程中执行install方法进行安装
代码如下:
final ResultBuilder builder = Result.newBuilder();
String[] args = {"pm", "install", "-r", getPackageUri(path).getPath()};
String result = null;
ProcessBuilder processBuilder = new ProcessBuilder(args);
Process process= null;
InputStream errIs = null;
InputStream inIs = null;
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read=-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 = new String(data);
} catch (IOException e) {
e.printStackTrace();
String log = e.toString() + ":" + "file is write ex!";
Log.e("", log);
builder.setCustomMessage(log);
}
当然此方法也不是完美的,也需要root权限或者系统界别的apk
三 采用反射方法
我们也可以采用反射方法,通过java反射原理,反射出PM然后执行insatallPackage(),PM并非直接可以反射,它是需要ActivityTherad进行提供支持,我们通过对系统总线程的反射出PM,接着反射出installPackage方法即可
Class<?>pmService;
Class<?> activityTherad;
Method method;
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, "installPackage");
method = pmService.getMethod("installPackage", paramTypes1);
method.invoke(PackageManagerService, getPackageUri(Path), null, 0, null);
# 总结 #
注意:实现静默安装都需要系统级别权限才能执行,具体是否监测系统给予权限
` <permission android:name="android.permission.INSTALL_PACKAGES" />`
其实root也是拥有系统级别权限的一种方式,本质是系统权限才有运行install的权限,
我们可以请求用户root (此方式个人感觉真心拿客户当牺牲品,建议pm不要这么做)
代码:
/**
* check rootPerssion.
* @return
*/
private static boolean hasRootPerssion() {
PrintWriter PrintWriter = null;
Process process = null;
try {
process = Runtime.getRuntime().exec("su");
PrintWriter = new PrintWriter(process.getOutputStream());
PrintWriter.flush();
PrintWriter.close();
int value = process.waitFor();
return returnResult(value);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (process!=null) {
process.destroy();
}
}
return false;
}
所以实现静默安装的前提必须内置或者root,其他情况无法做到静默安装。当然这里我们必须要放弃了,因为面向客户的apk是非系统的,
那么是不是我们无法做到除了以上内置的静默安装了呢 其实也未必:
目前我们可以采用辅助功能(Accessibility)实现自动安装,用来代替用户点击,监控在`com.android.packageinstaller`包的界面元素来遍历出所需要的按钮文本,来执行安装操作,微信抢红包插件也是利用此原理,但是采用辅助依旧会显示安装界面的,我们可以在原有的系统界面上添加一个view浮层来伪装静默安装功能(下期将带来免root实现静默安装),此种方式也需要用户主动授权。
到此静默安装又一次装逼失败,放弃
除此之外我们也可以采用动态加载来实现一个apk的安装,其实真正意义上并非静默安装,这需要一个apk来做宿主,只是将我们的apk解析出所用的组件信息,保存到本地,再将宿主的上下文直接注入插件apk中,至于插件实现apk免安装的知识有很多方式,也可以单独出个专题介绍一下。到此如归没有宿主apk存在那么当你的apk想无缘无故的安装起来是不可能的,到此你又一次要放弃!
疑问
这里有人还不服,我一定要实现静默安装,为何QQ之类的就能实现,这里解释一下当你的产品庞大到用户不能放弃的时候,那么很多时候你想做什么更笨不是难题,这种大品牌通过和手机厂家做OEM定制利益链,或者通过开发白名单的方式进行的非法采集勾当,这里就不想再说太多,你懂得!
不死进程一样。你终究要放弃!
--
辅助安装请看:http://blog.csdn.net/sk719887916/article/details/46746991
插件免安装:https://github.com/NeglectedByBoss/PluginLoader
Android PackageManager源码浅析以及静默安装实现方式的更多相关文章
- Android手势源码浅析-----手势绘制(GestureOverlayView)
Android手势源码浅析-----手势绘制(GestureOverlayView)
- Android 手势识别类 ( 三 ) GestureDetector 源码浅析
前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...
- Android源码浅析(二)——Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境
Android源码浅析(二)--Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境 接着上篇,上片主要是介绍了一些安装工具的小知识点Andr ...
- Android源码浅析(一)——VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置
Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这 ...
- Android源码浅析(五)——关于定制系统,如何给你的Android应用系统签名
Android源码浅析(五)--关于定制系统,如何给你的Android应用系统签名 今天来点简单的我相信很多定制系统的同学都会有一些特定功能的需求,比如 修改系统时间 静默安装 执行某shell命令 ...
- Android源码浅析(六)——SecureCRT远程连接Linux,配置端点和字节码
Android源码浅析(六)--SecureCRT远程连接Linux,配置端点和字节码 需要编译源码的同学,一般都是win+虚拟机吧,但是再虚拟机里体验并不是很好,所有市面上有很多的软件能够做到在wi ...
- Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令
Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...
- Android源码浅析(三)——Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机
Android源码浅析(三)--Android AOSP 5.1.1源码的同步sync和编译make,搭建Samba服务器进行更便捷的烧录刷机 最近比较忙,而且又要维护自己的博客,视频和公众号,也就没 ...
- Android开发之Theme、Style探索及源码浅析
1 背景 前段时间群里有伙伴问到了关于Android开发中Theme与Style的问题,当然,这类东西在网上随便一搜一大把模板,所以关于怎么用的问题我想这里也就不做太多的说明了,我们这里把重点放在理解 ...
随机推荐
- Spring常用接口和类
一.ApplicationContextAware接口 当一个类需要获取ApplicationContext实例时,可以让该类实现ApplicationContextAware接口.代码展示如下: p ...
- Maven的pom.xml文件结构之基本配置packaging和多模块聚合结构(微服务)
1. packaging packaging给出了项目的打包类型,即作为项目的发布形式,其可能的类型.在Maven 3中,其可用的打包类型如下: jar,默认类型 war ejb ear rar pa ...
- 将jdbc连接明文密码加密方案
最近没有及时写文章,把最近处理的几个问题集中了一下写出来.这篇文章是关于如何处理spring项目中引入数据库连接等 使用的用户名和密码的明文进行加密.防止被他人窃取利用. 我们选择的加密方式为DES加 ...
- 小白的Python之路_day2
Python 的逻辑运算符具有短路原则,例如: or 运算符前面只要是 True,后面都不需要看了,结果就是 True. Python 中表示为真必须用 True,如果用 true 则会当成是变量, ...
- Java为什么要配置环境变量及如何配置环境变量
在没有配置环境变量之前,用cmd执行Java文件,需要指明Java的可执行文件,否则无法运行. 配置环境是为了在不用切换可执行文件目录下,方便Java程序的执行和控制. 那么环境变量就是让系统根据环境 ...
- 全网代理公开ip爬取(隐藏元素混淆+端口加密)
简述 本次要爬取的网站是全网代理,貌似还是代理ip类网站中比较有名的几个之一,其官网地址: http://www.goubanjia.com/. 对于这个网站的爬取是属于比较悲剧的,因为很久之前就写好 ...
- 导出和导入Docker容器
导出容器 如果要导出本地某个容器,可以使用 docker export 命令. $ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATU ...
- java http post tomcat解除 长度限制
1. Get方法长度限制 Http Get方法提交的数据大小长度并没有限制,HTTP协议规范没有对URL长度进行限制.这个限制是特定的浏览器及服务器对它的限制. 如:IE对URL长度的限制是20 ...
- [多线程] 生产者消费者模型的BOOST实现
说明 如果 使用过程中有BUG 一定要告诉我:在下面留言或者给我邮件(sawpara at 126 dot com) 使用boost::thread库来实现生产者消费者模型中的缓冲区! 仓库内最多可以 ...
- 《Java多线程编程核心技术》推荐
写这篇博客主要是给猿友们推荐一本书<Java多线程编程核心技术>. 之所以要推荐它,主要因为这本书写得十分通俗易懂,以实例贯穿整本书,使得原本抽象的概念,理解起来不再抽象. 只要你有一点点 ...