概述接下来是上次后台启动Activity的需求,在处理了实战|Android后台启动Activity实战之路的一些方法后,虽然在AndroidQ中还存在一些问题版本,后台启动能力基本完成,然后我解锁了小米ROM的源码,了解了他们的后台启动权限是如何实现的,以及如何绕过这个权限,发现结果出奇的简单。。(这部分以后有机会单独写一篇文章)。这篇文章发生在研究后台启动之后。如果我们要后台启动的Activity页面在第三方SDK中,启动页面的动作(startActivity)也发生在第三方SDK中,那么他们直接startActivity。有了后台启动的能力,由于某些原因我们不能要求SDK去修改启动Activity的方法,所以我们需要想办法让它有后台启动的能力而不需要修改第三个——partySDK调用startActivity代码。第一反应是拦截startActivity请求。参考Androidsystem_server进程和Android-Activity启动流程,我们知道AMS是system_server进程中的一个线程,负责Activity启动的具体工作。它的工作完成后,会通过Binder在回调APP进程中调用Activity实例的生命周期方法。APP进程调用startActivity时,会通过Instrumentation获取AMS的Binder代理,然后通过它跨进程调用AMS的相关方法。我们可以做Hook拦截的地方就是这个Binder代理对象!下面我们从各个Android版本来看一下系统中这个进程的实现方式以及我们是如何拦截的,主要看AndroidP的源码。其他版本的进程虽然不一样,但是hook的方式是类似的。AndroidPAndroid8到Android9版本的AOSP以同样的方式获取AMS代理。APP进程调用context.startActivity后,会在Instrumentation中的相关方法中调用如下代码:intresult=ActivityManager.getService().startActivity(whoThread,who.getBasePackageName(),intent,...);这里通过Binder跨进程调用AMS中的相关方法,查看ActivityManager.getService()的实现:(){protectedIActivityManagercreate(){//1...}};可以看到IActivityManagerSingleton是Singleton类型的一个实例,显然这个Singleton是一个懒加载单例模板类:publicabstractclassSingleton{privateTmInstance;protectedabstractTcreate();publicfinalTget(){synchronized(this){if(mInstance==null){mInstance=create();}returnmInstance;}}}所以可以知道IActivityManagerSingleton.get()返回的是create方法中的实例,上面1处省略了create方法的代码:finalIBinderb=ServiceManager.getService(Context.ACTIVITY_SERVICE);finalIActivityManageram=IActivityManager.Stub.asInterface(b);returnam;熟悉Binder的同学一眼就能看出amhere是一个Binder代理对象。如果有ServiceManager.getService方法,则必须有ServiceManager.addService方法。一种是从ServiceManagerBinder服务中查询,一种是在ServiceManager中注册服务。注册的时机是系统启动system_server进程的时候。参考AMS启动流程。这里不做深入描述,所以ActivityManager.getService()方法实际上返回的是一个AMS的Binder代理对象,用于跨进程调用AMS相关方法,所以可以使用JDK动态代理方法,通过Proxy.newProxyInstance方法创建am的代理Proxy对象,将ActivityManager.getService()方法返回的am对象替换为我们的Proxy对象,那么当App进程调用ActivityManager.getService().XXX方法的时候,就会被我们的Proxy拦截,然后做一些处理。JDK动态代理也是Java中常用的设计模式之一。不熟悉的同学可以参考Jdk动态代理的使用。这个过程可以分为三个步骤:反射得到am对象,由于ActivityManager.getService()是一个隐藏方法,可以通过反射调用得到原来的am对象;创建代理对象Proxy;通过反射将am对象替换为Proxy;我们看到am对象其实就是Singleton中的mInstance属性(它的实例是IActivityManagerSingleton),所以第三步只需要通过反射将mInstance属性设置给我们的Proxy对象即可。下面的AmsHooker是一个抽象类,可以在不同的Android平台上有不同的实现,主要用于获取不同Android平台的am对象,通过反射替换am对象:abstractclassAmsHooker{//用proxyfunhookAms(proxy:Any?){try{valhookObj=getHookObj()throughreflectionvalhookField=getHookField()if(hookObj!=null&&hookField!=null&&proxy!=null){hookField.set(hookObj,proxy)}}catch(e:Exception){e.printStackTrace()}}//即IActivityManagerSingleton实例protectedabstractfungetHookObj():Any?//mInstanceprotectedabstractfungetHookField():Field?//amabstractfungetTarget():Any?//接口,用于在AndroidP平台上创建ProxyabstractfungetInterfaces():Array>}如下,详见注释:classAmsPHooker:AmsHooker(){overridefungetHookObj():Any?{valamClass=ReflectUtils.getClass("android.app.ActivityManager")//获取IActivityManagerSingleton属性returnReflectUtils.readStaticField(amClass,"IActivityManagerSingleton")}overridefungetHookField():Field?{//获取mInstanceFieldreturnReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"),)"mInstoverridedefungetTarget():Any?{//ActivityManager.getService()returnsamreturnReflectUtils.getClass("android.app.ActivityManager").getDeclaredMethod("getService").invoke(null)}//获取创建动态代理的接口overridedefungetInterfaces():Array>{returnarrayOf(ReflectUtils.getClass("android.app.IActivityManager"))}}接下来创建代理类(代码已删):publicclassAMSProxyimplementsInvocationHandler{privateAmsHookerhooker;//根据不同的Android的平台返回不同的实现privateObjectorigAm;//原始am对象privatebooleanensureInit(){//...hooker=getHooker();origAm=hooker.getTarget();}privateAmsHookergetHooker(){if(Build.VERSION.SDK_INT>Build.VERSION_CODES.P){returnnewAmsQHooker();}elseif(Build.VERSION.SDK_INT>Build.VERSION_CODES.N_MR1){returnnewAmsPHooker();}else{returnnewAmsNHooker();}}publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{//...}//创建代理Objectproxy=Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),hooker.getInterfaces(),this);//替换系统am对象hooker.hookAms(proxy);}上面以AMSProxy实例为参数创建了一个代理对象Proxy,而这个Proxy对象是通过hookAms方法替换am对象,这样当进程通过ActivityManager.getService()调用相关方法时,就会调用上面提到的invoke方法,可以拦截:publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{try{if(callback.canIntercept(method,args)){if(callback.autoRemove()){//恢复am对象//...}//拦截请求am并做你自己的业务处理returncallback.intercept(origAm,method,args);}returnmethod.invoke(origAm,args);}catch(Exceptione){e.printStackTrace();}returnnull;}当这个过程中有代码试图通过am调用相关方法时(比如startActivity等),就会被invoke方法拦截,然后我们可以通过我们设置的拦截条件(canIntercept)来选择是否拦截。建议在完成拦截的业务需求后,通过hookAms方法恢复原来的am对象。防止在此过程中不断拦截系统请求。这里已经强调了,就是这个过程。显然,通过反射替换am对象的方法只会对这个过程起作用。AndroidQ在AndroidQ上,上面Instrumentation中的调用变成了这样:intresult=ActivityTaskManager.getService().startActivity(whoThread,who.getBasePackageName(),intent,...);这变成ActivityTaskManager.getService():/**@hide*/publicstaticIActivityTaskManagergetService(){returnIActivityTaskManagerSingleton.get();}privatestaticfinalSingletonIActivityTaskManagerSingleton=newSingleton(){protectedIActivityTaskManagercreate(){finalIBinderb=ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);returnIActivityTaskManager.Stub.asInterface(b);}};可以看到在AndroidQ上,已经从ActivityManager变成了ActivityTaskManager系列类,所以我们的AmsQHooker实现如下:getClass("android.app.ActivityTaskManager")//获取IActivityTaskManagerSingleton属性eflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"),"mInstance")}overridefungetTarget():Any?{//ReflectiveaccesstogetServiceisforbiddenwhentargetingAPI29andabove//valgetServiceMethod=amClass.getDeclaredMethod("getService")returnReflectUtils.getClass("android.util.Singleton").getDeclaredMethod("get").invoke(getHookObj())}overridefungetInterfaces():Array>{returnarrayOf(ReflectUtils.getClass("android.app.IActivityTaskManager"))}}它的步骤跟上AndroidP是一样的AndroidN在Android7.1及以下,Instrumentation的调用有所不同:intresult=ActivityManagerNative.getDefault().startActivity(whoThread,who.getBasePackageName(),intent,...);这变成了ActivityManagerNative.getDefault():(b)};;可以看到虽然类名和方法都变了,但还是使用了Singleton类,所以只需要继承AmsHooker,重写相关方法即可:("android.app.ActivityManagerNative")//获取gDefault实例returnReflectUtils.readStaticField(amNativeClass,"gDefault")}overridefungetHookField():Field?{returnReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"),"mInstance")}overridedefungetTarget():Any?{returngetHookField()?.get(getHookObj())}overridedefungetInterfaces():Array>{returnarrayOf(ReflectUtils.getClass("android.app.IActivityManager"))}}别人也复用了AndroidP上的逻辑来综上所述,通过上述方法,可以拦截AMS的Binder代理在这个过程中调用的相关方法,可以用来实现一些非常规的功能。虽然最近的需求比较标新立异(liumang),但是抛开需求,对于开发来说,研究这些技术还是蛮有意思的。哈~写博客是一件有趣,有收??获,也有难度的事情。有必要尝试梳理一下文章的脉络和逻辑。怎么写才能使文章更好。清晰易懂,好久没更新了。最近太忙了,没时间做这些事情。心想自己的文章(可能)会被点赞,瞬间又有动力了,于是趁着忙写了一篇,内容不是很深,就当作平时的开发笔记吧。