您现在的位置是:首页 > 文章详情

13.源码阅读(启动一个没有注册的Activity为何会抛出异常-have you declared this activity in yo...

日期:2018-04-17点击:338

app中每一个activity都要在AndroidManifest文件中配置,否则启动会抛出异常

Unable to find explicit activity class ..; have you declared this activity in your AndroidManifest.xml? 

那么我们是否可以启动一个没有注册的activity呢?这就是今天看源码的目的

系统如何检查AndroidManifest中是否注册有一个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文件的过程,这个过程我们放到下一篇博客中说,今天看到这里基本上已经达到我们的目的了,总结一下

所有在AndroidManifest中注册的activity会被加入一个集合中,这个集合以这个activity的包名+类型为key值存储这个activity,当调用startActivity启动一个activity的时候,会根据这个activity的全类名去这个集合中查找,如果查找不到就表明这个activity没有在AndroidManifest中注册,抛出异常(这里省略了系统扫描AndroidManifest配置文件的过程,以app启动时,activity已经被加入集合为前提)

再次回到我们最初的问题,我们是否可以启动一个没有注册的activity?通过看源码也了解到了,之所以没有注册的activity启动会被抛出异常,是因为有一个监测的过程,如果能绕过这个监测过程,岂不是就可以启动这个activity了

这里采用偷梁换柱的方式来实现,通过动态代理hook Activity的启动过程,启动activity时,系统通过Intent中传递的Activity的ComponentName去集合中查找,只要查找得到就会躲过检查,那么我们的思路是这样的,当我们要启动一个未注册的activity时,传递一个已经注册的activity的componentname作为傀儡,当监测通过的时候,真正要启动这个activity的时候再将这个未注册的替换过去,达到偷梁换柱的目的

具体实现见下一篇博客

原文链接:https://yq.aliyun.com/articles/657344
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章