android插件开发-就是你了!启动吧!插件的activity(一)
通过之前的例子例子,我们学习了如何寻找hook点,并且做一些非常无聊的事情。比如是的粘贴板放一句无聊的句子,或者让系统在启动一个activity时打印一句话。这些看似无聊的事情其实都是为了本节做铺垫。
这一节会把之前的知识都贯穿起来——启动插件中的activity,不过这篇博客比较长,所以我分开成为两部分了第二部分
启动插件的activity还是非常难的一件事,因为在android中,所有的activity都必须在AndroidManifest.xml文件中声明。如果没有声明的话,启动它就会碰到下面的错误:
伤透脑筋啊~
由于android的机制,我们无法启动一个没有在AndroidManifest.xml中没有声明的activity,并且我们不能像平时写普通java代码一样,new一个Acticity对象出来就完事。因为android中的组件都是有生命的,不可以凭空产生,也不可以凭空消失,手动new出来的,它只是一个普通的对象,没有任何用处。那么我们是否可以,先在AndroidManifest.xml中声明一个activity,然后我们插件中的activity都通过它借尸还魂,以此来运行呢?想法有点大胆,不过也没办法,因为我们现在能想到的就这么多。
既然要借一个activity还魂,那么肯定得了解activity的启动原理啊,不然一切都真的是空谈。通过我们之前的学习,我们注意到,当启动一个activity时,Activity这个类中做这件事的其实是他的成员对象——mInstrumentation
在这个函数里面他最终是调用的是ActivityManagerNative.getDefault()的返回值来启动一个activity
ActivityManagerNative.getDefault返回的是一个Binder对象,他能够使用ActivityManagerService的服务(以下简称AMS)。正如其名,它正是管理activity的服务,由他赋予activity生命!
通过一系列的远程调用我们开始使用activity manager service的服务。其流程大概如下:
1:AMS调用ActivityStack的一系列方法来准备要启动的Activity的相关信息。我们平时说的什么任务栈啊都在这个类中有涉及
2:ActivityStack在完成一些准备工作后,通过ApplicationThread接口,远程通知当前的ui线程,我要准备调度了~注意!ApplicationThread这个接口是在activity启动另外一个activity的时候传入Activity的
关于它的信息在这里:
3:ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity。你大概可以感觉到了吧,ui线程通过ActivityManagerProxy与AMS”取得联系”,而AMS呢,通过ApplicationThread与ui线程获得联系
4: 对于通过点击应用程序图标来启动Activity的情景来说,AMS在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动
5: AMS调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;
6: ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。
以上内容有部分摘自老罗的博客
不过他看的android源码有点老了,现在的源码变化不小~
现在就开始分析吧
我们切入到AMS中看下:
1
他调用了另外一个成员函数(这里唠叨下,看到第一个参数没?AMS通过他和我们的ui线程通信)
2
这里的AMS代码被重构了一遍,这里是要进入到ActivityStackSupervisor这个类中去处理了。从名字上我们很容易看出,这里就是进行之前我们说的——让ActivityStack做一些准备工作
3
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
Bundle options, int userId, IActivityContainer iContainer, TaskRecord inTask) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
//查看下是否有component
boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
profilerInfo, userId);
...
int res = startActivityLocked(caller, intent, resolvedType, aInfo,
voiceSession, voiceInteractor, resultTo, resultWho,
requestCode, callingPid, callingUid, callingPackage,
realCallingPid, realCallingUid, startFlags, options,
componentSpecified, null, container, inTask);
...
return res;
}
}
这里有个非常重要的部分!
//查看下是否有component
boolean componentSpecified = intent.getComponent() != null;
我们平时可以有很多种方式启动一个activity,比如隐式,显式启动
隐式:
Intent intent = new Intent("your action");
...
startActivity(intent);
显式:
Intent intent = new Intent(context, xxx.class);
startActivity(intent);
我们这里只考虑显式。我们看下源码:
这个mComponent是一个ComponentName类型,他是系统用于区分组件的一个类:
好像有那么种感觉就是,AMS通过它区分要启动的activity是什么。回忆一下之前我介绍的activity启动流程。ActivityStack准备好一切之后,会回到ui线程,然后UI线程再回头问下AMS我是在当前进程启动一个activity还是再创建一个进程启动。这个过程是否有一种机制,让AMS能够快速识别这个Ui线程是哪个app的,毕竟手机里不止一个应用嘛。
我们不急,继续往下看。
之后的代码就是解析出当前要启动的activity信息:
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
profilerInfo, userId);
切进去看下:
@Override
3027 public ResolveInfo resolveIntent(Intent intent, String resolvedType,
3028 int flags, int userId) {
3029 if (!sUserManager.exists(userId)) return null;
3030 enforceCrossUserPermission(Binder.getCallingUid(),
userId, false, false, "resolve intent");
3031 List query = queryIntentActivities(intent, resolvedType, flags, userId);
//选择出最优的activity
3032 return chooseBestActivity(intent, resolvedType, flags, query, userId);
3033 }
queryIntentActivities:
3349 @Override
3350 public List More ...queryIntentActivities(Intent intent,
3351 String resolvedType, i