Android中实现Activity的启动拦截之----实现360卫士的安装应用界面
第一、摘要
今天不是周末,但是我已经放假了,所以就开始我们的技术探索之旅,今天我们来讲一下Android中最期待的技术,就是拦截Activity的启动,其实我在去年的时候,就像实现这个技术了,但是因为知识不充足,就放弃了,当时的需求很简单,就是实现应用锁,就是给每个应用打开的时候加上密码,不了解的同学,可以看看这篇文章:
http://blog.csdn.net/jiangwei0910410003/article/details/42149975
不过,当时也是实现了,只是使用了监听TopActivity的方式实现的,后台起一个Service来轮询监听。但是当时想到了性能上的问题,因为感觉后台启动一个Service来进行轮询操作会很消耗性能,但是最后发现,现在市场上的应用锁应用实现的原理都是这么做的,所以当时就是用这个技术来做了。但是关于其他方式来做的工作没有断续,现在有时间就来研究一下如何通过拦截Activity的启动方式来实现拦截。因为:监听比轮训机制高效。
第二、前提准备
不过实现了拦截Activity启动技术实现之后,我们还会发现这个技术在很多地方都会用到。关于注入技术需要关注的文章:
http://blog.csdn.net/jiangwei0910410003/article/details/40949475
看完这篇文章,才能理解注入技术,才能看懂这篇文章。看完这篇文章之后,我们能看到三个文件:
poison; libproxybinder.so; DemoInject3.apk
关于这三个文件的作用就不多说了。看完文章就知道了。
第三、技术解析
涉及到源码类:
ContextImpl.java
ActivityThread.java
Instrumentation.java
ActivityManagerNative.java
那么这里我们现在需要拦截Activity的启动流程的话,那就要看看Activity的启动源代码:
先来看一下ContextImpl.java中的startActivity代码:
它内部是调用ActivityThread类的Instrumentation变量的execStartActivity方法:
它内部是调用ActivityManagerNative的startActivity方法的:
到这里我们可以看到了,这里使用了Binder机制来做的,我们知道
ActivityManager
IActivityManager
ActivityManagerNative
ActivityManagerService
ActivityManagerProxy
这几个类的关系:
关于他们之间的具体信息,这里就不做详细介绍了。
我们看到ActivityManagerNative.java中的代码之后,我们知道我们只需要拦截code为启动Activity的就可以了:START_ACTIVITY_TRANSACTION
当然我们还需要解析Parcel数据,才能得到启动Activity的信息:具体的Parcel数据格式我们在代码中也能看到:
data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); String callingPackage = data.readString(); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); IBinder resultTo = data.readStrongBinder(); String resultWho = data.readString(); int requestCode = data.readInt(); int startFlags = data.readInt(); ProfilerInfo profilerInfo = data.readInt() != 0 ? ProfilerInfo.CREATOR.createFromParcel(data) : null; Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int result = startActivity(app, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, options); reply.writeNoException(); reply.writeInt(result);
EntryClass.java
@Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { /** * data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); String callingPackage = data.readString(); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); IBinder resultTo = data.readStrongBinder(); String resultWho = data.readString(); int requestCode = data.readInt(); int startFlags = data.readInt(); ProfilerInfo profilerInfo = data.readInt() != 0 ? ProfilerInfo.CREATOR.createFromParcel(data) : null; Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int result = startActivity(app, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, options); reply.writeNoException(); reply.writeInt(result); */ if(code == 3){ try{ Log.i("TTT", "code:"+code); data.enforceInterface("android.app.IActivityManager"); IBinder b = data.readStrongBinder(); Log.i("TTT", "binder:"+b.getInterfaceDescriptor()); String callingPackage = data.readString(); Log.i("TTT","pkg:"+callingPackage); Intent intent = Intent.CREATOR.createFromParcel(data); Log.i("TTT", "intent:"+intent); if(intent != null){ Log.i("TTT", "data:"+intent.getData()); Log.i("TTT", "type:"+intent.getType()); } String resolvedType = data.readString(); Log.i("TTT", "resolvedType:"+resolvedType); }catch(Exception e){ Log.i("TTT", "error:"+Log.getStackTraceString(e)); } } return mBinder.transact(code, data, reply, flags); }
DemoInject3项目的下载地址:http://download.csdn.net/detail/jiangwei0910410003/9147665
第四、运行程序
我们得到上面的三个文件:poison; libproxybinder.so; DemoInject3.apk
下载地址:http://download.csdn.net/detail/jiangwei0910410003/9147671
将poison和libproxybinder.so两个文件放到data/data目录下
adb push poison /data/data
adb push libproxybinder.so /data/data
将DemoInject3.apk存放到/data/local/tmp目录下
adb push DemoInject3.apk /data/local/tmp
运行:
先找到system_server的进程id,然后注入
然后到/data/data/目录下
运行poison
这时候我们使用 adb logcat -s TTT 查看log:
注入成功,那么这时候我们打开几个App看看log:
看到log之后,我们可以看到我们的拦截成功了,我们可以得到启动Activity的Intent内容和App的包名信息等。
多么激动的一天,拦截成功之后,我们什么事都可以干了。这个实现应用锁的效率会高很多的。但是这个也是有一个弊端,需要root。
第五、拦截系统安装界面,实现自己的安装界面
那么这个技术除了实现应用锁,还有其他什么用途呢?
下面我们来看一下360卫士的自定义安装界面的实现,先来看看他的效果:
我们看到他能够替换系统的安装界面,自己定义的安装界面。那么我们现在也是可以实现的。我们现在打开一个apk安装:
这里我们可以看到Intent中携带的信息,和type类型,pkg是安装来源的App的包名,Intent中有待安装的apk路径,有了路径之后,我们就可以得到这个apk的信息。那么这个安装界面的实现就不难了。
当然这个问题困扰了我很长时间,也是这个问题驱使我义无反顾的来解决这个问题。360的技术很不了的。一个360卫士的Apk我们学到的东西很多,而且可能学不完。后面我还有一篇文章专门来解读360手机卫士的技术点实现。
第六、总结
今天就介绍了如何通过注入技术来实现拦截Activity的启动,这个技术的实现,能够解决我们很多问题,当然这个注入技术是需要root的,所以这里只是理论介绍他可以来实现应用锁。但是我们在实际项目中,不会采用这个技术的,原因很简单,root是一个可以研究的技术,但是本人坚决反对使用这个技术来开发产品,因为这个技术是和Google开发者相违背的。这么做是不正确的。当然只是个人观点。不过后面我还会介绍一种技术来实现应用锁技术:辅助功能(AccessibilityService)。
PS: 关注微信,最新Android技术实时推送