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.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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue