diff --git a/core/src/main/java/de/robv/android/xposed/XposedInit.java b/core/src/main/java/de/robv/android/xposed/XposedInit.java
index a1aa6ee2..71bc65af 100644
--- a/core/src/main/java/de/robv/android/xposed/XposedInit.java
+++ b/core/src/main/java/de/robv/android/xposed/XposedInit.java
@@ -221,7 +221,7 @@ public final class XposedInit {
}
public static void loadModules() {
- var moduleList = serviceClient.getModulesList();
+ var moduleList = serviceClient.getLegacyModulesList();
moduleList.forEach(module -> {
var apk = module.apkPath;
var name = module.packageName;
@@ -286,7 +286,7 @@ public final class XposedInit {
* in assets/xposed_init.
*/
private static boolean loadModule(String name, String apk, PreLoadedApk file) {
- Log.i(TAG, "Loading module " + name + " from " + apk);
+ Log.i(TAG, "Loading legacy module " + name + " from " + apk);
var sb = new StringBuilder();
var abis = Process.is64Bit() ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS;
diff --git a/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java b/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java
index 8afac0c8..616b8b69 100644
--- a/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java
+++ b/core/src/main/java/org/lsposed/lspd/core/ApplicationServiceClient.java
@@ -66,6 +66,15 @@ public class ApplicationServiceClient implements ILSPApplicationService, IBinder
return null;
}
+ @Override
+ public List getLegacyModulesList() {
+ try {
+ return service.getLegacyModulesList();
+ } catch (RemoteException | NullPointerException ignored) {
+ }
+ return Collections.emptyList();
+ }
+
@Override
public List getModulesList() {
try {
diff --git a/core/src/main/java/org/lsposed/lspd/core/Startup.java b/core/src/main/java/org/lsposed/lspd/core/Startup.java
index 4ceb697e..21bea1f2 100644
--- a/core/src/main/java/org/lsposed/lspd/core/Startup.java
+++ b/core/src/main/java/org/lsposed/lspd/core/Startup.java
@@ -28,6 +28,7 @@ import android.content.res.CompatibilityInfo;
import com.android.internal.os.ZygoteInit;
import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter;
+import org.lsposed.lspd.hooker.AttachHooker;
import org.lsposed.lspd.hooker.CrashDumpHooker;
import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker;
import org.lsposed.lspd.hooker.LoadedApkCtorHooker;
@@ -59,6 +60,8 @@ public class Startup {
ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,
ClassLoader.class, boolean.class, boolean.class, boolean.class,
new LoadedApkCtorHooker());
+ XposedHelpers.findAndHookMethod(ActivityThread.class, "attach", boolean.class,
+ long.class, new AttachHooker());
}
public static void bootstrapXposed() {
diff --git a/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java
new file mode 100644
index 00000000..0a3fb3d4
--- /dev/null
+++ b/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java
@@ -0,0 +1,72 @@
+package org.lsposed.lspd.hooker;
+
+import static org.lsposed.lspd.core.ApplicationServiceClient.serviceClient;
+
+import android.app.ActivityThread;
+import android.app.LoadedApk;
+import android.content.Context;
+import android.content.ContextParams;
+import android.os.Build;
+import android.os.Process;
+
+import org.lsposed.lspd.util.Hookers;
+import org.lsposed.lspd.util.LspModuleClassLoader;
+
+import java.io.File;
+
+import de.robv.android.xposed.XC_MethodHook;
+import de.robv.android.xposed.XposedHelpers;
+import de.robv.android.xposed.XposedInit;
+
+public class AttachHooker extends XC_MethodHook {
+ @Override
+ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
+ var at = (ActivityThread) param.thisObject;
+ var moduleList = serviceClient.getModulesList();
+ moduleList.forEach(module -> {
+ try {
+ XposedInit.getLoadedModules().add(module.packageName);
+ var loadedApk = at.getPackageInfoNoCheck(module.applicationInfo, null);
+ var sb = new StringBuilder();
+ var abis = Process.is64Bit() ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS;
+ for (String abi : abis) {
+ sb.append(module.apkPath).append("!/lib/").append(abi).append(File.pathSeparator);
+ }
+ var librarySearchPath = sb.toString();
+
+ var initLoader = XposedInit.class.getClassLoader();
+ var mcl = LspModuleClassLoader.loadApk(module.apkPath, module.file.preLoadedDexes, librarySearchPath, initLoader);
+ XposedHelpers.setObjectField(loadedApk, "mClassLoader", mcl);
+ var c = Class.forName("android.app.ContextImpl");
+ var ctor = c.getDeclaredConstructors()[0];
+ ctor.setAccessible(true);
+ var args = new Object[ctor.getParameterTypes().length];
+ for (int i = 0; i < ctor.getParameterTypes().length; ++i) {
+ if (ctor.getParameterTypes()[i] == LoadedApk.class) {
+ args[i] = loadedApk;
+ continue;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (ctor.getParameterTypes()[i] == ContextParams.class) {
+ args[i] = new ContextParams.Builder().build();
+ continue;
+ }
+ }
+ if (ctor.getParameterTypes()[i] == ActivityThread.class) {
+ args[i] = at;
+ continue;
+ }
+ if (ctor.getParameterTypes()[i] == int.class) {
+ args[i] = 0;
+ continue;
+ }
+ args[i] = null;
+ }
+ var ctx = (Context) ctor.newInstance(args);
+ Hookers.logD("Loaded module " + module.packageName + ": " + ctx);
+ } catch (Throwable e) {
+ Hookers.logE("Loading module " + module.packageName, e);
+ }
+ });
+ }
+}
diff --git a/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java
index e8b98910..1cdbcc4a 100644
--- a/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java
+++ b/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java
@@ -40,6 +40,9 @@ public class LoadedApkCtorHooker extends XC_MethodHook {
try {
LoadedApk loadedApk = (LoadedApk) param.thisObject;
String packageName = loadedApk.getPackageName();
+ if (XposedInit.getLoadedModules().contains(packageName)) {
+ return;
+ }
Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir");
Hookers.logD("LoadedApk# ends: " + mAppDir);
diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java
index 74ddf83c..eb31126d 100644
--- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java
+++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java
@@ -368,9 +368,11 @@ public class ConfigFileManager {
// TODO: we can store more info like api version, module description, etc. in META-INF
readName(apkFile, "META-INF/xposed/xposed_init", moduleClassNames);
if (moduleClassNames.isEmpty()) {
+ file.usingContext = false;
readName(apkFile, "assets/xposed_init", moduleClassNames);
readName(apkFile, "assets/native_init", moduleLibraryNames);
} else {
+ file.usingContext = true;
readName(apkFile, "META-INF/xposed/native_init", moduleLibraryNames);
}
} catch (IOException e) {
diff --git a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java
index dee77012..a787e0e8 100644
--- a/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java
+++ b/daemon/src/main/java/org/lsposed/lspd/service/LSPApplicationService.java
@@ -37,6 +37,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
public class LSPApplicationService extends ILSPApplicationService.Stub {
final static int DEX_TRANSACTION_CODE = 1310096052;
@@ -115,8 +116,7 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
}
}
- @Override
- public List getModulesList() throws RemoteException {
+ private List getAllModulesList() throws RemoteException {
var processInfo = ensureRegistered();
if (processInfo.uid == 1000 && processInfo.processName.equals("android")) {
return ConfigManager.getInstance().getModulesForSystemServer();
@@ -126,6 +126,16 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
return ConfigManager.getInstance().getModulesForProcess(processInfo.processName, processInfo.uid);
}
+ @Override
+ public List getLegacyModulesList() throws RemoteException {
+ return getAllModulesList().stream().filter(m -> !m.file.usingContext).collect(Collectors.toList());
+ }
+
+ @Override
+ public List getModulesList() throws RemoteException {
+ return getAllModulesList().stream().filter(m -> m.file.usingContext).collect(Collectors.toList());
+ }
+
@Override
public String getPrefsPath(String packageName) throws RemoteException {
ensureRegistered();
diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl
index 643e9d67..a92cf53b 100644
--- a/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl
+++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/models/PreLoadedApk.aidl
@@ -4,4 +4,5 @@ parcelable PreLoadedApk {
List preLoadedDexes;
List moduleClassNames;
List moduleLibraryNames;
+ boolean usingContext;
}
diff --git a/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl
index 430f3acc..8ff6199c 100644
--- a/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl
+++ b/services/daemon-service/src/main/aidl/org/lsposed/lspd/service/ILSPApplicationService.aidl
@@ -5,6 +5,8 @@ import org.lsposed.lspd.models.Module;
interface ILSPApplicationService {
IBinder requestModuleBinder(String name);
+ List getLegacyModulesList();
+
List getModulesList();
String getPrefsPath(String packageName);