在上一篇我們留下問題:handleLoadPackage如何生效即在何時被執行。
先看XposedBridge.class的main(該函數是在appruntime.start函數中替換原先zygoteinit,故結尾處會調用zygoteinit):
1 private static void main(String[] args) { 2 String startClassName = getStartClassName(); 3 ...... 4 if(initNative()) { 5 if(startClassName == null) { 6 initXbridgeZygote(); 7 } 8 // 讀取/data/data/de.robv.android.xposed.installer/conf/modules.list內容,存儲着模塊apk的地址, 9 // 內容:/data/app/com.jason.learnxposed-2.apk 10 // 加載模塊loadModule(APK,startClassName) 11 loadModules(startClassName); 12 } 13 ...... 14 if(startClassName == null) { 15 ZygoteInit.main(args); 16 } else { 17 RuntimeInit.main(args); 18 } 20 }
main中分三步:initXbridgeZygote、initloadModules、ZygoteInit.main。
先來看initXbridgeZygote:
private static void initXbridgeZygote() throws Throwable { final HashSet loadedPackagesInProcess = new HashSet(1); XposedHelpers.findAndHookMethod(ActivityThread.class, "handleBindApplication", new Object[]{"android.app.ActivityThread.AppBindData", new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { }}); XposedHelpers.findAndHookMethod("com.android.server.ServerThread", (ClassLoader)null, VERSION.SDK_INT < 19?"run":"initAndLoop", new Object[]{new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { }}); hookAllConstructors(LoadedApk.class, new XC_MethodHook() { protected void afterHookedMethod(MethodHookParam param) throws Throwable { }); XposedHelpers.findAndHookMethod("android.app.ApplicationPackageManager", (ClassLoader)null, "getResourcesForApplication", new Object[]{ApplicationInfo.class, new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { }}); if(!(new File("/data/data/de.robv.android.xposed.installer/conf/disable_resources")).exists()) { hookResources(); } else { disableResources = true; } }
上面粉紅色的誘惑很熟悉對吧,就是前面上篇分析的findAndHookMethod函數。來看下它hooked了什么函數,"handleBindApplication","initAndLoop"
"getResourcesForApplication",不要小看這幾個hooked哦,這是xposed的基石。我們先不去看起before回調函數做了什么,等分析完loadModules后再回來有彩蛋。看紅色警戒的hookAllConstructors,之前沒見過這小子,看看他是干什么吃的。
public static Set<Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) { ...... for(int var4 = 0; var4 < var5; ++var4) { Constructor constructor = var6[var4]; unhooks.add(hookMethod(constructor, callback)); } ...... }
原來他也是要調用hookMethod,與findAndHookMethod區別是少了findMethodExact這步。前面分析過findMethodExact是把參數+返回值+函數名組成method,這樣相同的函數如果帶不同的參數也可以hook(xposed會根據method來判別是否已hooked),而hookAllConstructors直接來是什么用意呢?因為在hookAllConstructors中會hook所有的構造函數故沒有必要去findMethodExact。ok,上面就是幾個hooked,看下面if里是hookResources道理不言而喻咯,我們先不分析這個,有空再來^_^。
再來看loadModules
private static void loadModule(String apk, String startClassName) { ...... PathClassLoader mcl = new PathClassLoader(apk, BOOTCLASSLOADER); InputStream is = mcl.getResourceAsStream("assets/xposed_init"); // 讀取apk的assets/xposed_init內容,Xposed學習一中的第五步 if(is == null) { log("assets/xposed_init not found in the APK"); } else { BufferedReader moduleClassesReader = new BufferedReader(new InputStreamReader(is)); ...... if(startClassName == null) { if(moduleInstance instanceof IXposedHookZygoteInit) { StartupParam param = new StartupParam(); param.modulePath = apk; ((IXposedHookZygoteInit)moduleInstance).initZygote(param); } if(moduleInstance instanceof IXposedHookLoadPackage) { hookLoadPackage(new Wrapper((IXposedHookLoadPackage)moduleInstance)); } if(moduleInstance instanceof IXposedHookInitPackageResources) { hookInitPackageResources(new de.robv.android.xposed.IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources)moduleInstance)); } } else if(moduleInstance instanceof IXposedHookCmdInit) { de.robv.android.xposed.IXposedHookCmdInit.StartupParam param1 = new de.robv.android.xposed.IXposedHookCmdInit.StartupParam(); param1.modulePath = apk; param1.startClassName = startClassName; ((IXposedHookCmdInit)moduleInstance).initCmdApp(param1); } ...... } } }
簡單說下loadModule中步驟,
1. 讀取Xposed_init文本里的內容,里面是我們的類名。我們可以在一個模塊中寫好幾個功能的類,但請注意一定要將類全名字符串寫到Xposed_init里且每個類字符串為一行。到此,我們就得到類名可以加載類啦;
2. 根據startClassName是否為空又可分為執行普通的xposed模塊和IXposedHookCmdInit。startClassName值我們后面再深究,先繼續往下看;
3. 根據功能類實現的接口又可執行多步操作(當然功能類能實現多個接口)
3.1 IXposedHookZygoteInit:
話不多說,直接執行類的initZygote(IXposedHookCmdInit類同)。這個最干脆,不像下面2個還慢慢來^—^
3.2 IXposedHookLoadPackage:——>redClock示例中使用這個接口 貌似在XC_loadPackage的call中被執行,待分析
代碼就不貼了,反正在java層就是各種封裝和存儲。一句話總結:把類添加到
XposedBridge.CopyOnWriteSortedSet<XC_LoadPackage> sLoadedPackageCallbacks
3.3 IXposedHookInitPackageResources:
把類添加到XposedBridge.CopyOnWriteSortedSet<XC_InitPackageResources> sInitPackageResourcesCallbacks
ok,loadMoudle就算結束了。但我們得到2個list,肯定要執行的嘛。記得之前的蛋蛋嗎,重新回到initXbridgeZygote分析其before回調:
代碼我就不貼了,老是貼代碼搞得我很水一樣(* ̄rǒ ̄)。先看sLoadedPackageCallbacks,其出現在"handleBindApplication","initAndLoop","loadedAPK"的before回調函數里。那這三個是干什么的呢
handleBindApplication函數:mInstrumentation.callApplicationOnCreate(app);
initAndLoop函數:在serverThread的initAndLoop啟動系統服務(在system_server的main函數中)
loadedAPK類:
不用多說,很明顯其都和package有關。但是最終要的是在各自的before函數都包含代碼:XC_LoadPackage.callAll(lpparam);lpparam包含sLoadedPackageCallbacks,上面的代碼回去執行sLoadedPackageCallbacks中所有callbakc也就是我們在hookLoadPackage函數中添加的callback。即在此刻會調用所有實現IXposedHookLoadPackage接口的類中的handleLoadPackage()。有點繞哦,舉例子就是redClock中的handleLoadPackage會在此刻被調用,如果package=com.android.systemui則會執行findAndHookMethod函數去hooked "updateClock"函數。現在理清了吧
同理來看sInitPackageResourcesCallbacks,還記得上面initXbridgeZygote中的hookResources不。在hookResources會hooked"getTopLevelResources",而其after回調函數會執行XCallback.callAll(resparam1);當然 resparam1 = new InitPackageResourcesParam(XposedBridge.sInitPackageResourcesCallbacks);
第一步initXbridgeZygote和loadMoudle到此為此了啦。
來看init之前我們還是先看下startClassName這個字符串,它涉及到剛剛分析的loadModule和下面的init。
Android是基於Linux內核構建的,它最早存在的肯定是Native世界,那么Java世界是什么時候創建的呢? ---by 深入理解android
答案就是Zygote,而Zygote是通過init讀取init.rc文本中配置來創建。在init.rc(在android5.0中移到/system/core/rootdir/init_zygotexx.rc)中,關於Zygote的配置
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
執行/system/bin/app_process ,參數arg是-Xzygote /system/bin --zygote --start-system-server。在Xposed的app_main的main函數中會判斷器參數是否為先前定義的字符串(自行看代碼),若不是會賦值為className,而這就是我們上面的startClassName(這里如果表述有錯誤,往指出)。看完startClassName,我們看這里是是干什么的,執行ZygoteInit.main正式進入java世界。其實對比android原本的app_main執行流程,我們發現在XposedBridge.main中除了這個ZygoteInit.main代碼(xposed結合Zygote分析)其他都是xposed自己添加的,而這也正是xposed基石,基石,基石。
疑問:類實現多個接口,但像上轉接口時,每個接口時如何協調的。比如class 實現inface1 inface2,(inface1 )class和(inface2)class如何操作???