Treewide: Add more API 101 behaviour
This commit is contained in:
parent
62ea99fa35
commit
43ffcee9c8
|
|
@ -160,6 +160,31 @@ public class LoadedApkCreateCLHooker implements XposedInterface.Hooker {
|
||||||
return isFirstPackage;
|
return isFirstPackage;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ClassLoader defaultClassLoaderForCompat = classLoader;
|
||||||
|
if (defaultClassLoaderField != null) {
|
||||||
|
try {
|
||||||
|
var defaultCl = (ClassLoader) defaultClassLoaderField.get(loadedApk);
|
||||||
|
if (defaultCl != null) {
|
||||||
|
defaultClassLoaderForCompat = defaultCl;
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object appComponentFactory = null;
|
||||||
|
try {
|
||||||
|
appComponentFactory = XposedHelpers.getObjectField(loadedApk, "mAppComponentFactory");
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
LSPosedContext.callOnPackageReady(
|
||||||
|
loadedApk.getPackageName(),
|
||||||
|
loadedApk.getApplicationInfo(),
|
||||||
|
isFirstPackage,
|
||||||
|
defaultClassLoaderForCompat,
|
||||||
|
classLoader,
|
||||||
|
appComponentFactory
|
||||||
|
);
|
||||||
|
Hookers.logD("callOnPackageReady via LoadedApkCreateCLHooker: " + loadedApk.getPackageName());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Hookers.logE("error when hooking LoadedApk#createClassLoader", t);
|
Hookers.logE("error when hooking LoadedApk#createClassLoader", t);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import java.io.IOException;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
|
|
@ -75,6 +76,27 @@ public class LSPosedContext implements XposedInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void callOnPackageReady(
|
||||||
|
String packageName,
|
||||||
|
ApplicationInfo applicationInfo,
|
||||||
|
boolean isFirstPackage,
|
||||||
|
ClassLoader defaultClassLoader,
|
||||||
|
ClassLoader classLoader,
|
||||||
|
Object appComponentFactory
|
||||||
|
) {
|
||||||
|
var lifecycle = new CompatLifecycleData(
|
||||||
|
packageName,
|
||||||
|
applicationInfo,
|
||||||
|
isFirstPackage,
|
||||||
|
defaultClassLoader,
|
||||||
|
classLoader,
|
||||||
|
appComponentFactory
|
||||||
|
);
|
||||||
|
for (XposedModule module : modules) {
|
||||||
|
invokeCompatLifecycle(module, "onPackageReady", lifecycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void callOnSystemServerLoaded(XposedModuleInterface.SystemServerLoadedParam param) {
|
public static void callOnSystemServerLoaded(XposedModuleInterface.SystemServerLoadedParam param) {
|
||||||
for (XposedModule module : modules) {
|
for (XposedModule module : modules) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -82,6 +104,18 @@ public class LSPosedContext implements XposedInterface {
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Log.e(TAG, "Error when calling onSystemServerLoaded of " + module.getApplicationInfo().packageName, t);
|
Log.e(TAG, "Error when calling onSystemServerLoaded of " + module.getApplicationInfo().packageName, t);
|
||||||
}
|
}
|
||||||
|
invokeCompatLifecycle(
|
||||||
|
module,
|
||||||
|
"onSystemServerStarting",
|
||||||
|
new CompatLifecycleData(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
param.getClassLoader(),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,19 +146,7 @@ public class LSPosedContext implements XposedInterface {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var moduleEntry = moduleClass.getConstructor(XposedInterface.class, XposedModuleInterface.ModuleLoadedParam.class);
|
var moduleContext = instantiateModuleCompat(moduleClass, ctx);
|
||||||
var moduleContext = (XposedModule) moduleEntry.newInstance(ctx, new XposedModuleInterface.ModuleLoadedParam() {
|
|
||||||
@Override
|
|
||||||
public boolean isSystemServer() {
|
|
||||||
return isSystemServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String getProcessName() {
|
|
||||||
return processName;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
modules.add(moduleContext);
|
modules.add(moduleContext);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.e(TAG, " Failed to load class " + moduleClass, e);
|
Log.e(TAG, " Failed to load class " + moduleClass, e);
|
||||||
|
|
@ -139,6 +161,127 @@ public class LSPosedContext implements XposedInterface {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static XposedModule instantiateModuleCompat(Class<?> moduleClass, LSPosedContext context) throws Throwable {
|
||||||
|
// New API-style path: no-arg constructor + attachFramework + onModuleLoaded callback.
|
||||||
|
try {
|
||||||
|
var ctor = moduleClass.getDeclaredConstructor();
|
||||||
|
ctor.setAccessible(true);
|
||||||
|
var instance = ctor.newInstance();
|
||||||
|
if (instance instanceof XposedModule) {
|
||||||
|
var typed = (XposedModule) instance;
|
||||||
|
typed.attachFramework(context);
|
||||||
|
typed.onModuleLoaded(new XposedModuleInterface.ModuleLoadedParam() {
|
||||||
|
@Override
|
||||||
|
public boolean isSystemServer() {
|
||||||
|
return isSystemServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getProcessName() {
|
||||||
|
return processName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (XposedModule) instance;
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old API-style path: constructor takes (XposedInterface, ModuleLoadedParam).
|
||||||
|
var moduleEntry = moduleClass.getConstructor(XposedInterface.class, XposedModuleInterface.ModuleLoadedParam.class);
|
||||||
|
return (XposedModule) moduleEntry.newInstance(context, new XposedModuleInterface.ModuleLoadedParam() {
|
||||||
|
@Override
|
||||||
|
public boolean isSystemServer() {
|
||||||
|
return isSystemServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getProcessName() {
|
||||||
|
return processName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CompatLifecycleData {
|
||||||
|
final String packageName;
|
||||||
|
final ApplicationInfo applicationInfo;
|
||||||
|
final boolean isFirstPackage;
|
||||||
|
final ClassLoader defaultClassLoader;
|
||||||
|
final ClassLoader classLoader;
|
||||||
|
final Object appComponentFactory;
|
||||||
|
|
||||||
|
CompatLifecycleData(
|
||||||
|
String packageName,
|
||||||
|
ApplicationInfo applicationInfo,
|
||||||
|
boolean isFirstPackage,
|
||||||
|
ClassLoader defaultClassLoader,
|
||||||
|
ClassLoader classLoader,
|
||||||
|
Object appComponentFactory
|
||||||
|
) {
|
||||||
|
this.packageName = packageName;
|
||||||
|
this.applicationInfo = applicationInfo;
|
||||||
|
this.isFirstPackage = isFirstPackage;
|
||||||
|
this.defaultClassLoader = defaultClassLoader;
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
this.appComponentFactory = appComponentFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void invokeCompatLifecycle(XposedModule module, String methodName, CompatLifecycleData data) {
|
||||||
|
try {
|
||||||
|
for (Method method : module.getClass().getMethods()) {
|
||||||
|
if (!method.getName().equals(methodName) || method.getParameterCount() != 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var paramType = method.getParameterTypes()[0];
|
||||||
|
var proxy = Proxy.newProxyInstance(
|
||||||
|
paramType.getClassLoader(),
|
||||||
|
new Class<?>[]{paramType},
|
||||||
|
(proxyObj, invokedMethod, args) -> {
|
||||||
|
String invokedName = invokedMethod.getName();
|
||||||
|
return switch (invokedName) {
|
||||||
|
case "getPackageName" -> data.packageName;
|
||||||
|
case "getApplicationInfo" -> data.applicationInfo;
|
||||||
|
case "isFirstPackage" -> data.isFirstPackage;
|
||||||
|
case "getDefaultClassLoader" -> data.defaultClassLoader;
|
||||||
|
case "getClassLoader" -> data.classLoader;
|
||||||
|
case "getAppComponentFactory" -> data.appComponentFactory;
|
||||||
|
case "getClassLoaderForSystemServer", "getSystemServerClassLoader" -> data.defaultClassLoader;
|
||||||
|
// Keep Object-contract methods stable for proxy users.
|
||||||
|
case "toString" -> "CompatLifecycleProxy(" + methodName + "," + data.packageName + ")";
|
||||||
|
case "hashCode" -> System.identityHashCode(proxyObj);
|
||||||
|
case "equals" -> proxyObj == (args == null || args.length == 0 ? null : args[0]);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
method.invoke(module, proxy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
String modulePkg = "<unknown>";
|
||||||
|
try {
|
||||||
|
if (module.getApplicationInfo() != null && module.getApplicationInfo().packageName != null) {
|
||||||
|
modulePkg = module.getApplicationInfo().packageName;
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
Throwable root = t;
|
||||||
|
if (t instanceof InvocationTargetException && ((InvocationTargetException) t).getTargetException() != null) {
|
||||||
|
root = ((InvocationTargetException) t).getTargetException();
|
||||||
|
} else if (t.getCause() != null) {
|
||||||
|
root = t.getCause();
|
||||||
|
}
|
||||||
|
String brief = "LSP_INVOKE_ERR method=" + methodName
|
||||||
|
+ " module=" + modulePkg
|
||||||
|
+ " type=" + root.getClass().getName()
|
||||||
|
+ " msg=" + String.valueOf(root.getMessage());
|
||||||
|
android.util.Log.e(TAG, brief);
|
||||||
|
Log.e(TAG, brief, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String getFrameworkName() {
|
public String getFrameworkName() {
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getXposedApiVersion() {
|
public int getXposedApiVersion() {
|
||||||
return IXposedService.API;
|
return IXposedService.LIB_API;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ public class LSPModuleService extends IXposedService.Stub {
|
||||||
@Override
|
@Override
|
||||||
public int getAPIVersion() throws RemoteException {
|
public int getAPIVersion() throws RemoteException {
|
||||||
ensureModule();
|
ensureModule();
|
||||||
return API;
|
return IXposedService.LIB_API;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -152,6 +152,12 @@ public class LSPModuleService extends IXposedService.Stub {
|
||||||
return IXposedService.FRAMEWORK_PRIVILEGE_ROOT;
|
return IXposedService.FRAMEWORK_PRIVILEGE_ROOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getFrameworkProperties() throws RemoteException {
|
||||||
|
ensureModule();
|
||||||
|
return IXposedService.PROP_CAP_SYSTEM | IXposedService.PROP_CAP_REMOTE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getScope() throws RemoteException {
|
public List<String> getScope() throws RemoteException {
|
||||||
ensureModule();
|
ensureModule();
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 496b76fa3e5af87958ebef97bd160319e05da79b
|
Subproject commit ec7cb26aa215b20b2ab1baf7c7b5a91a021a9016
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit b896dbcda3fa1550d04d43d962923318ed5a61a8
|
Subproject commit 745afd390c7b87d9cd31efd246a7bc12cc190ac0
|
||||||
Loading…
Reference in New Issue