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>
This commit is contained in:
NkBe 2026-03-06 22:10:05 +08:00
parent 43ca1640ab
commit db64adde94
No known key found for this signature in database
GPG Key ID: 9FACEE0DB6DF678E
2 changed files with 38 additions and 16 deletions

View File

@ -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);

View File

@ -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);
}
}
}