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:
parent
43ca1640ab
commit
db64adde94
|
|
@ -45,7 +45,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import dalvik.system.DexFile;
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import hidden.HiddenApiBridge;
|
import hidden.HiddenApiBridge;
|
||||||
|
|
@ -201,6 +200,7 @@ public class LSPApplication {
|
||||||
appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
|
appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);
|
||||||
|
|
||||||
if (config.injectProvider && providerPath != null) {
|
if (config.injectProvider && providerPath != null) {
|
||||||
|
try {
|
||||||
ClassLoader loader = appLoadedApk.getClassLoader();
|
ClassLoader loader = appLoadedApk.getClassLoader();
|
||||||
Object dexPathList = XposedHelpers.getObjectField(loader, "pathList");
|
Object dexPathList = XposedHelpers.getObjectField(loader, "pathList");
|
||||||
Object dexElements = XposedHelpers.getObjectField(dexPathList, "dexElements");
|
Object dexElements = XposedHelpers.getObjectField(dexPathList, "dexElements");
|
||||||
|
|
@ -208,12 +208,16 @@ public class LSPApplication {
|
||||||
Object newElements = Array.newInstance(dexElements.getClass().getComponentType(), length + 1);
|
Object newElements = Array.newInstance(dexElements.getClass().getComponentType(), length + 1);
|
||||||
System.arraycopy(dexElements, 0, newElements, 0, length);
|
System.arraycopy(dexElements, 0, newElements, 0, length);
|
||||||
|
|
||||||
DexFile dexFile = new DexFile(providerPath.toString());
|
// Use reflection for DexFile to handle deprecation on Android 14+
|
||||||
Object element = XposedHelpers.newInstance(XposedHelpers.findClass("dalvik.system.DexPathList$Element", loader), new Class[]{
|
Class<?> dexFileClass = Class.forName("dalvik.system.DexFile");
|
||||||
DexFile.class
|
Object dexFile = dexFileClass.getConstructor(String.class).newInstance(providerPath.toString());
|
||||||
}, dexFile);
|
Class<?> elementClass = Class.forName("dalvik.system.DexPathList$Element");
|
||||||
|
Object element = elementClass.getConstructor(dexFileClass).newInstance(dexFile);
|
||||||
Array.set(newElements, length, element);
|
Array.set(newElements, length, element);
|
||||||
XposedHelpers.setObjectField(dexPathList, "dexElements", newElements);
|
XposedHelpers.setObjectField(dexPathList, "dexElements", newElements);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "Failed to inject provider dex: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XposedHelpers.setObjectField(mBoundApplication, "info", appLoadedApk);
|
XposedHelpers.setObjectField(mBoundApplication, "info", appLoadedApk);
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,20 @@ package org.lsposed.npatch.loader;
|
||||||
|
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
import android.app.LoadedApk;
|
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.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedInit;
|
import de.robv.android.xposed.XposedInit;
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
|
|
||||||
public class LSPLoader {
|
public class LSPLoader {
|
||||||
|
private static final String TAG = "NPatch";
|
||||||
|
|
||||||
public static void initModules(LoadedApk loadedApk) {
|
public static void initModules(LoadedApk loadedApk) {
|
||||||
XposedInit.loadedPackagesInProcess.add(loadedApk.getPackageName());
|
XposedInit.loadedPackagesInProcess.add(loadedApk.getPackageName());
|
||||||
XResources.setPackageNameForResDir(loadedApk.getPackageName(), loadedApk.getResDir());
|
setPackageNameForResDir(loadedApk.getPackageName(), loadedApk.getResDir());
|
||||||
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(
|
XC_LoadPackage.LoadPackageParam lpparam = new XC_LoadPackage.LoadPackageParam(
|
||||||
XposedBridge.sLoadedPackageCallbacks);
|
XposedBridge.sLoadedPackageCallbacks);
|
||||||
lpparam.packageName = loadedApk.getPackageName();
|
lpparam.packageName = loadedApk.getPackageName();
|
||||||
|
|
@ -21,4 +25,18 @@ public class LSPLoader {
|
||||||
lpparam.isFirstApplication = true;
|
lpparam.isFirstApplication = true;
|
||||||
XC_LoadPackage.callAll(lpparam);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue