diff --git a/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java b/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java index 9189c020..3b61f6e1 100644 --- a/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java +++ b/core/src/main/java/de/robv/android/xposed/XC_MethodHook.java @@ -75,10 +75,10 @@ public abstract class XC_MethodHook extends XCallback { * @param param Information about the method call. * @throws Throwable Everything the callback throws is caught and logged. */ - protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { } - public void callBeforeHookedMethod(MethodHookParam param) throws Throwable { + public void callBeforeHookedMethod(MethodHookParam param) throws Throwable { beforeHookedMethod(param); } @@ -93,10 +93,10 @@ public abstract class XC_MethodHook extends XCallback { * @param param Information about the method call. * @throws Throwable Everything the callback throws is caught and logged. */ - protected void afterHookedMethod(MethodHookParam param) throws Throwable { + protected void afterHookedMethod(MethodHookParam param) throws Throwable { } - public void callAfterHookedMethod(MethodHookParam param) throws Throwable { + public void callAfterHookedMethod(MethodHookParam param) throws Throwable { afterHookedMethod(param); } diff --git a/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java b/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java index e6207a38..c88b6389 100644 --- a/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java +++ b/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java @@ -50,6 +50,7 @@ import java.security.MessageDigest; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import de.robv.android.xposed.services.FileResult; @@ -160,29 +161,27 @@ public final class XSharedPreferences implements SharedPreferences { */ public XSharedPreferences(String packageName, String prefFileName) { boolean newModule = false; - Set modules = XposedInit.getLoadedModules(); - for (String m : modules) { - if (m.contains("/" + packageName + "-")) { - boolean isModule = false; - int xposedminversion = -1; - boolean xposedsharedprefs = false; - try { - Map metaData = MetaDataReader.getMetaData(new File(m)); - isModule = metaData.containsKey("xposedminversion"); - if (isModule) { - Object minVersionRaw = metaData.get("xposedminversion"); - if (minVersionRaw instanceof Integer) { - xposedminversion = (Integer) minVersionRaw; - } else if (minVersionRaw instanceof String) { - xposedminversion = MetaDataReader.extractIntPart((String) minVersionRaw); - } - xposedsharedprefs = metaData.containsKey("xposedsharedprefs"); + var m = XposedInit.getLoadedModules().getOrDefault(packageName, Optional.empty()); + if (m.isPresent()) { + boolean isModule = false; + int xposedminversion = -1; + boolean xposedsharedprefs = false; + try { + Map metaData = MetaDataReader.getMetaData(new File(m.get())); + isModule = metaData.containsKey("xposedminversion"); + if (isModule) { + Object minVersionRaw = metaData.get("xposedminversion"); + if (minVersionRaw instanceof Integer) { + xposedminversion = (Integer) minVersionRaw; + } else if (minVersionRaw instanceof String) { + xposedminversion = MetaDataReader.extractIntPart((String) minVersionRaw); } - } catch (NumberFormatException | IOException e) { - Log.w(TAG, "Apk parser fails: " + e); + xposedsharedprefs = metaData.containsKey("xposedsharedprefs"); } - newModule = isModule && (xposedminversion > 92 || xposedsharedprefs); + } catch (NumberFormatException | IOException e) { + Log.w(TAG, "Apk parser fails: " + e); } + newModule = isModule && (xposedminversion > 92 || xposedsharedprefs); } if (newModule) { mFile = new File(serviceClient.getPrefsPath(packageName), prefFileName + ".xml"); 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 595449d6..629bca4a 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedInit.java +++ b/core/src/main/java/de/robv/android/xposed/XposedInit.java @@ -29,6 +29,7 @@ import static de.robv.android.xposed.XposedHelpers.getObjectField; import static de.robv.android.xposed.XposedHelpers.getParameterIndexByType; import static de.robv.android.xposed.XposedHelpers.setStaticObjectField; +import android.app.ActivityThread; import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.content.res.ResourcesImpl; @@ -50,9 +51,10 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; import de.robv.android.xposed.callbacks.XC_InitPackageResources; @@ -82,7 +84,7 @@ public final class XposedInit { findAndHookMethod("android.app.ApplicationPackageManager", null, "getResourcesForApplication", ApplicationInfo.class, new XC_MethodHook() { @Override - protected void beforeHookedMethod(MethodHookParam param) { + protected void beforeHookedMethod(MethodHookParam param) { ApplicationInfo app = (ApplicationInfo) param.args[0]; XResources.setPackageNameForResDir(app.packageName, app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir); @@ -117,7 +119,7 @@ public final class XposedInit { final Class classActivityRes = XposedHelpers.findClassIfExists("android.app.ResourcesManager$ActivityResource", classGTLR.getClassLoader()); var hooker = new XC_MethodHook() { @Override - protected void afterHookedMethod(MethodHookParam param) { + protected void afterHookedMethod(MethodHookParam param) { // At least on OnePlus 5, the method has an additional parameter compared to AOSP. Object activityToken = null; try { @@ -162,7 +164,7 @@ public final class XposedInit { findAndHookMethod(TypedArray.class, "obtain", Resources.class, int.class, new XC_MethodHook() { @Override - protected void afterHookedMethod(MethodHookParam param) throws Throwable { + protected void afterHookedMethod(MethodHookParam param) throws Throwable { if (param.getResult() instanceof XResources.XTypedArray) { return; } @@ -189,7 +191,7 @@ public final class XposedInit { XResources.init(latestResKey); } - private static XResources cloneToXResources(XC_MethodHook.MethodHookParam param, String resDir) { + private static XResources cloneToXResources(XC_MethodHook.MethodHookParam param, String resDir) { Object result = param.getResult(); if (result == null || result instanceof XResources) { return null; @@ -213,21 +215,31 @@ public final class XposedInit { return newRes; } - private static final Set loadedModules = new CopyOnWriteArraySet<>(); + // only legacy modules have non-empty value + private static final Map> loadedModules = new ConcurrentHashMap<>(); - public static Set getLoadedModules() { + public static Map> getLoadedModules() { return loadedModules; } - public static void loadModules() { + public static void loadLegacyModules() { var moduleList = serviceClient.getLegacyModulesList(); moduleList.forEach(module -> { var apk = module.apkPath; var name = module.packageName; var file = module.file; - loadedModules.add(apk); // temporarily add it for XSharedPreference + loadedModules.put(name, Optional.of(apk)); // temporarily add it for XSharedPreference if (!loadModule(name, apk, file)) { - loadedModules.remove(apk); + loadedModules.remove(name); + } + }); + } + + public static void loadModules(ActivityThread at) { + serviceClient.getModulesList().forEach(module -> { + loadedModules.put(module.packageName, Optional.empty()); + if (!LSPosedContext.loadModule(at, module)) { + loadedModules.remove(module.packageName); } }); } 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 ca22f96d..824b1b3c 100644 --- a/core/src/main/java/org/lsposed/lspd/core/Startup.java +++ b/core/src/main/java/org/lsposed/lspd/core/Startup.java @@ -68,7 +68,7 @@ public class Startup { // Initialize the Xposed framework try { startBootstrapHook(XposedInit.startsSystemServer); - XposedInit.loadModules(); + XposedInit.loadLegacyModules(); } catch (Throwable t) { Utils.logE("error during Xposed initialization", t); } diff --git a/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java index 761f529f..e8115ab8 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/AttachHooker.java @@ -6,16 +6,14 @@ import android.app.ActivityThread; import org.lsposed.lspd.impl.LSPosedContext; +import java.util.Optional; + import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedInit; public class AttachHooker extends XC_MethodHook { @Override - protected void afterHookedMethod(MethodHookParam param) throws Throwable { - serviceClient.getModulesList().forEach(module -> { - if (LSPosedContext.loadModule((ActivityThread) param.thisObject, module)) { - XposedInit.getLoadedModules().add(module.packageName); - } - }); + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + XposedInit.loadModules((ActivityThread) param.thisObject); } } diff --git a/core/src/main/java/org/lsposed/lspd/hooker/CrashDumpHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/CrashDumpHooker.java index c5ad4297..69729520 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/CrashDumpHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/CrashDumpHooker.java @@ -7,7 +7,7 @@ import de.robv.android.xposed.XposedBridge; public class CrashDumpHooker extends XC_MethodHook { @Override - protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + protected void beforeHookedMethod(MethodHookParam param) { try { var e = (Throwable) param.args[0]; XposedBridge.log("Crash unexpectedly: " + Log.getStackTraceString(e)); diff --git a/core/src/main/java/org/lsposed/lspd/hooker/HandleSystemServerProcessHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/HandleSystemServerProcessHooker.java index 2f37dd82..9ef9cda6 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/HandleSystemServerProcessHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/HandleSystemServerProcessHooker.java @@ -33,7 +33,7 @@ public class HandleSystemServerProcessHooker extends XC_MethodHook { public static volatile ClassLoader systemServerCL; @Override - protected void afterHookedMethod(MethodHookParam param) { + protected void afterHookedMethod(MethodHookParam param) { Hookers.logD("ZygoteInit#handleSystemServerProcess() starts"); try { // get system_server classLoader 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 1cdbcc4a..7355a79f 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkCtorHooker.java @@ -20,12 +20,15 @@ package org.lsposed.lspd.hooker; +import android.app.ActivityThread; import android.app.LoadedApk; import android.content.res.XResources; import android.util.Log; import org.lsposed.lspd.util.Hookers; +import java.util.Optional; + import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedInit; @@ -34,13 +37,14 @@ import de.robv.android.xposed.XposedInit; public class LoadedApkCtorHooker extends XC_MethodHook { @Override - protected void afterHookedMethod(MethodHookParam param) { + protected void afterHookedMethod(MethodHookParam param) { Hookers.logD("LoadedApk# starts"); try { LoadedApk loadedApk = (LoadedApk) param.thisObject; String packageName = loadedApk.getPackageName(); - if (XposedInit.getLoadedModules().contains(packageName)) { + boolean isFirstPackage = packageName != null && ActivityThread.currentProcessName() != null && packageName.equals(ActivityThread.currentPackageName()); + if (!isFirstPackage && !XposedInit.getLoadedModules().getOrDefault(packageName, Optional.of("")).isPresent()) { return; } Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir"); @@ -77,7 +81,7 @@ public class LoadedApkCtorHooker extends XC_MethodHook { return; } - new LoadedApkGetCLHooker(loadedApk); + new LoadedApkGetCLHooker(loadedApk, isFirstPackage); } catch (Throwable t) { Hookers.logE("error when hooking LoadedApk.", t); } diff --git a/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkGetCLHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkGetCLHooker.java index 6e46905c..a9f5b311 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkGetCLHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/LoadedApkGetCLHooker.java @@ -40,11 +40,13 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.util.Map; +import java.util.Optional; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; +import de.robv.android.xposed.XposedInit; import de.robv.android.xposed.callbacks.XC_LoadPackage; import io.github.libxposed.api.XposedModuleInterface; @@ -67,13 +69,16 @@ public class LoadedApkGetCLHooker extends XC_MethodHook { private final LoadedApk loadedApk; private final Unhook unhook; - public LoadedApkGetCLHooker(LoadedApk loadedApk) { + private final boolean isFirstPackage; + + public LoadedApkGetCLHooker(LoadedApk loadedApk, boolean isFirstPackage) { this.loadedApk = loadedApk; + this.isFirstPackage = isFirstPackage; unhook = XposedHelpers.findAndHookMethod(LoadedApk.class, "getClassLoader", this); } @Override - protected void afterHookedMethod(MethodHookParam param) { + protected void afterHookedMethod(MethodHookParam param) { LoadedApk loadedApk = (LoadedApk) param.thisObject; if (loadedApk != this.loadedApk) { @@ -83,15 +88,8 @@ public class LoadedApkGetCLHooker extends XC_MethodHook { try { Hookers.logD("LoadedApk#getClassLoader starts"); - String packageName = ActivityThread.currentPackageName(); - String processName = ActivityThread.currentProcessName(); - boolean isFirstPackage = packageName != null && processName != null && packageName.equals(loadedApk.getPackageName()); - if (!isFirstPackage) { - packageName = loadedApk.getPackageName(); - processName = AndroidAppHelper.currentProcessName(); - } else if (packageName.equals("android")) { - packageName = "system"; - } + final String processName = AndroidAppHelper.currentProcessName(); + final String packageName = isFirstPackage && "android".equals(ActivityThread.currentPackageName()) ? "system" : loadedApk.getPackageName(); Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir"); ClassLoader classLoader = (ClassLoader) param.getResult(); @@ -109,7 +107,9 @@ public class LoadedApkGetCLHooker extends XC_MethodHook { lpparam.appInfo = loadedApk.getApplicationInfo(); lpparam.isFirstApplication = isFirstPackage; - hookNewXSP(lpparam); + if (isFirstPackage && XposedInit.getLoadedModules().getOrDefault(packageName, Optional.empty()).isPresent()) { + hookNewXSP(lpparam); + } Hookers.logD("Call handleLoadedPackage: packageName=" + lpparam.packageName + " processName=" + lpparam.processName + " isFirstPackage=" + isFirstPackage + " classLoader=" + lpparam.classLoader + " appInfo=" + lpparam.appInfo); XC_LoadPackage.callAll(lpparam); diff --git a/core/src/main/java/org/lsposed/lspd/hooker/OpenDexFileHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/OpenDexFileHooker.java index 350b0cff..501e7063 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/OpenDexFileHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/OpenDexFileHooker.java @@ -9,7 +9,7 @@ import de.robv.android.xposed.XposedHelpers; public class OpenDexFileHooker extends XC_MethodHook { @Override - protected void afterHookedMethod(MethodHookParam param) throws Throwable { + protected void afterHookedMethod(MethodHookParam param) throws Throwable { ClassLoader classLoader = null; for (var arg : param.args) { if (arg instanceof ClassLoader) { diff --git a/core/src/main/java/org/lsposed/lspd/hooker/StartBootstrapServicesHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/StartBootstrapServicesHooker.java index ef987897..2ee2eba8 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/StartBootstrapServicesHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/StartBootstrapServicesHooker.java @@ -36,7 +36,7 @@ import io.github.libxposed.api.XposedModuleInterface; public class StartBootstrapServicesHooker extends XC_MethodHook { @Override - protected void beforeHookedMethod(MethodHookParam param) { + protected void beforeHookedMethod(MethodHookParam param) { logD("SystemServer#startBootstrapServices() starts"); try { diff --git a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java index a369fac2..0cceb3a8 100644 --- a/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/daemon/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -98,7 +98,7 @@ public class ConfigManager { private boolean verboseLog = true; private boolean dexObfuscate = false; private boolean enableStatusNotification = true; - private String miscPath = null; + private Path miscPath = null; private int managerUid = -1; @@ -280,16 +280,15 @@ public class ConfigManager { // Don't migrate to ConfigFileManager, as XSharedPreferences will be restored soon String string = (String) config.get("misc_path"); if (string == null) { - miscPath = "/data/misc/" + UUID.randomUUID().toString(); - updateModulePrefs("lspd", 0, "config", "misc_path", miscPath); + miscPath = Paths.get("/data", "misc", UUID.randomUUID().toString()); + updateModulePrefs("lspd", 0, "config", "misc_path", miscPath.toString()); } else { - miscPath = string; + miscPath = Paths.get(string); } try { - Path prefs = Paths.get(miscPath); var perms = PosixFilePermissions.fromString("rwx--x--x"); - Files.createDirectories(prefs, PosixFilePermissions.asFileAttribute(perms)); - walkFileTree(prefs, f -> SELinux.setFileContext(f.toString(), "u:object_r:magisk_file:s0")); + Files.createDirectories(miscPath, PosixFilePermissions.asFileAttribute(perms)); + walkFileTree(miscPath, f -> SELinux.setFileContext(f.toString(), "u:object_r:magisk_file:s0")); } catch (IOException e) { Log.e(TAG, Log.getStackTraceString(e)); } @@ -1083,9 +1082,26 @@ public class ConfigManager { return managerUid != -1; } - public String getPrefsPath(String fileName, int uid) { + public String getPrefsPath(String packageName, int uid) { int userId = uid / PER_USER_RANGE; - return miscPath + "/prefs" + (userId == 0 ? "" : String.valueOf(userId)) + "/" + fileName; + var path = miscPath.resolve("prefs" + (userId == 0 ? "" : String.valueOf(userId))).resolve(packageName); + var module = cachedModule.getOrDefault(packageName, null); + if (module != null && module.appId == uid % PER_USER_RANGE) { + try { + var perms = PosixFilePermissions.fromString("rwx--x--x"); + Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms)); + walkFileTree(path, p -> { + try { + Os.chown(p.toString(), uid, uid); + } catch (ErrnoException e) { + Log.e(TAG, Log.getStackTraceString(e)); + } + }); + } catch (IOException e) { + Log.e(TAG, Log.getStackTraceString(e)); + } + } + return path.toString(); } // this is slow, avoid using it