概述
接着上次后台启动 Activity 的需求,依照 实战|Android后台启动Activity实践之路 中的一些方法处理后,虽然在 Android Q 版本上还是有一些问题,但后台启动的能力算是基本完成了,后来我又解开了小米 ROM 的源码,找到了他们后台启动这一项权限的实现方式以及怎么绕开这项权限的方法,发现结果意外的简单..(这部分以后有机会单独写一篇文章)。
这篇文章发生在后台启动的调研之后,如果我们要后台启动的 Activity 页面在第三方 SDK 里,且启动该页面的动作(startActivity)也发生在第三方 SDK 中,那么它们直接 startActivity 的方式是不具备后台启动的能力的,因为一些原因我们不能要求 SDK 方修改启动 Activity 的方法,因此需要找个方法能够在不修改第三方 SDK 调用 startActivity 代码的情况下,让其具备后台启动的能力。第一个反应就是拦截 startActivity 的请求,参考 Android之system_server进程 和 Android-Activity启动流程,我们知道 AMS 是 system_server 进程中的一个线程,它负责启动 Activity 的具体工作,在它的工作完成之后,会通过 Binder 调用回调 APP 进程中 Activity 实例的生命周期方法。当 APP 进程调用 startActivity 时,会由 Instrumentation 获取到 AMS 的 Binder 代理,然后通过它来跨进程调用 AMS 的相关方法,我们能做 Hook 拦截的地方就是这个 Binder 代理对象!
下面从各个 Android 版本看一下系统这个过程的实现方法以及我们是怎么拦截的,主要看一下 Android P 的源码,其它版本的虽然过程不一样,但是 Hook 的方式是类似的。
Android P
Android 8 到 Android 9 版本的 AOSP 获取 AMS 代理的方式是一样的,APP 进程在调用 context.startActivity 后,会来到 Instrumentation 中的相关方法里调用如下代码:
- int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent, ...);
这里通过 Binder 跨进程调用到 AMS 中的相关方法,看一下 ActivityManager.getService() 的实现:
- /** @hide */
- public static IActivityManager getService() {
- return IActivityManagerSingleton.get();
- }
- private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() {
- protected IActivityManager create() {
- // 1...
- }
- };
可以看到 IActivityManagerSingleton 是 Singleton 类型的实例,很显然这个 Singleton 是一个懒加载的单例模板类:
- 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;
- }
- }
- }
于是可以知道 IActivityManagerSingleton.get() 返回的便是 create 方法中的实例,给出上面 1 处省略的 create 方法代码:
- final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
- final IActivityManager am = IActivityManager.Stub.asInterface(b);
- return am;
熟悉 Binder 的同学一眼就能看出这里的 am 是一个 Binder 代理对象,存在 ServiceManager.getService 方法就肯定存在 ServiceManager.addService 方法,一个是从 ServiceManager 中查询 Binder 服务,一个是往 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;
- 通过反射用 Proxy 替换 am 对象;
我们看到 am 对象其实就是 Singleton(其实例是IActivityManagerSingleton) 中的 mInstance 属性,因此第三步只需通过反射将 mInstance 属性设置为我们的 Proxy 对象即可,下面的 AmsHooker 是一个抽象类,在不同的 Android 平台上有不同的实现,主要用来获取不同 Android 平台的 am 对象及通过反射替换 am 对象:
- abstract class AmsHooker {
- // 通过反射,将am替换成proxy
- fun hookAms(proxy: Any?) {
- try {
- val hookObj = getHookObj()
- val hookField = getHookField()
- if (hookObj != null && hookField != null && proxy != null) {
- hookField.set(hookObj, proxy)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- // 即IActivityManagerSingleton实例
- protected abstract fun getHookObj(): Any?
- // 即mInstance
- protected abstract fun getHookField(): Field?
- // 即am
- abstract fun getTarget(): Any?
- // 接口,用来创建Proxy
- abstract fun getInterfaces(): Array<Class<*>>
- }
在 Android P 平台上的实现如下,具体看注释:
- class AmsPHooker : AmsHooker() {
- override fun getHookObj(): Any? {
- val amClass = ReflectUtils.getClass("android.app.ActivityManager")
- // 拿到 IActivityManagerSingleton 属性
- return ReflectUtils.readStaticField(amClass, "IActivityManagerSingleton")
- }
- override fun getHookField(): Field? {
- // 获取 mInstance Field
- return ReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"), "mInstance")
- }
- override fun getTarget(): Any? {
- // ActivityManager.getService()返回 am
- return ReflectUtils.getClass("android.app.ActivityManager").getDeclaredMethod("getService").invoke(null)
- }
- // 获取interfaces,用来创建动态代理
- override fun getInterfaces(): Array<Class<*>> {
- return arrayOf(ReflectUtils.getClass("android.app.IActivityManager"))
- }
- }
接下来创建代理类(代码有删减):
- public class AMSProxy implements InvocationHandler {
- private AmsHooker hooker; // 根据不同 Android 平台返回不同实现
- private Object origAm; // 原有 am 对象
- private boolean ensureInit() {
- // ...
- hooker = getHooker();
- origAm = hooker.getTarget();
- }
- private AmsHooker getHooker() {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
- return new AmsQHooker();
- } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
- return new AmsPHooker();
- } else {
- return new AmsNHooker();
- }
- }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // ...
- }
- // 创建代理
- Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
- hooker.getInterfaces(), this);
- // 替换系统am对象
- hooker.hookAms(proxy);
- }
上面以 AMSProxy 实例为参数创建了一个代理对象 Proxy,并用这个 Proxy 对象通过 hookAms 方法替换掉了 am 对象,这样在本进程通过 ActivityManager.getService() 来调用相关方法时,会调用到上述的 invoke 方法,在这可以做拦截:
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- if (callback.canIntercept(method, args)) {
- if (callback.autoRemove()) {
- // 将am对象还原
- // ...
- }
- // 拦截am的请求,做自己的业务处理
- return callback.intercept(origAm, method, args);
- }
- return method.invoke(origAm, args);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
当本进程中有代码尝试通过 am 来调用相关方法(比如说startActivity等)时,都会被 invoke 方法所拦截,然后通过我们设置的拦截条件(canIntercept)去选择是否拦截。建议每次完成了拦截的业务需求后,就把原 am 对象通过 hookAms 方法还原,防止的本次进程中持续拦截系统请求。这里一直强调是本次进程,显而易见,通过反射去替换 am 对象的方式,只会针对本进程起作用。
Android Q
在 Android Q 上,上述 Instrumentation 中的调用变成如下:
- int result = ActivityTaskManager.getService().startActivity(whoThread, who.getBasePackageName(), intent, ...);
这变成了 ActivityTaskManager.getService():
- /** @hide */
- public static IActivityTaskManager getService() {
- return IActivityTaskManagerSingleton.get();
- }
- private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = new Singleton<IActivityTaskManager>() {
- protected IActivityTaskManager create() {
- final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
- return IActivityTaskManager.Stub.asInterface(b);
- }
- };
可以看到在 Android Q 上从 ActivityManager 变成了 ActivityTaskManager 系列的类,于是我们的 AmsQHooker 实现如下:
- class AmsQHooker : AmsHooker() {
- override fun getHookObj(): Any? {
- val amClass = ReflectUtils.getClass("android.app.ActivityTaskManager")
- // 拿到 IActivityTaskManagerSingleton 属性
- return ReflectUtils.readStaticField(amClass, "IActivityTaskManagerSingleton")
- }
- override fun getHookField(): Field? {
- return ReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"), "mInstance")
- }
- override fun getTarget(): Any? {
- // Reflective access to getService is forbidden when targeting API 29 and above
- // val getServiceMethod = amClass.getDeclaredMethod("getService")
- return ReflectUtils.getClass("android.util.Singleton").getDeclaredMethod("get").invoke(getHookObj())
- }
- override fun getInterfaces(): Array<Class<*>> {
- return arrayOf(ReflectUtils.getClass("android.app.IActivityTaskManager"))
- }
- }
其它的步骤跟 Android P 是一样的。
Android N
在 Android 7.1 及以下,Instrumentation 的调用又不一样:
- int result = ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent, ...);
这变成了 ActivityManagerNative.getDefault():
- static public IActivityManager getDefault() {
- return gDefault.get();
- }
- private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
- protected IActivityManager create() {
- IBinder b = ServiceManager.getService("activity");
- IActivityManager am = asInterface(b);
- return am;
- }
- };
可以看到虽然类名和方法有所变化,但还是借助了 Singleton 类,所以只需要继承 AmsHooker 重写相关方法即可:
- class AmsNHooker : AmsHooker() {
- override fun getHookObj(): Any? {
- val amNativeClass = ReflectUtils.getClass("android.app.ActivityManagerNative")
- // 获取gDefault实例
- return ReflectUtils.readStaticField(amNativeClass, "gDefault")
- }
- override fun getHookField(): Field? {
- return ReflectUtils.getField(ReflectUtils.getClass("android.util.Singleton"), "mInstance")
- }
- override fun getTarget(): Any? {
- return getHookField()?.get(getHookObj())
- }
- override fun getInterfaces(): Array<Class<*>> {
- return arrayOf(ReflectUtils.getClass("android.app.IActivityManager"))
- }
- }
其它的也是重用 Android P 上的逻辑。
总结
通过上面的方式,可以实现 在本进程内拦截通过 AMS 的 Binder 代理调用的相关方法,可以用来实现一些非常规的功能,虽然最近做的需求都比较非常规(liumang),不过抛开需求,对于开发而言去调研这些技术,还是挺有意思的..哈~
写博客是一件有意思有收获也有难度的事情,需要争取把文章的脉络和逻辑梳理清楚,怎么写才能把文章写得更加清晰易懂,又有好久没更新了,最近太忙了都没有时间做这些事情,想到自己写的文章(可)能被点赞,瞬间又有了动力,于是忙里偷鱼写了一篇,内容没啥深度,就当平时的开发笔记吧。