接着上次後臺啓動 Activity 的需求,依照 實戰|Android後臺啓動Activity實踐之路 中的一些方法處理後,雖然在 Android Q 版本上仍是有一些問題,但後臺啓動的能力算是基本完成了,後來我又解開了小米 ROM 的源碼,找到了他們後臺啓動這一項權限的實現方式以及怎麼繞開這項權限的方法,發現結果意外的簡單..(這部分之後有機會單獨寫一篇文章)。android
這篇文章發生在後臺啓動的調研以後,若是咱們要後臺啓動的 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 代理對象!git
下面從各個 Android 版本看一下系統這個過程的實現方法以及咱們是怎麼攔截的,主要看一下 Android P 的源碼,其它版本的雖然過程不同,可是 Hook 的方式是相似的。github
Android 8 到 Android 9 版本的 AOSP 獲取 AMS 代理的方式是同樣的,APP 進程在調用 context.startActivity 後,會來到 Instrumentation 中的相關方法裏調用以下代碼:web
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>() {
@Override
protected IActivityManager create() {
// 1...
}
};
複製代碼
能夠看到 IActivityManagerSingleton 是 Singleton 類型的實例,很顯然這個 Singleton 是一個懶加載的單例模板類:markdown
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 方法代碼:app
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啓動流程,這裏就不深刻描述了。ide
因此 ActivityManager.getService()
方法其實就是返回了 AMS 的一個 Binder 代理對象,用來跨進程調用 AMS 相關方法,所以能夠經過 JDK 動態代理的方式,經過 Proxy.newProxyInstance
方法建立 am 的代理 Proxy 對象,並經過反射的方式將 ActivityManager.getService()
方法返回的 am 對象替換成咱們的 Proxy 對象,那麼在 App 進程調用 ActivityManager.getService().XXX
方法時都會被咱們的 Proxy 攔截到,進而作一些處理。JDK 動態代理也是 Java 經常使用的設計模式之一,不太熟悉的同窗能夠參考 Jdk動態代理 的使用。oop
這個過程能夠分紅三個步驟:post
ActivityManager.getService()
是一個隱藏方法,所以能夠經過反射調用它拿到原 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();
}
}
@Override
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 方法,在這能夠作攔截:
@Override
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 上,上述 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>() {
@Override
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 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),不過拋開需求,對於開發而言去調研這些技術,仍是挺有意思的..哈~
PS:最近發現寫的一些文章在其它平臺轉載了,可是原有的博客連接被刪除了,也沒有標明轉載出處(不過圖片水印沒刪..😀),像 Android圖形系統綜述 這幾篇文章轉載後全都得到了四百多個點贊,比我掘金的總點贊都多,大吃一斤檸檬🍋啊~^~做爲一個新人很是開心有人轉載個人文章,不過但願xuowei標明一下出處哈,我的仍是挺喜歡掘金的技術氛圍的,以前註冊過其它平臺的帳號,嘗試發文章發現都是石沉大海,妹想到在掘金能收穫到意外之喜,扯遠了。
文中內容若有錯誤歡迎指出,共同進步!以爲不錯的留個 贊 再走哈~
插個題外話,能夠的話幫忙投幾個票哈:蒼耳叔叔,不勝感激!