[core] Fix resource hook in Android 12 (#1270)
This commit is contained in:
parent
4dc0705e75
commit
2388517b96
|
|
@ -53,6 +53,7 @@ import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
|
@ -101,46 +102,70 @@ public final class XposedInit {
|
||||||
final Class<?> classGTLR;
|
final Class<?> classGTLR;
|
||||||
final Class<?> classResKey;
|
final Class<?> classResKey;
|
||||||
final ThreadLocal<Object> latestResKey = new ThreadLocal<>();
|
final ThreadLocal<Object> latestResKey = new ThreadLocal<>();
|
||||||
final String createResourceMethod;
|
final ArrayList<String> createResourceMethods = new ArrayList<>();
|
||||||
|
|
||||||
classGTLR = android.app.ResourcesManager.class;
|
classGTLR = android.app.ResourcesManager.class;
|
||||||
classResKey = android.content.res.ResourcesKey.class;
|
classResKey = android.content.res.ResourcesKey.class;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
if (Build.VERSION.SDK_INT < 30) {
|
createResourceMethods.add("createResources");
|
||||||
createResourceMethod = "getOrCreateResources";
|
createResourceMethods.add("createResourcesForActivity");
|
||||||
|
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
||||||
|
createResourceMethods.add("createResources");
|
||||||
} else {
|
} else {
|
||||||
createResourceMethod = "createResources";
|
createResourceMethods.add("getOrCreateResources");
|
||||||
}
|
}
|
||||||
|
|
||||||
hookAllMethods(classGTLR, createResourceMethod, new XC_MethodHook() {
|
//noinspection TrivialFunctionalExpressionUsage
|
||||||
|
final Class<?> classActivityRes = ((Callable<Class<?>>) () -> {
|
||||||
|
try {
|
||||||
|
return XposedHelpers.findClass("android.app.ResourcesManager$ActivityResource", classGTLR.getClassLoader());
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).call();
|
||||||
|
|
||||||
|
|
||||||
|
var hooker = new XC_MethodHook() {
|
||||||
@Override
|
@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.
|
// At least on OnePlus 5, the method has an additional parameter compared to AOSP.
|
||||||
|
Object activityToken = null;
|
||||||
|
try {
|
||||||
final int activityTokenIdx = getParameterIndexByType(param.method, IBinder.class);
|
final int activityTokenIdx = getParameterIndexByType(param.method, IBinder.class);
|
||||||
|
activityToken = param.args[activityTokenIdx];
|
||||||
|
} catch (NoSuchFieldError ignored) {
|
||||||
|
}
|
||||||
final int resKeyIdx = getParameterIndexByType(param.method, classResKey);
|
final int resKeyIdx = getParameterIndexByType(param.method, classResKey);
|
||||||
|
|
||||||
String resDir = (String) getObjectField(param.args[resKeyIdx], "mResDir");
|
String resDir = (String) getObjectField(param.args[resKeyIdx], "mResDir");
|
||||||
XResources newRes = cloneToXResources(param, resDir);
|
XResources newRes = cloneToXResources(param, resDir);
|
||||||
if (newRes == null) {
|
if (newRes == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object activityToken = param.args[activityTokenIdx];
|
|
||||||
//noinspection SynchronizeOnNonFinalField
|
//noinspection SynchronizeOnNonFinalField
|
||||||
synchronized (param.thisObject) {
|
synchronized (param.thisObject) {
|
||||||
ArrayList<WeakReference<Resources>> resourceReferences;
|
ArrayList<Object> resourceReferences;
|
||||||
if (activityToken != null) {
|
if (activityToken != null) {
|
||||||
Object activityResources = callMethod(param.thisObject, "getOrCreateActivityResourcesStructLocked", activityToken);
|
Object activityResources = callMethod(param.thisObject, "getOrCreateActivityResourcesStructLocked", activityToken);
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
resourceReferences = (ArrayList<WeakReference<Resources>>) getObjectField(activityResources, "activityResources");
|
resourceReferences = (ArrayList<Object>) getObjectField(activityResources, "activityResources");
|
||||||
} else {
|
} else {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
resourceReferences = (ArrayList<WeakReference<Resources>>) getObjectField(param.thisObject, "mResourceReferences");
|
resourceReferences = (ArrayList<Object>) getObjectField(param.thisObject, "mResourceReferences");
|
||||||
}
|
}
|
||||||
|
if (classActivityRes == null) {
|
||||||
resourceReferences.add(new WeakReference<>(newRes));
|
resourceReferences.add(new WeakReference<>(newRes));
|
||||||
|
} else {
|
||||||
|
var activityRes = XposedHelpers.newInstance(classActivityRes);
|
||||||
|
XposedHelpers.setObjectField(activityRes, "resources", new WeakReference<>(newRes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var createResourceMethod : createResourceMethods) {
|
||||||
|
hookAllMethods(classGTLR, createResourceMethod, hooker);
|
||||||
|
}
|
||||||
|
|
||||||
findAndHookMethod(TypedArray.class, "obtain", Resources.class, int.class,
|
findAndHookMethod(TypedArray.class, "obtain", Resources.class, int.class,
|
||||||
new XC_MethodHook() {
|
new XC_MethodHook() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue