API: Compatiablity for API 101 over API 100
This commit is contained in:
parent
b896dbcda3
commit
745afd390c
|
|
@ -10,10 +10,12 @@ import androidx.annotation.Nullable;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.github.libxposed.api.errors.HookFailedError;
|
||||
import io.github.libxposed.api.utils.DexParser;
|
||||
|
|
@ -24,9 +26,28 @@ import io.github.libxposed.api.utils.DexParser;
|
|||
@SuppressWarnings("unused")
|
||||
public interface XposedInterface {
|
||||
/**
|
||||
* SDK API version.
|
||||
* The API version of this library.
|
||||
*/
|
||||
int API = 100;
|
||||
int LIB_API = 101;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #LIB_API}. Kept for API100 compatibility.
|
||||
*/
|
||||
@Deprecated
|
||||
int API = LIB_API;
|
||||
|
||||
/**
|
||||
* The framework has capability to hook system_server.
|
||||
*/
|
||||
long PROP_CAP_SYSTEM = 1L;
|
||||
/**
|
||||
* The framework provides remote preferences and remote files support.
|
||||
*/
|
||||
long PROP_CAP_REMOTE = 1L << 1;
|
||||
/**
|
||||
* The framework enables runtime Xposed API protection.
|
||||
*/
|
||||
long PROP_RT_API_PROTECTION = 1L << 2;
|
||||
|
||||
/**
|
||||
* Indicates that the framework is running as root.
|
||||
|
|
@ -208,6 +229,16 @@ public interface XposedInterface {
|
|||
* }</pre>
|
||||
*/
|
||||
interface Hooker {
|
||||
@Nullable
|
||||
default Object intercept(@NonNull Chain chain) throws Throwable {
|
||||
return chain.proceed();
|
||||
}
|
||||
}
|
||||
|
||||
enum ExceptionMode {
|
||||
DEFAULT,
|
||||
PASSTHROUGH,
|
||||
PROTECTIVE
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -228,6 +259,221 @@ public interface XposedInterface {
|
|||
void unhook();
|
||||
}
|
||||
|
||||
interface HookHandle {
|
||||
@NonNull
|
||||
Executable getExecutable();
|
||||
|
||||
void unhook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Chain context for the API 101 style interceptor.
|
||||
*/
|
||||
interface Chain {
|
||||
@NonNull
|
||||
Executable getExecutable();
|
||||
|
||||
@Nullable
|
||||
Object getThisObject();
|
||||
|
||||
@NonNull
|
||||
Object[] getArgs();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
default <T> T getArg(int index) {
|
||||
return (T) getArgs()[index];
|
||||
}
|
||||
|
||||
default void setArg(int index, @Nullable Object value) {
|
||||
getArgs()[index] = value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Object proceed() throws InvocationTargetException, IllegalArgumentException, IllegalAccessException;
|
||||
}
|
||||
|
||||
/**
|
||||
* API 101 interceptor.
|
||||
*/
|
||||
interface Interceptor {
|
||||
@Nullable
|
||||
Object intercept(@NonNull Chain chain) throws Throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* API 101 hook builder.
|
||||
*/
|
||||
interface HookBuilder {
|
||||
@NonNull
|
||||
default HookBuilder setPriority(int priority) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
default HookBuilder setExceptionMode(@NonNull ExceptionMode mode) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
HookHandle intercept(@NonNull Hooker hooker);
|
||||
|
||||
@NonNull
|
||||
default HookHandle intercept(@NonNull Interceptor interceptor) {
|
||||
return intercept(new Hooker() {
|
||||
@SuppressWarnings("unused")
|
||||
public Object intercept(@NonNull Chain chain) throws Throwable {
|
||||
return interceptor.intercept(chain);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final class LegacyHookRegistry {
|
||||
private LegacyHookRegistry() {
|
||||
}
|
||||
|
||||
static final ConcurrentHashMap<Executable, HookEntry> ENTRIES = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
final class HookEntry {
|
||||
final XposedInterface base;
|
||||
final Hooker hooker;
|
||||
final Method interceptMethod;
|
||||
final ExceptionMode exceptionMode;
|
||||
|
||||
HookEntry(@NonNull XposedInterface base, @NonNull Hooker hooker, @NonNull Method interceptMethod, @NonNull ExceptionMode exceptionMode) {
|
||||
this.base = base;
|
||||
this.hooker = hooker;
|
||||
this.interceptMethod = interceptMethod;
|
||||
this.exceptionMode = exceptionMode;
|
||||
}
|
||||
}
|
||||
|
||||
final class CompatHookBuilder implements HookBuilder {
|
||||
private final XposedInterface base;
|
||||
private final Executable origin;
|
||||
private int priority = PRIORITY_DEFAULT;
|
||||
private ExceptionMode exceptionMode = ExceptionMode.DEFAULT;
|
||||
|
||||
CompatHookBuilder(@NonNull XposedInterface base, @NonNull Executable origin) {
|
||||
this.base = base;
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HookBuilder setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HookBuilder setExceptionMode(@NonNull ExceptionMode mode) {
|
||||
this.exceptionMode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HookHandle intercept(@NonNull Hooker hooker) {
|
||||
final Method method = CompatHooker.findInterceptMethod(hooker.getClass());
|
||||
LegacyHookRegistry.ENTRIES.put(origin, new HookEntry(base, hooker, method, exceptionMode));
|
||||
final MethodUnhooker<? extends Executable> raw;
|
||||
if (origin instanceof Method) {
|
||||
raw = base.hook((Method) origin, priority, CompatHooker.class);
|
||||
} else if (origin instanceof Constructor<?>) {
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
MethodUnhooker<? extends Executable> cast = (MethodUnhooker) base.hook((Constructor) origin, priority, CompatHooker.class);
|
||||
raw = cast;
|
||||
} else {
|
||||
LegacyHookRegistry.ENTRIES.remove(origin);
|
||||
throw new IllegalArgumentException("Unsupported executable type: " + origin.getClass().getName());
|
||||
}
|
||||
return new HookHandle() {
|
||||
@NonNull
|
||||
@Override
|
||||
public Executable getExecutable() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unhook() {
|
||||
LegacyHookRegistry.ENTRIES.remove(origin);
|
||||
raw.unhook();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
final class CompatHooker implements Hooker {
|
||||
private CompatHooker() {
|
||||
}
|
||||
|
||||
static Method findInterceptMethod(@NonNull Class<?> cls) {
|
||||
for (Method method : cls.getMethods()) {
|
||||
if (!method.getName().equals("intercept") || method.getParameterCount() != 1) {
|
||||
continue;
|
||||
}
|
||||
if (method.getParameterTypes()[0] == Chain.class) {
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Hooker must declare public intercept(Chain): " + cls.getName());
|
||||
}
|
||||
|
||||
public static void before(@NonNull BeforeHookCallback callback) {
|
||||
var member = callback.getMember();
|
||||
if (!(member instanceof Executable)) {
|
||||
return;
|
||||
}
|
||||
var executable = (Executable) member;
|
||||
var entry = LegacyHookRegistry.ENTRIES.get(executable);
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
var chain = new Chain() {
|
||||
@NonNull
|
||||
@Override
|
||||
public Executable getExecutable() {
|
||||
return executable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getThisObject() {
|
||||
return callback.getThisObject();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object[] getArgs() {
|
||||
return callback.getArgs();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public Object proceed() throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
||||
if (executable instanceof Method method) {
|
||||
return entry.base.invokeOrigin(method, callback.getThisObject(), callback.getArgs());
|
||||
}
|
||||
entry.base.invokeOrigin((Constructor) executable, callback.getThisObject(), callback.getArgs());
|
||||
return callback.getThisObject();
|
||||
}
|
||||
};
|
||||
try {
|
||||
callback.returnAndSkip(entry.interceptMethod.invoke(entry.hooker, chain));
|
||||
} catch (InvocationTargetException ite) {
|
||||
callback.throwAndSkip(ite.getCause() != null ? ite.getCause() : ite);
|
||||
} catch (Throwable t) {
|
||||
callback.throwAndSkip(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Xposed framework name of current implementation.
|
||||
*
|
||||
|
|
@ -236,6 +482,13 @@ public interface XposedInterface {
|
|||
@NonNull
|
||||
String getFrameworkName();
|
||||
|
||||
/**
|
||||
* Gets runtime API version.
|
||||
*/
|
||||
default int getApiVersion() {
|
||||
return LIB_API;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Xposed framework version of current implementation.
|
||||
*
|
||||
|
|
@ -251,6 +504,21 @@ public interface XposedInterface {
|
|||
*/
|
||||
long getFrameworkVersionCode();
|
||||
|
||||
/**
|
||||
* Gets the Xposed framework properties.
|
||||
*/
|
||||
default long getFrameworkProperties() {
|
||||
long prop = 0L;
|
||||
if (getFrameworkPrivilege() == FRAMEWORK_PRIVILEGE_ROOT ||
|
||||
getFrameworkPrivilege() == FRAMEWORK_PRIVILEGE_CONTAINER) {
|
||||
prop |= PROP_CAP_SYSTEM;
|
||||
}
|
||||
if (getFrameworkPrivilege() != FRAMEWORK_PRIVILEGE_EMBEDDED) {
|
||||
prop |= PROP_CAP_REMOTE;
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Xposed framework privilege of current implementation.
|
||||
*
|
||||
|
|
@ -345,6 +613,16 @@ public interface XposedInterface {
|
|||
@NonNull
|
||||
<T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Class<? extends Hooker> hooker);
|
||||
|
||||
@NonNull
|
||||
default HookBuilder hook(@NonNull Executable origin) {
|
||||
return new CompatHookBuilder(this, origin);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
default HookBuilder hook(@NonNull Executable origin, int priority) {
|
||||
return new CompatHookBuilder(this, origin).setPriority(priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deoptimizes a method in case hooked callee is not called because of inline.
|
||||
*
|
||||
|
|
@ -468,6 +746,14 @@ public interface XposedInterface {
|
|||
*/
|
||||
void log(@NonNull String message);
|
||||
|
||||
/**
|
||||
* Writes a message to the Xposed log with explicit priority and optional tag.
|
||||
*/
|
||||
default void log(int priority, @Nullable String tag, @NonNull String msg) {
|
||||
String prefix = tag == null || tag.trim().isEmpty() ? "" : ("[" + tag + "] ");
|
||||
log(prefix + msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a message with a stack trace to the Xposed log.
|
||||
*
|
||||
|
|
@ -476,6 +762,18 @@ public interface XposedInterface {
|
|||
*/
|
||||
void log(@NonNull String message, @NonNull Throwable throwable);
|
||||
|
||||
/**
|
||||
* Writes a message with stack trace to the Xposed log with explicit priority and optional tag.
|
||||
*/
|
||||
default void log(int priority, @Nullable String tag, @NonNull String msg, @Nullable Throwable tr) {
|
||||
String prefix = tag == null || tag.trim().isEmpty() ? "" : ("[" + tag + "] ");
|
||||
if (tr == null) {
|
||||
log(prefix + msg);
|
||||
} else {
|
||||
log(prefix + msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a dex file in memory.
|
||||
*
|
||||
|
|
@ -491,8 +789,17 @@ public interface XposedInterface {
|
|||
* Gets the application info of the module.
|
||||
*/
|
||||
@NonNull
|
||||
@Deprecated
|
||||
ApplicationInfo getApplicationInfo();
|
||||
|
||||
/**
|
||||
* Gets the application info of the module.
|
||||
*/
|
||||
@NonNull
|
||||
default ApplicationInfo getModuleApplicationInfo() {
|
||||
return getApplicationInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets remote preferences stored in Xposed framework. Note that those are read-only in hooked apps.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.annotation.Nullable;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
|
|
@ -21,151 +22,210 @@ import io.github.libxposed.api.utils.DexParser;
|
|||
*/
|
||||
public class XposedInterfaceWrapper implements XposedInterface {
|
||||
|
||||
private final XposedInterface mBase;
|
||||
private volatile XposedInterface mBase;
|
||||
|
||||
public XposedInterfaceWrapper() {
|
||||
}
|
||||
|
||||
XposedInterfaceWrapper(@NonNull XposedInterface base) {
|
||||
mBase = base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the framework interface to the module. Modules should never call this method directly.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final void attachFramework(@NonNull XposedInterface base) {
|
||||
if (mBase != null) {
|
||||
throw new IllegalStateException("Framework already attached");
|
||||
}
|
||||
mBase = base;
|
||||
}
|
||||
|
||||
private void ensureAttached() {
|
||||
if (mBase == null) {
|
||||
throw new IllegalStateException("Framework not attached");
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final String getFrameworkName() {
|
||||
ensureAttached();
|
||||
return mBase.getFrameworkName();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final String getFrameworkVersion() {
|
||||
ensureAttached();
|
||||
return mBase.getFrameworkVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long getFrameworkVersionCode() {
|
||||
ensureAttached();
|
||||
return mBase.getFrameworkVersionCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getFrameworkPrivilege() {
|
||||
ensureAttached();
|
||||
return mBase.getFrameworkPrivilege();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker) {
|
||||
ensureAttached();
|
||||
return mBase.hook(origin, hooker);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final HookBuilder hook(@NonNull Executable origin) {
|
||||
ensureAttached();
|
||||
return mBase.hook(origin);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final HookBuilder hook(@NonNull Executable origin, int priority) {
|
||||
ensureAttached();
|
||||
return mBase.hook(origin, priority);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> MethodUnhooker<Constructor<T>> hookClassInitializer(@NonNull Class<T> origin, @NonNull Class<? extends Hooker> hooker) {
|
||||
ensureAttached();
|
||||
return mBase.hookClassInitializer(origin, hooker);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T> MethodUnhooker<Constructor<T>> hookClassInitializer(@NonNull Class<T> origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||
ensureAttached();
|
||||
return mBase.hookClassInitializer(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final MethodUnhooker<Method> hook(@NonNull Method origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||
ensureAttached();
|
||||
return mBase.hook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull Class<? extends Hooker> hooker) {
|
||||
ensureAttached();
|
||||
return mBase.hook(origin, hooker);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||
ensureAttached();
|
||||
return mBase.hook(origin, priority, hooker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean deoptimize(@NonNull Method method) {
|
||||
ensureAttached();
|
||||
return mBase.deoptimize(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final <T> boolean deoptimize(@NonNull Constructor<T> constructor) {
|
||||
ensureAttached();
|
||||
return mBase.deoptimize(constructor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
||||
ensureAttached();
|
||||
return mBase.invokeOrigin(method, thisObject, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void invokeOrigin(@NonNull Constructor<T> constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
||||
ensureAttached();
|
||||
mBase.invokeOrigin(constructor, thisObject, args);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
||||
ensureAttached();
|
||||
return mBase.invokeSpecial(method, thisObject, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void invokeSpecial(@NonNull Constructor<T> constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
||||
ensureAttached();
|
||||
mBase.invokeSpecial(constructor, thisObject, args);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final <T> T newInstanceOrigin(@NonNull Constructor<T> constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {
|
||||
ensureAttached();
|
||||
return mBase.newInstanceOrigin(constructor, args);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final <T, U> U newInstanceSpecial(@NonNull Constructor<T> constructor, @NonNull Class<U> subClass, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {
|
||||
ensureAttached();
|
||||
return mBase.newInstanceSpecial(constructor, subClass, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void log(@NonNull String message) {
|
||||
ensureAttached();
|
||||
mBase.log(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void log(@NonNull String message, @NonNull Throwable throwable) {
|
||||
ensureAttached();
|
||||
mBase.log(message, throwable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException {
|
||||
ensureAttached();
|
||||
return mBase.parseDex(dexData, includeAnnotations);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SharedPreferences getRemotePreferences(@NonNull String name) {
|
||||
ensureAttached();
|
||||
return mBase.getRemotePreferences(name);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ApplicationInfo getApplicationInfo() {
|
||||
ensureAttached();
|
||||
return mBase.getApplicationInfo();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String[] listRemoteFiles() {
|
||||
ensureAttached();
|
||||
return mBase.listRemoteFiles();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ParcelFileDescriptor openRemoteFile(@NonNull String name) throws FileNotFoundException {
|
||||
ensureAttached();
|
||||
return mBase.openRemoteFile(name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,19 @@ import androidx.annotation.NonNull;
|
|||
|
||||
/**
|
||||
* Super class which all Xposed module entry classes should extend.<br/>
|
||||
* Entry classes will be instantiated exactly once for each process.
|
||||
* Entry classes will be instantiated exactly once for each process. Modules should avoid
|
||||
* initialization work before {@link #onModuleLoaded(ModuleLoadedParam)} is called.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class XposedModule extends XposedInterfaceWrapper implements XposedModuleInterface {
|
||||
/**
|
||||
* New API-style constructor. The framework will attach itself via
|
||||
* {@link #attachFramework(XposedInterface)} and then dispatch lifecycle callbacks.
|
||||
*/
|
||||
public XposedModule() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Xposed module.<br/>
|
||||
* When the module is loaded into the target process, the constructor will be called.
|
||||
|
|
@ -15,7 +24,10 @@ public abstract class XposedModule extends XposedInterfaceWrapper implements Xpo
|
|||
* @param base The implementation interface provided by the framework, should not be used by the module
|
||||
* @param param Information about the process in which the module is loaded
|
||||
*/
|
||||
@Deprecated
|
||||
public XposedModule(@NonNull XposedInterface base, @NonNull ModuleLoadedParam param) {
|
||||
super(base);
|
||||
this();
|
||||
attachFramework(base);
|
||||
onModuleLoaded(param);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package io.github.libxposed.api;
|
||||
|
||||
import android.app.AppComponentFactory;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Build;
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ public interface XposedModuleInterface {
|
|||
/**
|
||||
* Wraps information about system server.
|
||||
*/
|
||||
interface SystemServerLoadedParam {
|
||||
interface SystemServerStartingParam {
|
||||
/**
|
||||
* Gets the class loader of system server.
|
||||
*
|
||||
|
|
@ -44,6 +45,13 @@ public interface XposedModuleInterface {
|
|||
ClassLoader getClassLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Kept for API100 compatibility.
|
||||
*/
|
||||
@Deprecated
|
||||
interface SystemServerLoadedParam extends SystemServerStartingParam {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps information about the package being loaded.
|
||||
*/
|
||||
|
|
@ -73,6 +81,27 @@ public interface XposedModuleInterface {
|
|||
@NonNull
|
||||
ClassLoader getDefaultClassLoader();
|
||||
|
||||
/**
|
||||
* Gets information about whether is this package the first and main package of the app process.
|
||||
*
|
||||
* @return {@code true} if this is the first package.
|
||||
*/
|
||||
boolean isFirstPackage();
|
||||
|
||||
/**
|
||||
* @deprecated Kept for API100 compatibility. Use {@link PackageReadyParam#getClassLoader()}.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull
|
||||
default ClassLoader getClassLoader() {
|
||||
return getDefaultClassLoader();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps information about the package whose class loader is ready.
|
||||
*/
|
||||
interface PackageReadyParam extends PackageLoadedParam {
|
||||
/**
|
||||
* Gets the class loader of the package being loaded.
|
||||
*
|
||||
|
|
@ -82,11 +111,17 @@ public interface XposedModuleInterface {
|
|||
ClassLoader getClassLoader();
|
||||
|
||||
/**
|
||||
* Gets information about whether is this package the first and main package of the app process.
|
||||
*
|
||||
* @return {@code true} if this is the first package.
|
||||
* Gets the app component factory of the package being loaded.
|
||||
*/
|
||||
boolean isFirstPackage();
|
||||
@RequiresApi(Build.VERSION_CODES.P)
|
||||
@NonNull
|
||||
AppComponentFactory getAppComponentFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets notified when the module is loaded into the target process.
|
||||
*/
|
||||
default void onModuleLoaded(@NonNull ModuleLoadedParam param) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -104,5 +139,19 @@ public interface XposedModuleInterface {
|
|||
* @param param Information about system server
|
||||
*/
|
||||
default void onSystemServerLoaded(@NonNull SystemServerLoadedParam param) {
|
||||
onSystemServerStarting(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets notified when custom {@link android.app.AppComponentFactory} has created the final
|
||||
* class loader.
|
||||
*/
|
||||
default void onPackageReady(@NonNull PackageReadyParam param) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets notified when system server is ready to start critical services.
|
||||
*/
|
||||
default void onSystemServerStarting(@NonNull SystemServerStartingParam param) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue