赞
踩
app中每一个activity都要在AndroidManifest文件中配置,否则启动会抛出异常
Unable to find explicit activity class ..; have you declared this activity in your AndroidManifest.xml?
那么我们是否可以启动一个没有注册的activity呢?这就是今天看源码的目的
在文章03.源码阅读(Activity启动流程--android api 23)中我们已经通过源码知道,启动一个activity调用startActivity后,会进入Instrumentation的方法中
- public ActivityResult execStartActivity(
- Context who, IBinder contextThread, IBinder token, Activity target,
- Intent intent, int requestCode, Bundle options) {
- ......
- int result = ActivityManagerNative.getDefault()
- .startActivity(whoThread, who.getBasePackageName(), intent,
- intent.resolveTypeIfNeeded(who.getContentResolver()),
- token, target != null ? target.mEmbeddedID : null,
- requestCode, 0, null, options);
- //通过result值进行一些列的异常抛出,上边的那个就是这里抛出的,
- checkStartActivityResult(result, intent);
- ......
- }
已经知道ActivityManagerNative.getDefault()(api26叫ActivityManager.getService())这行代码已经看过多次,但是这次还是要贴出来说一说,因为很重要,它得到的是IActivityManager这个接口的一个实现类----ActivityMangagerService,最终还是要去看ActivityMangagerService中的startActivity
- /**
- * Retrieve the system's default/global activity manager.
- */
- static public IActivityManager getDefault() {
- return gDefault.get();
- }
-
- private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
- protected IActivityManager create() {
- IBinder b = ServiceManager.getService("activity");
- if (false) {
- Log.v("ActivityManager", "default service binder = " + b);
- }
- IActivityManager am = asInterface(b);
- if (false) {
- Log.v("ActivityManager", "default service = " + am);
- }
- return am;
- }
- };
-
- public abstract class Singleton<T> {
- private T mInstance;
-
- protected abstract T create();
-
- public final T get() {
- synchronized (this) {
- if (mInstance == null) {
- mInstance = create();
- }
- return mInstance;
- }
- }
- }
IActivityManager这个接口是hook拦截activity创建的关键,这里先放着
进入ActivityManagerService的startActivity中,一路点进入会来到ActivityStackSupervisor类中的startActivityLocked方法
- final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType, ActivityInfo aInfo,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- IBinder resultTo, String resultWho, int requestCode,
- int callingPid, int callingUid, String callingPackage,
- int realCallingPid, int realCallingUid, int startFlags, Bundle options,
- boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
- ActivityContainer container, TaskRecord inTask) {
- //activity启动的返回结果
- int err = ActivityManager.START_SUCCESS;
- ......
- if (err == ActivityManager.START_SUCCESS && aInfo == null) {
- // 就是在这个返回值的情况下会抛出activity未注册的异常
- err = ActivityManager.START_CLASS_NOT_FOUND;
- }
- ......
- }
看一下什么情况下会返回这个,err == ActivityManager.START_SUCCESS 一上来就设置了,所以关键看aInfo,它什么时候会为null,那么现在要回退过去了,看看aInfo是何时赋值的,找到ActivityStackSupervisor中的方法startActivityMayWait
- 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, boolean ignoreTargetSecurity, int userId,
- IActivityContainer iContainer, TaskRecord inTask) {
- ......
- ActivityInfo aInfo =
- resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
- ......
- }
- ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
- ProfilerInfo profilerInfo, int userId) {
- ......
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- //ActivityInfo是从ResolveInfo中的activityInfo赋值得到的,所以需要找到ResolveInfo如何获取的
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
- ......
- }
AppGlobals.getPackageManager()最终得到的是IPackageManager的实现类PackageManagerService,来到这里的resolveIntent方法->chooseBestActivity->findPreferredActivity->getActivityInfo
- @Override
- public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- //检查users-permission
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
- synchronized (mPackages) {
- PackageParser.Activity a = mActivities.mActivities.get(component);
-
- if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
- PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
- if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
- userId);
- }
- if (mResolveComponentName.equals(component)) {
- return PackageParser.generateActivityInfo(mResolveActivity, flags,
- new PackageUserState(), userId);
- }
- }
- return null;
- }
进入PackageParser中,可以看到,只要Activity a不为null,ActivityInfo就不会为null,那么到了这里又要回退过去看看Activity什么时候为null的
- public static final ActivityInfo generateActivityInfo(Activity a, int flags,
- PackageUserState state, int userId) {
- if (a == null) return null;
- if (!checkUseInstalledOrHidden(flags, state)) {
- return null;
- }
- if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
- return a.info;
- }
- // Make shallow copies so we can store the metadata safely
- ActivityInfo ai = new ActivityInfo(a.info);
- ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
- return ai;
- }
来到PackageManagerService类中,可以看到activity是从集合中取出来的
- @Override
- public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
- enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
- synchronized (mPackages) {
- PackageParser.Activity a = mActivities.mActivities.get(component);
-
- if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
- if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
- PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
- if (ps == null) return null;
- return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
- userId);
- }
- if (mResolveComponentName.equals(component)) {
- return PackageParser.generateActivityInfo(mResolveActivity, flags,
- new PackageUserState(), userId);
- }
- }
- return null;
- }
PackageParser.Activity a = mActivities.mActivities.get(component);这个集合以ComponentName为key,以这个Activity为value,ComponentName肯定是类的全类名,包名+类名的形式,那么能不能从这个集合中取出这个activity关键就是看这个activity有没有被加入集合了
- private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
- = new ArrayMap<ComponentName, PackageParser.Activity>();
可以在PackageManagerService类中找到这个方法
- public final void addActivity(PackageParser.Activity a, String type) {
- final boolean systemApp = a.info.applicationInfo.isSystemApp();
- mActivities.put(a.getComponentName(), a);
- ......
- }
这里可以猜测一下,app启动后(或者app安装时就已经扫描保存了)应该会扫描AndroidManifest文件,然后将activity都加入到这个集合,然后启动一个activity的时候如果从这个集合中找不到这个activity,就抛出异常activity未在AndroidManifest中注册,所以肯定有一个扫描AndroidManifest文件的过程,这个过程我们放到下一篇博客中说,今天看到这里基本上已经达到我们的目的了,总结一下
再次回到我们最初的问题,我们是否可以启动一个没有注册的activity?通过看源码也了解到了,之所以没有注册的activity启动会被抛出异常,是因为有一个监测的过程,如果能绕过这个监测过程,岂不是就可以启动这个activity了
这里采用偷梁换柱的方式来实现,通过动态代理hook Activity的启动过程,启动activity时,系统通过Intent中传递的Activity的ComponentName去集合中查找,只要查找得到就会躲过检查,那么我们的思路是这样的,当我们要启动一个未注册的activity时,传递一个已经注册的activity的componentname作为傀儡,当监测通过的时候,真正要启动这个activity的时候再将这个未注册的替换过去,达到偷梁换柱的目的
具体实现见下一篇博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。