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;
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
Hookers.logE("error when hooking LoadedApk#createClassLoader", t);
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import java.io.IOException;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
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) {
|
||||
for (XposedModule module : modules) {
|
||||
try {
|
||||
|
|
@ -82,6 +104,18 @@ public class LSPosedContext implements XposedInterface {
|
|||
} catch (Throwable 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;
|
||||
}
|
||||
try {
|
||||
var moduleEntry = moduleClass.getConstructor(XposedInterface.class, XposedModuleInterface.ModuleLoadedParam.class);
|
||||
var moduleContext = (XposedModule) moduleEntry.newInstance(ctx, new XposedModuleInterface.ModuleLoadedParam() {
|
||||
@Override
|
||||
public boolean isSystemServer() {
|
||||
return isSystemServer;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getProcessName() {
|
||||
return processName;
|
||||
}
|
||||
});
|
||||
var moduleContext = instantiateModuleCompat(moduleClass, ctx);
|
||||
modules.add(moduleContext);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, " Failed to load class " + moduleClass, e);
|
||||
|
|
@ -139,6 +161,127 @@ public class LSPosedContext implements XposedInterface {
|
|||
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
|
||||
@Override
|
||||
public String getFrameworkName() {
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
|
||||
@Override
|
||||
public int getXposedApiVersion() {
|
||||
return IXposedService.API;
|
||||
return IXposedService.LIB_API;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ public class LSPModuleService extends IXposedService.Stub {
|
|||
@Override
|
||||
public int getAPIVersion() throws RemoteException {
|
||||
ensureModule();
|
||||
return API;
|
||||
return IXposedService.LIB_API;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -152,6 +152,12 @@ public class LSPModuleService extends IXposedService.Stub {
|
|||
return IXposedService.FRAMEWORK_PRIVILEGE_ROOT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFrameworkProperties() throws RemoteException {
|
||||
ensureModule();
|
||||
return IXposedService.PROP_CAP_SYSTEM | IXposedService.PROP_CAP_REMOTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getScope() throws RemoteException {
|
||||
ensureModule();
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 496b76fa3e5af87958ebef97bd160319e05da79b
|
||||
Subproject commit ec7cb26aa215b20b2ab1baf7c7b5a91a021a9016
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit b896dbcda3fa1550d04d43d962923318ed5a61a8
|
||||
Subproject commit 745afd390c7b87d9cd31efd246a7bc12cc190ac0
|
||||
Loading…
Reference in New Issue