Android攔截AMS請求實戰
概述
接著上次后臺啟動 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),不過拋開需求,對于開發而言去調研這些技術,還是挺有意思的..哈~
寫博客是一件有意思有收獲也有難度的事情,需要爭取把文章的脈絡和邏輯梳理清楚,怎么寫才能把文章寫得更加清晰易懂,又有好久沒更新了,最近太忙了都沒有時間做這些事情,想到自己寫的文章(可)能被點贊,瞬間又有了動力,于是忙里偷魚寫了一篇,內容沒啥深度,就當平時的開發筆記吧。