Optimize module loading under dynamic-modules mode
by only loading newly added or updated modules
This commit is contained in:
parent
be9105317f
commit
f6d2e3b62f
|
|
@ -1,4 +1,4 @@
|
||||||
version: '0.4.4.6_alpha({build})'
|
version: '0.4.4.7_alpha({build})'
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
ANDROID_HOME: C:\android-sdk-windows
|
ANDROID_HOME: C:\android-sdk-windows
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,10 @@ public abstract class BaseRouter implements Router {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadModulesSafely(boolean isInZygote) {
|
public void loadModulesSafely(boolean isInZygote, boolean callInitZygote) {
|
||||||
try {
|
try {
|
||||||
// FIXME some coredomain app can't reading modules.list
|
// FIXME some coredomain app can't reading modules.list
|
||||||
XposedInit.loadModules(isInZygote);
|
XposedInit.loadModules(isInZygote, callInitZygote);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
Utils.logE("error loading module list", exception);
|
Utils.logE("error loading module list", exception);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,7 @@ public class BlackWhiteListProxy extends BaseProxy {
|
||||||
// because installed hooks would be propagated to all child processes of zygote
|
// because installed hooks would be propagated to all child processes of zygote
|
||||||
mRouter.startWorkAroundHook();
|
mRouter.startWorkAroundHook();
|
||||||
// loadModules once for all child processes of zygote
|
// loadModules once for all child processes of zygote
|
||||||
// TODO maybe just save initZygote callbacks and call them when whitelisted process forked?
|
mRouter.loadModulesSafely(true, false);
|
||||||
mRouter.loadModulesSafely(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
|
private void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
|
||||||
|
|
@ -104,12 +103,15 @@ public class BlackWhiteListProxy extends BaseProxy {
|
||||||
mRouter.prepare(isSystemServer);
|
mRouter.prepare(isSystemServer);
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods();
|
PrebuiltMethodsDeopter.deoptBootMethods();
|
||||||
mRouter.installBootstrapHooks(isSystemServer);
|
mRouter.installBootstrapHooks(isSystemServer);
|
||||||
|
|
||||||
|
// under dynamic modules mode, don't call initZygote when loadModule
|
||||||
|
// cuz loaded module won't has that chance to do it
|
||||||
if (isDynamicModulesMode) {
|
if (isDynamicModulesMode) {
|
||||||
mRouter.loadModulesSafely(false);
|
mRouter.loadModulesSafely(false, false);
|
||||||
} else {
|
|
||||||
XposedBridge.callInitZygotes();
|
|
||||||
XposedBridge.clearInitZygotes(); // one-time use
|
|
||||||
}
|
}
|
||||||
|
// call all initZygote callbacks
|
||||||
|
XposedBridge.callInitZygotes();
|
||||||
|
|
||||||
mRouter.onForkFinish();
|
mRouter.onForkFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,8 +133,6 @@ public class BlackWhiteListProxy extends BaseProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void onBlackListed() {
|
private static void onBlackListed() {
|
||||||
XposedBridge.clearLoadedPackages();
|
XposedBridge.clearAllCallbacks();
|
||||||
XposedBridge.clearInitPackageResources();
|
|
||||||
XposedBridge.clearInitZygotes();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public class NormalProxy extends BaseProxy {
|
||||||
// install bootstrap hooks for secondary zygote
|
// install bootstrap hooks for secondary zygote
|
||||||
mRouter.installBootstrapHooks(false);
|
mRouter.installBootstrapHooks(false);
|
||||||
// only load modules for secondary zygote
|
// only load modules for secondary zygote
|
||||||
mRouter.loadModulesSafely(true);
|
mRouter.loadModulesSafely(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
public void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
|
||||||
|
|
@ -35,7 +35,7 @@ public class NormalProxy extends BaseProxy {
|
||||||
mRouter.prepare(false);
|
mRouter.prepare(false);
|
||||||
mRouter.onEnterChildProcess();
|
mRouter.onEnterChildProcess();
|
||||||
// load modules for each app process on its forked if dynamic modules mode is on
|
// load modules for each app process on its forked if dynamic modules mode is on
|
||||||
mRouter.loadModulesSafely(false);
|
mRouter.loadModulesSafely(false, true);
|
||||||
mRouter.onForkFinish();
|
mRouter.onForkFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ public class NormalProxy extends BaseProxy {
|
||||||
// loadModules have to be executed in zygote even isDynamicModules is false
|
// loadModules have to be executed in zygote even isDynamicModules is false
|
||||||
// because if not global hooks installed in initZygote might not be
|
// because if not global hooks installed in initZygote might not be
|
||||||
// propagated to processes not forked via forkAndSpecialize
|
// propagated to processes not forked via forkAndSpecialize
|
||||||
mRouter.loadModulesSafely(true);
|
mRouter.loadModulesSafely(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forkSystemServerPost(int pid) {
|
public void forkSystemServerPost(int pid) {
|
||||||
|
|
@ -63,7 +63,9 @@ public class NormalProxy extends BaseProxy {
|
||||||
mRouter.prepare(true);
|
mRouter.prepare(true);
|
||||||
mRouter.onEnterChildProcess();
|
mRouter.onEnterChildProcess();
|
||||||
// reload module list if dynamic mode is on
|
// reload module list if dynamic mode is on
|
||||||
mRouter.loadModulesSafely(false);
|
if (ConfigManager.isDynamicModulesEnabled()) {
|
||||||
|
mRouter.loadModulesSafely(false, true);
|
||||||
|
}
|
||||||
mRouter.onForkFinish();
|
mRouter.onForkFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ public interface Router {
|
||||||
|
|
||||||
void installBootstrapHooks(boolean isSystem);
|
void installBootstrapHooks(boolean isSystem);
|
||||||
|
|
||||||
void loadModulesSafely(boolean isInZygote);
|
void loadModulesSafely(boolean isInZygote, boolean callInitZygote);
|
||||||
|
|
||||||
void startBootstrapHook(boolean isSystem);
|
void startBootstrapHook(boolean isSystem);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import org.gradle.internal.os.OperatingSystem
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
// Values set here will be overriden by AppVeyor, feel free to modify during development.
|
// Values set here will be overriden by AppVeyor, feel free to modify during development.
|
||||||
def buildVersionName = 'v0.4.4.6_alpha'
|
def buildVersionName = 'v0.4.4.7_alpha'
|
||||||
def buildVersionCode = 10000
|
def buildVersionCode = 10000
|
||||||
|
|
||||||
if (System.env.APPVEYOR_BUILD_VERSION != null) {
|
if (System.env.APPVEYOR_BUILD_VERSION != null) {
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ namespace edxp {
|
||||||
variant_ = static_cast<Variant>(variant);
|
variant_ = static_cast<Variant>(variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LOGI("EdxpVariant: %d", variant_);
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ public class SandHookRouter extends BaseRouter {
|
||||||
public void injectConfig() {
|
public void injectConfig() {
|
||||||
EdXpConfigGlobal.sConfig = new SandHookEdxpConfig();
|
EdXpConfigGlobal.sConfig = new SandHookEdxpConfig();
|
||||||
EdXpConfigGlobal.sHookProvider = new SandHookProvider();
|
EdXpConfigGlobal.sHookProvider = new SandHookProvider();
|
||||||
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,11 @@
|
||||||
package com.elderdrivers.riru.edxp.whale.core;
|
package com.elderdrivers.riru.edxp.whale.core;
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.res.CompatibilityInfo;
|
|
||||||
|
|
||||||
import com.elderdrivers.riru.edxp._hooker.impl.HandleBindApp;
|
|
||||||
import com.elderdrivers.riru.edxp._hooker.impl.LoadedApkCstr;
|
|
||||||
import com.elderdrivers.riru.edxp._hooker.yahfa.HandleBindAppHooker;
|
|
||||||
import com.elderdrivers.riru.edxp._hooker.yahfa.LoadedApkConstructorHooker;
|
|
||||||
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
||||||
import com.elderdrivers.riru.edxp.framework.Zygote;
|
import com.elderdrivers.riru.edxp.framework.Zygote;
|
||||||
import com.elderdrivers.riru.edxp.proxy.BaseRouter;
|
import com.elderdrivers.riru.edxp.proxy.BaseRouter;
|
||||||
import com.elderdrivers.riru.edxp.whale.config.WhaleEdxpConfig;
|
import com.elderdrivers.riru.edxp.whale.config.WhaleEdxpConfig;
|
||||||
import com.elderdrivers.riru.edxp.whale.config.WhaleHookProvider;
|
import com.elderdrivers.riru.edxp.whale.config.WhaleHookProvider;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
|
||||||
|
|
||||||
public class WhaleRouter extends BaseRouter {
|
public class WhaleRouter extends BaseRouter {
|
||||||
|
|
||||||
public void onEnterChildProcess() {
|
public void onEnterChildProcess() {
|
||||||
|
|
@ -27,7 +16,6 @@ public class WhaleRouter extends BaseRouter {
|
||||||
BaseRouter.useXposedApi = true;
|
BaseRouter.useXposedApi = true;
|
||||||
EdXpConfigGlobal.sConfig = new WhaleEdxpConfig();
|
EdXpConfigGlobal.sConfig = new WhaleEdxpConfig();
|
||||||
EdXpConfigGlobal.sHookProvider = new WhaleHookProvider();
|
EdXpConfigGlobal.sHookProvider = new WhaleHookProvider();
|
||||||
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
|
||||||
Zygote.allowFileAcrossFork("/system/lib/libwhale.edxp.so");
|
Zygote.allowFileAcrossFork("/system/lib/libwhale.edxp.so");
|
||||||
Zygote.allowFileAcrossFork("/system/lib64/libwhale.edxp.so");
|
Zygote.allowFileAcrossFork("/system/lib64/libwhale.edxp.so");
|
||||||
Zygote.allowFileAcrossFork("/system/lib/libart.so");
|
Zygote.allowFileAcrossFork("/system/lib/libart.so");
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ import com.elderdrivers.riru.edxp.yahfa.config.YahfaEdxpConfig;
|
||||||
import com.elderdrivers.riru.edxp.yahfa.config.YahfaHookProvider;
|
import com.elderdrivers.riru.edxp.yahfa.config.YahfaHookProvider;
|
||||||
import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge;
|
import com.elderdrivers.riru.edxp.yahfa.dexmaker.DynamicBridge;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
|
||||||
|
|
||||||
public class YahfaRouter extends BaseRouter {
|
public class YahfaRouter extends BaseRouter {
|
||||||
|
|
||||||
public void onEnterChildProcess() {
|
public void onEnterChildProcess() {
|
||||||
|
|
@ -17,7 +15,6 @@ public class YahfaRouter extends BaseRouter {
|
||||||
public void injectConfig() {
|
public void injectConfig() {
|
||||||
EdXpConfigGlobal.sConfig = new YahfaEdxpConfig();
|
EdXpConfigGlobal.sConfig = new YahfaEdxpConfig();
|
||||||
EdXpConfigGlobal.sHookProvider = new YahfaHookProvider();
|
EdXpConfigGlobal.sHookProvider = new YahfaHookProvider();
|
||||||
XposedBridge.log("using HookProvider: " + EdXpConfigGlobal.sHookProvider.getClass().getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,6 @@
|
||||||
|
package de.robv.android.xposed;
|
||||||
|
|
||||||
|
public interface IModuleContext {
|
||||||
|
|
||||||
|
String getApkPath();
|
||||||
|
}
|
||||||
|
|
@ -25,12 +25,21 @@ public interface IXposedHookInitPackageResources extends IXposedMod {
|
||||||
/** @hide */
|
/** @hide */
|
||||||
final class Wrapper extends XC_InitPackageResources {
|
final class Wrapper extends XC_InitPackageResources {
|
||||||
private final IXposedHookInitPackageResources instance;
|
private final IXposedHookInitPackageResources instance;
|
||||||
public Wrapper(IXposedHookInitPackageResources instance) {
|
private final String apkPath;
|
||||||
|
|
||||||
|
public Wrapper(IXposedHookInitPackageResources instance, String apkPath) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
this.apkPath = apkPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
|
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
|
||||||
instance.handleInitPackageResources(resparam);
|
instance.handleInitPackageResources(resparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApkPath() {
|
||||||
|
return apkPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,20 @@ public interface IXposedHookLoadPackage extends IXposedMod {
|
||||||
/** @hide */
|
/** @hide */
|
||||||
final class Wrapper extends XC_LoadPackage {
|
final class Wrapper extends XC_LoadPackage {
|
||||||
private final IXposedHookLoadPackage instance;
|
private final IXposedHookLoadPackage instance;
|
||||||
public Wrapper(IXposedHookLoadPackage instance) {
|
private final String apkPath;
|
||||||
|
|
||||||
|
public Wrapper(IXposedHookLoadPackage instance, String apkPath) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
this.apkPath = apkPath;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
|
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
|
||||||
instance.handleLoadPackage(lpparam);
|
instance.handleLoadPackage(lpparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApkPath() {
|
||||||
|
return apkPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,5 +62,10 @@ public interface IXposedHookZygoteInit extends IXposedMod {
|
||||||
// cause startupParam info is generated and saved along with instance here
|
// cause startupParam info is generated and saved along with instance here
|
||||||
instance.initZygote(this.startupParam);
|
instance.initZygote(this.startupParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApkPath() {
|
||||||
|
return startupParam.modulePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -446,6 +446,12 @@ public final class XposedBridge {
|
||||||
XCallback.callAll(new IXposedHookZygoteInit.StartupParam(sInitZygoteCallbacks));
|
XCallback.callAll(new IXposedHookZygoteInit.StartupParam(sInitZygoteCallbacks));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void clearAllCallbacks() {
|
||||||
|
clearLoadedPackages();
|
||||||
|
clearInitPackageResources();
|
||||||
|
clearInitZygotes();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intercept every call to the specified method and call a handler function instead.
|
* Intercept every call to the specified method and call a handler function instead.
|
||||||
* @param method The method to intercept
|
* @param method The method to intercept
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.os.ZygoteInit;
|
import com.android.internal.os.ZygoteInit;
|
||||||
|
|
@ -27,6 +28,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
@ -34,11 +36,17 @@ import java.util.zip.ZipFile;
|
||||||
import dalvik.system.DexFile;
|
import dalvik.system.DexFile;
|
||||||
import dalvik.system.PathClassLoader;
|
import dalvik.system.PathClassLoader;
|
||||||
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
||||||
|
import de.robv.android.xposed.callbacks.XC_InitZygote;
|
||||||
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
import de.robv.android.xposed.callbacks.XCallback;
|
import de.robv.android.xposed.callbacks.XCallback;
|
||||||
import de.robv.android.xposed.services.BaseService;
|
import de.robv.android.xposed.services.BaseService;
|
||||||
|
|
||||||
|
import static de.robv.android.xposed.XposedBridge.clearAllCallbacks;
|
||||||
import static de.robv.android.xposed.XposedBridge.hookAllConstructors;
|
import static de.robv.android.xposed.XposedBridge.hookAllConstructors;
|
||||||
import static de.robv.android.xposed.XposedBridge.hookAllMethods;
|
import static de.robv.android.xposed.XposedBridge.hookAllMethods;
|
||||||
|
import static de.robv.android.xposed.XposedBridge.sInitPackageResourcesCallbacks;
|
||||||
|
import static de.robv.android.xposed.XposedBridge.sInitZygoteCallbacks;
|
||||||
|
import static de.robv.android.xposed.XposedBridge.sLoadedPackageCallbacks;
|
||||||
import static de.robv.android.xposed.XposedHelpers.callMethod;
|
import static de.robv.android.xposed.XposedHelpers.callMethod;
|
||||||
import static de.robv.android.xposed.XposedHelpers.closeSilently;
|
import static de.robv.android.xposed.XposedHelpers.closeSilently;
|
||||||
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
|
||||||
|
|
@ -292,44 +300,104 @@ public final class XposedInit {
|
||||||
/**
|
/**
|
||||||
* Try to load all modules defined in <code>INSTALLER_DATA_BASE_DIR/conf/modules.list</code>
|
* Try to load all modules defined in <code>INSTALLER_DATA_BASE_DIR/conf/modules.list</code>
|
||||||
*/
|
*/
|
||||||
private static volatile AtomicBoolean modulesLoaded = new AtomicBoolean(false);
|
private static final AtomicBoolean modulesLoaded = new AtomicBoolean(false);
|
||||||
|
private static final Object moduleLoadLock = new Object();
|
||||||
|
// @GuardedBy("moduleLoadLock")
|
||||||
|
private static final ArraySet<String> loadedModules = new ArraySet<>();
|
||||||
|
// @GuardedBy("moduleLoadLock")
|
||||||
|
private static long lastModuleListModifiedTime = -1;
|
||||||
|
|
||||||
public static void loadModules(boolean isInZygote) throws IOException {
|
public static boolean loadModules(boolean isInZygote, boolean callInitZygote) throws IOException {
|
||||||
boolean hasLoaded = !modulesLoaded.compareAndSet(false, true);
|
boolean hasLoaded = !modulesLoaded.compareAndSet(false, true);
|
||||||
// dynamic module list mode doesn't apply to loading in zygote
|
// dynamic module list mode doesn't apply to loading in zygote
|
||||||
if (hasLoaded && (isInZygote || !EdXpConfigGlobal.getConfig().isDynamicModulesMode())) {
|
if (hasLoaded && (isInZygote || !EdXpConfigGlobal.getConfig().isDynamicModulesMode())) {
|
||||||
return;
|
return false;
|
||||||
}
|
|
||||||
// FIXME module list is cleared but never could be reload again when using dynamic-module-list under multi-user environment
|
|
||||||
XposedBridge.clearLoadedPackages();
|
|
||||||
final String filename = EdXpConfigGlobal.getConfig().getInstallerBaseDir() + "conf/modules.list";
|
|
||||||
BaseService service = SELinuxHelper.getAppDataFileService();
|
|
||||||
if (!service.checkFileExists(filename)) {
|
|
||||||
Log.e(TAG, "Cannot load any modules because " + filename + " was not found");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
synchronized (moduleLoadLock) {
|
||||||
|
final String filename = EdXpConfigGlobal.getConfig().getInstallerBaseDir() + "conf/modules.list";
|
||||||
|
BaseService service = SELinuxHelper.getAppDataFileService();
|
||||||
|
if (!service.checkFileExists(filename)) {
|
||||||
|
Log.e(TAG, "Cannot load any modules because " + filename + " was not found");
|
||||||
|
// FIXME module list is cleared but never could be reload again
|
||||||
|
// when using dynamic-module-list under multi-user environment
|
||||||
|
clearAllCallbacks();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ClassLoader topClassLoader = XposedBridge.BOOTCLASSLOADER;
|
long moduleListModifiedTime = service.getFileModificationTime(filename);
|
||||||
ClassLoader parent;
|
if (lastModuleListModifiedTime == moduleListModifiedTime) {
|
||||||
while ((parent = topClassLoader.getParent()) != null) {
|
// module list has not changed
|
||||||
topClassLoader = parent;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClassLoader topClassLoader = XposedBridge.BOOTCLASSLOADER;
|
||||||
|
ClassLoader parent;
|
||||||
|
while ((parent = topClassLoader.getParent()) != null) {
|
||||||
|
topClassLoader = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream stream = service.getFileInputStream(filename);
|
||||||
|
BufferedReader apks = new BufferedReader(new InputStreamReader(stream));
|
||||||
|
ArraySet<String> newLoadedApk = new ArraySet<>();
|
||||||
|
String apk;
|
||||||
|
while ((apk = apks.readLine()) != null) {
|
||||||
|
if (loadedModules.contains(apk)) {
|
||||||
|
newLoadedApk.add(apk);
|
||||||
|
} else {
|
||||||
|
boolean loadSuccess = loadModule(apk, topClassLoader, callInitZygote);
|
||||||
|
if (loadSuccess) {
|
||||||
|
newLoadedApk.add(apk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadedModules.clear();
|
||||||
|
loadedModules.addAll(newLoadedApk);
|
||||||
|
apks.close();
|
||||||
|
|
||||||
|
// refresh callback according to current loaded module list
|
||||||
|
pruneCallbacks(loadedModules);
|
||||||
|
|
||||||
|
lastModuleListModifiedTime = moduleListModifiedTime;
|
||||||
|
|
||||||
InputStream stream = service.getFileInputStream(filename);
|
|
||||||
BufferedReader apks = new BufferedReader(new InputStreamReader(stream));
|
|
||||||
String apk;
|
|
||||||
while ((apk = apks.readLine()) != null) {
|
|
||||||
loadModule(apk, topClassLoader);
|
|
||||||
}
|
}
|
||||||
apks.close();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove deactivated or outdated module callbacks
|
||||||
|
private static void pruneCallbacks(Set<String> loadedModules) {
|
||||||
|
synchronized (moduleLoadLock) {
|
||||||
|
Object[] loadedPkgSnapshot = sLoadedPackageCallbacks.getSnapshot();
|
||||||
|
Object[] initPkgResSnapshot = sInitPackageResourcesCallbacks.getSnapshot();
|
||||||
|
Object[] initZygoteSnapshot = sInitZygoteCallbacks.getSnapshot();
|
||||||
|
for (Object loadedPkg : loadedPkgSnapshot) {
|
||||||
|
if (loadedPkg instanceof IModuleContext) {
|
||||||
|
if (!loadedModules.contains(((IModuleContext) loadedPkg).getApkPath())) {
|
||||||
|
sLoadedPackageCallbacks.remove((XC_LoadPackage) loadedPkg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Object initPkgRes : initPkgResSnapshot) {
|
||||||
|
if (initPkgRes instanceof IModuleContext) {
|
||||||
|
if (!loadedModules.contains(((IModuleContext) initPkgRes).getApkPath())) {
|
||||||
|
sInitPackageResourcesCallbacks.remove((XC_InitPackageResources) initPkgRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Object initZygote : initZygoteSnapshot) {
|
||||||
|
if (initZygote instanceof IModuleContext) {
|
||||||
|
if (!loadedModules.contains(((IModuleContext) initZygote).getApkPath())) {
|
||||||
|
sInitZygoteCallbacks.remove((XC_InitZygote) initZygote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a module from an APK by calling the init(String) method for all classes defined
|
* Load a module from an APK by calling the init(String) method for all classes defined
|
||||||
* in <code>assets/xposed_init</code>.
|
* in <code>assets/xposed_init</code>.
|
||||||
*/
|
*/
|
||||||
private static void loadModule(String apk, ClassLoader topClassLoader) {
|
private static boolean loadModule(String apk, ClassLoader topClassLoader, boolean callInitZygote) {
|
||||||
Log.i(TAG, "Loading modules from " + apk);
|
Log.i(TAG, "Loading modules from " + apk);
|
||||||
|
|
||||||
// todo remove this legacy logic
|
// todo remove this legacy logic
|
||||||
|
|
@ -337,12 +405,12 @@ public final class XposedInit {
|
||||||
if (!TextUtils.isEmpty(apk) && !TextUtils.isEmpty(blackListModulePackageName)
|
if (!TextUtils.isEmpty(apk) && !TextUtils.isEmpty(blackListModulePackageName)
|
||||||
&& apk.contains(blackListModulePackageName)) {
|
&& apk.contains(blackListModulePackageName)) {
|
||||||
Log.i(TAG, "We are going to take over black list's job...");
|
Log.i(TAG, "We are going to take over black list's job...");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!new File(apk).exists()) {
|
if (!new File(apk).exists()) {
|
||||||
Log.e(TAG, " File does not exist");
|
Log.e(TAG, " File does not exist");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DexFile dexFile;
|
DexFile dexFile;
|
||||||
|
|
@ -350,13 +418,13 @@ public final class XposedInit {
|
||||||
dexFile = new DexFile(apk);
|
dexFile = new DexFile(apk);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, " Cannot load module", e);
|
Log.e(TAG, " Cannot load module", e);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dexFile.loadClass(INSTANT_RUN_CLASS, topClassLoader) != null) {
|
if (dexFile.loadClass(INSTANT_RUN_CLASS, topClassLoader) != null) {
|
||||||
Log.e(TAG, " Cannot load module, please disable \"Instant Run\" in Android Studio.");
|
Log.e(TAG, " Cannot load module, please disable \"Instant Run\" in Android Studio.");
|
||||||
closeSilently(dexFile);
|
closeSilently(dexFile);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dexFile.loadClass(XposedBridge.class.getName(), topClassLoader) != null) {
|
if (dexFile.loadClass(XposedBridge.class.getName(), topClassLoader) != null) {
|
||||||
|
|
@ -365,7 +433,7 @@ public final class XposedInit {
|
||||||
Log.e(TAG, " This may cause strange issues and must be fixed by the module developer.");
|
Log.e(TAG, " This may cause strange issues and must be fixed by the module developer.");
|
||||||
Log.e(TAG, " For details, see: http://api.xposed.info/using.html");
|
Log.e(TAG, " For details, see: http://api.xposed.info/using.html");
|
||||||
closeSilently(dexFile);
|
closeSilently(dexFile);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
closeSilently(dexFile);
|
closeSilently(dexFile);
|
||||||
|
|
@ -378,13 +446,13 @@ public final class XposedInit {
|
||||||
if (zipEntry == null) {
|
if (zipEntry == null) {
|
||||||
Log.e(TAG, " assets/xposed_init not found in the APK");
|
Log.e(TAG, " assets/xposed_init not found in the APK");
|
||||||
closeSilently(zipFile);
|
closeSilently(zipFile);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
is = zipFile.getInputStream(zipEntry);
|
is = zipFile.getInputStream(zipEntry);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, " Cannot read assets/xposed_init in the APK", e);
|
Log.e(TAG, " Cannot read assets/xposed_init in the APK", e);
|
||||||
closeSilently(zipFile);
|
closeSilently(zipFile);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassLoader mcl = new PathClassLoader(apk, XposedInit.class.getClassLoader());
|
ClassLoader mcl = new PathClassLoader(apk, XposedInit.class.getClassLoader());
|
||||||
|
|
@ -414,24 +482,21 @@ public final class XposedInit {
|
||||||
IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
|
IXposedHookZygoteInit.StartupParam param = new IXposedHookZygoteInit.StartupParam();
|
||||||
param.modulePath = apk;
|
param.modulePath = apk;
|
||||||
param.startsSystemServer = startsSystemServer;
|
param.startsSystemServer = startsSystemServer;
|
||||||
if (EdXpConfigGlobal.getConfig().isBlackWhiteListMode()
|
|
||||||
&& !EdXpConfigGlobal.getConfig().isDynamicModulesMode()) {
|
XposedBridge.hookInitZygote(new IXposedHookZygoteInit.Wrapper(
|
||||||
// postpone initZygote callbacks under black/white list mode
|
(IXposedHookZygoteInit) moduleInstance, param));
|
||||||
// if dynamic modules mode is on, callback directly cause we
|
if (callInitZygote) {
|
||||||
// are already in app process here
|
|
||||||
XposedBridge.hookInitZygote(new IXposedHookZygoteInit.Wrapper(
|
|
||||||
(IXposedHookZygoteInit) moduleInstance, param));
|
|
||||||
} else {
|
|
||||||
// FIXME under dynamic modules mode, initZygote is called twice
|
|
||||||
((IXposedHookZygoteInit) moduleInstance).initZygote(param);
|
((IXposedHookZygoteInit) moduleInstance).initZygote(param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moduleInstance instanceof IXposedHookLoadPackage)
|
if (moduleInstance instanceof IXposedHookLoadPackage)
|
||||||
XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper((IXposedHookLoadPackage) moduleInstance));
|
XposedBridge.hookLoadPackage(new IXposedHookLoadPackage.Wrapper(
|
||||||
|
(IXposedHookLoadPackage) moduleInstance, apk));
|
||||||
|
|
||||||
if (moduleInstance instanceof IXposedHookInitPackageResources)
|
if (moduleInstance instanceof IXposedHookInitPackageResources)
|
||||||
XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper((IXposedHookInitPackageResources) moduleInstance));
|
XposedBridge.hookInitPackageResources(new IXposedHookInitPackageResources.Wrapper(
|
||||||
|
(IXposedHookInitPackageResources) moduleInstance, apk));
|
||||||
} else {
|
} else {
|
||||||
if (moduleInstance instanceof IXposedHookCmdInit) {
|
if (moduleInstance instanceof IXposedHookCmdInit) {
|
||||||
IXposedHookCmdInit.StartupParam param = new IXposedHookCmdInit.StartupParam();
|
IXposedHookCmdInit.StartupParam param = new IXposedHookCmdInit.StartupParam();
|
||||||
|
|
@ -442,10 +507,13 @@ public final class XposedInit {
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Log.e(TAG, " Failed to load class " + moduleClassName, t);
|
Log.e(TAG, " Failed to load class " + moduleClassName, t);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, " Failed to load module from " + apk, e);
|
Log.e(TAG, " Failed to load module from " + apk, e);
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
closeSilently(is);
|
closeSilently(is);
|
||||||
closeSilently(zipFile);
|
closeSilently(zipFile);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.elderdrivers.riru.edxp.config.EdXpConfigGlobal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import de.robv.android.xposed.IModuleContext;
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
||||||
|
|
||||||
|
|
@ -15,7 +16,7 @@ import de.robv.android.xposed.XposedBridge.CopyOnWriteSortedSet;
|
||||||
* This class only keeps a priority for ordering multiple callbacks.
|
* This class only keeps a priority for ordering multiple callbacks.
|
||||||
* The actual (abstract) callback methods are added by subclasses.
|
* The actual (abstract) callback methods are added by subclasses.
|
||||||
*/
|
*/
|
||||||
public abstract class XCallback implements Comparable<XCallback> {
|
public abstract class XCallback implements Comparable<XCallback>, IModuleContext {
|
||||||
/**
|
/**
|
||||||
* Callback priority, higher number means earlier execution.
|
* Callback priority, higher number means earlier execution.
|
||||||
*
|
*
|
||||||
|
|
@ -121,6 +122,11 @@ public abstract class XCallback implements Comparable<XCallback> {
|
||||||
/** @hide */
|
/** @hide */
|
||||||
protected void call(Param param) throws Throwable {}
|
protected void call(Param param) throws Throwable {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getApkPath() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(XCallback other) {
|
public int compareTo(XCallback other) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue