From db64adde94d26a8d8f1197170d1049a1bd2541b4 Mon Sep 17 00:00:00 2001 From: NkBe Date: Fri, 6 Mar 2026 22:10:05 +0800 Subject: [PATCH] Use reflection for DexFile and XResources Avoid direct references to dalvik.system.DexFile and android.content.res.XResources by using reflection and guarded calls. LSPApplication now constructs and injects the provider dex via reflective DexFile/DexPathList$Element creation inside a try/catch to handle deprecation/compat issues on newer Android versions and log failures. LSPLoader replaces the direct XResources.setPackageNameForResDir call with a reflective invocation (with warning logs) to prevent class resolution failures under strict boot classloader namespace delegation. Also added logging and minor import/constant adjustments to improve robustness and diagnostics. Co-Authored-By: MrZhongzq <108169409+mrzhongzq@users.noreply.github.com> Co-Authored-By: Claude <81847+claude@users.noreply.github.com> --- .../lsposed/npatch/loader/LSPApplication.java | 30 +++++++++++-------- .../org/lsposed/npatch/loader/LSPLoader.java | 24 +++++++++++++-- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java b/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java index 0eee448..3cfbc11 100644 --- a/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java +++ b/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java @@ -45,7 +45,6 @@ import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -import dalvik.system.DexFile; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import hidden.HiddenApiBridge; @@ -201,19 +200,24 @@ public class LSPApplication { appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo); if (config.injectProvider && providerPath != null) { - ClassLoader loader = appLoadedApk.getClassLoader(); - Object dexPathList = XposedHelpers.getObjectField(loader, "pathList"); - Object dexElements = XposedHelpers.getObjectField(dexPathList, "dexElements"); - int length = Array.getLength(dexElements); - Object newElements = Array.newInstance(dexElements.getClass().getComponentType(), length + 1); - System.arraycopy(dexElements, 0, newElements, 0, length); + try { + ClassLoader loader = appLoadedApk.getClassLoader(); + Object dexPathList = XposedHelpers.getObjectField(loader, "pathList"); + Object dexElements = XposedHelpers.getObjectField(dexPathList, "dexElements"); + int length = Array.getLength(dexElements); + Object newElements = Array.newInstance(dexElements.getClass().getComponentType(), length + 1); + System.arraycopy(dexElements, 0, newElements, 0, length); - DexFile dexFile = new DexFile(providerPath.toString()); - Object element = XposedHelpers.newInstance(XposedHelpers.findClass("dalvik.system.DexPathList$Element", loader), new Class[]{ - DexFile.class - }, dexFile); - Array.set(newElements, length, element); - XposedHelpers.setObjectField(dexPathList, "dexElements", newElements); + // Use reflection for DexFile to handle deprecation on Android 14+ + Class dexFileClass = Class.forName("dalvik.system.DexFile"); + Object dexFile = dexFileClass.getConstructor(String.class).newInstance(providerPath.toString()); + Class elementClass = Class.forName("dalvik.system.DexPathList$Element"); + Object element = elementClass.getConstructor(dexFileClass).newInstance(dexFile); + Array.set(newElements, length, element); + XposedHelpers.setObjectField(dexPathList, "dexElements", newElements); + } catch (Throwable e) { + Log.e(TAG, "Failed to inject provider dex: " + e.getMessage(), e); + } } XposedHelpers.setObjectField(mBoundApplication, "info", appLoadedApk); diff --git a/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPLoader.java b/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPLoader.java index 8214b6e..88ba2ac 100644 --- a/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPLoader.java +++ b/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPLoader.java @@ -2,16 +2,20 @@ package org.lsposed.npatch.loader; import android.app.ActivityThread; import android.app.LoadedApk; -import android.content.res.XResources; +import android.util.Log; + +import java.lang.reflect.Method; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedInit; import de.robv.android.xposed.callbacks.XC_LoadPackage; public class LSPLoader { + private static final String TAG = "NPatch"; + public static void initModules(LoadedApk loadedApk) { XposedInit.loadedPackagesInProcess.add(loadedApk.getPackageName()); - XResources.setPackageNameForResDir(loadedApk.getPackageName(), loadedApk.getResDir()); + setPackageNameForResDir(loadedApk.getPackageName(), loadedApk.getResDir()); XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam( XposedBridge.sLoadedPackageCallbacks); lpparam.packageName = loadedApk.getPackageName(); @@ -21,4 +25,18 @@ public class LSPLoader { lpparam.isFirstApplication = true; XC_LoadPackage.callAll(lpparam); } -} + + private static void setPackageNameForResDir(String packageName, String resDir) { + try { + // Use reflection to avoid direct type reference to android.content.res.XResources + // which fails class resolution on Android 16+ due to strict boot classloader + // namespace delegation for the android.content.res.* package. + ClassLoader cl = LSPLoader.class.getClassLoader(); + Class xResourcesClass = cl.loadClass("android.content.res.XResources"); + Method setMethod = xResourcesClass.getMethod("setPackageNameForResDir", String.class, String.class); + setMethod.invoke(null, packageName, resDir); + } catch (Throwable e) { + Log.w(TAG, "XResources.setPackageNameForResDir not available, skipping resource dir setup", e); + } + } +} \ No newline at end of file