Compare commits
10 Commits
a42f85d06e
...
745afd390c
| Author | SHA1 | Date |
|---|---|---|
|
|
745afd390c | |
|
|
b896dbcda3 | |
|
|
ca2e4b8da8 | |
|
|
3248419435 | |
|
|
64e29bd657 | |
|
|
7b6727313c | |
|
|
55efdf9d15 | |
|
|
5458273031 | |
|
|
2f03a689cf | |
|
|
02fd45cae8 |
|
|
@ -19,14 +19,14 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: set up JDK 17
|
- name: set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
|
||||||
|
|
@ -42,16 +42,18 @@ jobs:
|
||||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.maven_pgp_signingKey }}
|
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.maven_pgp_signingKey }}
|
||||||
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.maven_pgp_signingPassword }}
|
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.maven_pgp_signingPassword }}
|
||||||
- name: Upload library
|
- name: Upload library
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: library
|
name: library
|
||||||
path: ~/.m2
|
path: ~/.m2/repository/
|
||||||
|
include-hidden-files: true
|
||||||
|
if-no-files-found: error
|
||||||
- name: Upload pages
|
- name: Upload pages
|
||||||
uses: actions/upload-pages-artifact@v1
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
# Upload entire repository
|
# Upload entire repository
|
||||||
path: 'api/build/intermediates/java_doc_dir/release'
|
path: 'api/build/intermediates/java_doc_dir/release/javaDocReleaseGeneration'
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@main
|
uses: actions/deploy-pages@v4
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ plugins {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "io.github.libxposed.api"
|
namespace = "io.github.libxposed.api"
|
||||||
compileSdk = 34
|
compileSdk = 35
|
||||||
buildToolsVersion = "34.0.0"
|
buildToolsVersion = "35.0.0"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
|
|
@ -20,8 +20,8 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
|
|
@ -92,6 +92,6 @@ signing {
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("androidx.annotation:annotation:1.6.0")
|
compileOnly(libs.annotation)
|
||||||
lintPublish(project(":checks"))
|
lintPublish(project(":checks"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,8 @@
|
||||||
-keep class io.github.libxposed.** { *; }
|
-keep class io.github.libxposed.** { *; }
|
||||||
|
-keepclassmembers,allowoptimization class ** implements io.github.libxposed.api.XposedInterface$Hooker {
|
||||||
|
public static *** before();
|
||||||
|
public static *** before(io.github.libxposed.api.XposedInterface$BeforeHookCallback);
|
||||||
|
public static void after();
|
||||||
|
public static void after(io.github.libxposed.api.XposedInterface$AfterHookCallback);
|
||||||
|
public static void after(io.github.libxposed.api.XposedInterface$AfterHookCallback, ***);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,13 @@ import androidx.annotation.Nullable;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import io.github.libxposed.api.annotations.AfterInvocation;
|
|
||||||
import io.github.libxposed.api.annotations.BeforeInvocation;
|
|
||||||
import io.github.libxposed.api.annotations.XposedHooker;
|
|
||||||
import io.github.libxposed.api.errors.HookFailedError;
|
import io.github.libxposed.api.errors.HookFailedError;
|
||||||
import io.github.libxposed.api.utils.DexParser;
|
import io.github.libxposed.api.utils.DexParser;
|
||||||
|
|
||||||
|
|
@ -27,9 +26,28 @@ import io.github.libxposed.api.utils.DexParser;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public interface XposedInterface {
|
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.
|
* Indicates that the framework is running as root.
|
||||||
|
|
@ -166,9 +184,8 @@ public interface XposedInterface {
|
||||||
* like the old API.
|
* like the old API.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Classes implementing this interface should be annotated with {@link XposedHooker} and should provide
|
* Classes implementing this interface should should provide two public static methods named
|
||||||
* two public static methods that are annotated with {@link BeforeInvocation} and {@link AfterInvocation},
|
* before and after for before invocation and after invocation respectively.
|
||||||
* respectively.
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -187,30 +204,24 @@ public interface XposedInterface {
|
||||||
* <p>Example usage:</p>
|
* <p>Example usage:</p>
|
||||||
*
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* @XposedHooker
|
|
||||||
* public class ExampleHooker implements Hooker {
|
* public class ExampleHooker implements Hooker {
|
||||||
*
|
*
|
||||||
* @BeforeInvocation
|
|
||||||
* public static void before(@NonNull BeforeHookCallback callback) {
|
* public static void before(@NonNull BeforeHookCallback callback) {
|
||||||
* // Pre-hooking logic goes here
|
* // Pre-hooking logic goes here
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @AfterInvocation
|
|
||||||
* public static void after(@NonNull AfterHookCallback callback) {
|
* public static void after(@NonNull AfterHookCallback callback) {
|
||||||
* // Post-hooking logic goes here
|
* // Post-hooking logic goes here
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @XposedHooker
|
|
||||||
* public class ExampleHookerWithContext implements Hooker {
|
* public class ExampleHookerWithContext implements Hooker {
|
||||||
*
|
*
|
||||||
* @BeforeInvocation
|
|
||||||
* public static MyContext before(@NonNull BeforeHookCallback callback) {
|
* public static MyContext before(@NonNull BeforeHookCallback callback) {
|
||||||
* // Pre-hooking logic goes here
|
* // Pre-hooking logic goes here
|
||||||
* return new MyContext();
|
* return new MyContext();
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @AfterInvocation
|
|
||||||
* public static void after(@NonNull AfterHookCallback callback, MyContext context) {
|
* public static void after(@NonNull AfterHookCallback callback, MyContext context) {
|
||||||
* // Post-hooking logic goes here
|
* // Post-hooking logic goes here
|
||||||
* }
|
* }
|
||||||
|
|
@ -218,6 +229,16 @@ public interface XposedInterface {
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
interface Hooker {
|
interface Hooker {
|
||||||
|
@Nullable
|
||||||
|
default Object intercept(@NonNull Chain chain) throws Throwable {
|
||||||
|
return chain.proceed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExceptionMode {
|
||||||
|
DEFAULT,
|
||||||
|
PASSTHROUGH,
|
||||||
|
PROTECTIVE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -238,6 +259,221 @@ public interface XposedInterface {
|
||||||
void unhook();
|
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.
|
* Gets the Xposed framework name of current implementation.
|
||||||
*
|
*
|
||||||
|
|
@ -246,6 +482,13 @@ public interface XposedInterface {
|
||||||
@NonNull
|
@NonNull
|
||||||
String getFrameworkName();
|
String getFrameworkName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets runtime API version.
|
||||||
|
*/
|
||||||
|
default int getApiVersion() {
|
||||||
|
return LIB_API;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Xposed framework version of current implementation.
|
* Gets the Xposed framework version of current implementation.
|
||||||
*
|
*
|
||||||
|
|
@ -261,6 +504,21 @@ public interface XposedInterface {
|
||||||
*/
|
*/
|
||||||
long getFrameworkVersionCode();
|
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.
|
* Gets the Xposed framework privilege of current implementation.
|
||||||
*
|
*
|
||||||
|
|
@ -281,6 +539,37 @@ public interface XposedInterface {
|
||||||
@NonNull
|
@NonNull
|
||||||
MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker);
|
MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook the static initializer of a class with default priority.
|
||||||
|
* <p>
|
||||||
|
* Note: If the class is initialized, the hook will never be called.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param origin The class to be hooked
|
||||||
|
* @param hooker The hooker class
|
||||||
|
* @return Unhooker for canceling the hook
|
||||||
|
* @throws IllegalArgumentException if class has no static initializer or hooker is invalid
|
||||||
|
* @throws HookFailedError if hook fails due to framework internal error
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
<T> MethodUnhooker<Constructor<T>> hookClassInitializer(@NonNull Class<T> origin, @NonNull Class<? extends Hooker> hooker);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook the static initializer of a class with specified priority.
|
||||||
|
* <p>
|
||||||
|
* Note: If the class is initialized, the hook will never be called.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param origin The class to be hooked
|
||||||
|
* @param priority The hook priority
|
||||||
|
* @param hooker The hooker class
|
||||||
|
* @return Unhooker for canceling the hook
|
||||||
|
* @throws IllegalArgumentException if class has no static initializer or hooker is invalid
|
||||||
|
* @throws HookFailedError if hook fails due to framework internal error
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
<T> MethodUnhooker<Constructor<T>> hookClassInitializer(@NonNull Class<T> origin, int priority, @NonNull Class<? extends Hooker> hooker);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook a method with specified priority.
|
* Hook a method with specified priority.
|
||||||
*
|
*
|
||||||
|
|
@ -324,6 +613,16 @@ public interface XposedInterface {
|
||||||
@NonNull
|
@NonNull
|
||||||
<T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Class<? extends Hooker> hooker);
|
<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.
|
* Deoptimizes a method in case hooked callee is not called because of inline.
|
||||||
*
|
*
|
||||||
|
|
@ -365,6 +664,18 @@ public interface XposedInterface {
|
||||||
@Nullable
|
@Nullable
|
||||||
Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException;
|
Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basically the same as {@link Constructor#newInstance(Object...)}, but calls the original constructor
|
||||||
|
* as it was before the interception by Xposed.
|
||||||
|
*
|
||||||
|
* @param constructor The constructor to create and initialize a new instance
|
||||||
|
* @param thisObject The instance to be constructed
|
||||||
|
* @param args The arguments used for the construction
|
||||||
|
* @param <T> The type of the instance
|
||||||
|
* @see Constructor#newInstance(Object...)
|
||||||
|
*/
|
||||||
|
<T> void invokeOrigin(@NonNull Constructor<T> constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes a special (non-virtual) method on a given object instance, similar to the functionality of
|
* Invokes a special (non-virtual) method on a given object instance, similar to the functionality of
|
||||||
* {@code CallNonVirtual<type>Method} in JNI, which invokes an instance (nonstatic) method on a Java
|
* {@code CallNonVirtual<type>Method} in JNI, which invokes an instance (nonstatic) method on a Java
|
||||||
|
|
@ -382,6 +693,21 @@ public interface XposedInterface {
|
||||||
@Nullable
|
@Nullable
|
||||||
Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException;
|
Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes a special (non-virtual) method on a given object instance, similar to the functionality of
|
||||||
|
* {@code CallNonVirtual<type>Method} in JNI, which invokes an instance (nonstatic) method on a Java
|
||||||
|
* object. This method is useful when you need to call a specific method on an object, bypassing any
|
||||||
|
* overridden methods in subclasses and directly invoking the method defined in the specified class.
|
||||||
|
*
|
||||||
|
* <p>This method is useful when you need to call {@code super.xxx()} in a hooked constructor.</p>
|
||||||
|
*
|
||||||
|
* @param constructor The constructor to create and initialize a new instance
|
||||||
|
* @param thisObject The instance to be constructed
|
||||||
|
* @param args The arguments used for the construction
|
||||||
|
* @see Constructor#newInstance(Object...)
|
||||||
|
*/
|
||||||
|
<T> void invokeSpecial(@NonNull Constructor<T> constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basically the same as {@link Constructor#newInstance(Object...)}, but calls the original constructor
|
* Basically the same as {@link Constructor#newInstance(Object...)}, but calls the original constructor
|
||||||
* as it was before the interception by Xposed.
|
* as it was before the interception by Xposed.
|
||||||
|
|
@ -420,6 +746,14 @@ public interface XposedInterface {
|
||||||
*/
|
*/
|
||||||
void log(@NonNull String message);
|
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.
|
* Writes a message with a stack trace to the Xposed log.
|
||||||
*
|
*
|
||||||
|
|
@ -428,6 +762,18 @@ public interface XposedInterface {
|
||||||
*/
|
*/
|
||||||
void log(@NonNull String message, @NonNull Throwable throwable);
|
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.
|
* Parse a dex file in memory.
|
||||||
*
|
*
|
||||||
|
|
@ -443,8 +789,17 @@ public interface XposedInterface {
|
||||||
* Gets the application info of the module.
|
* Gets the application info of the module.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@Deprecated
|
||||||
ApplicationInfo getApplicationInfo();
|
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.
|
* 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.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Constructor;
|
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.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
@ -21,129 +22,210 @@ import io.github.libxposed.api.utils.DexParser;
|
||||||
*/
|
*/
|
||||||
public class XposedInterfaceWrapper implements XposedInterface {
|
public class XposedInterfaceWrapper implements XposedInterface {
|
||||||
|
|
||||||
private final XposedInterface mBase;
|
private volatile XposedInterface mBase;
|
||||||
|
|
||||||
|
public XposedInterfaceWrapper() {
|
||||||
|
}
|
||||||
|
|
||||||
XposedInterfaceWrapper(@NonNull XposedInterface base) {
|
XposedInterfaceWrapper(@NonNull XposedInterface base) {
|
||||||
mBase = 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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final String getFrameworkName() {
|
public final String getFrameworkName() {
|
||||||
|
ensureAttached();
|
||||||
return mBase.getFrameworkName();
|
return mBase.getFrameworkName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final String getFrameworkVersion() {
|
public final String getFrameworkVersion() {
|
||||||
|
ensureAttached();
|
||||||
return mBase.getFrameworkVersion();
|
return mBase.getFrameworkVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final long getFrameworkVersionCode() {
|
public final long getFrameworkVersionCode() {
|
||||||
|
ensureAttached();
|
||||||
return mBase.getFrameworkVersionCode();
|
return mBase.getFrameworkVersionCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getFrameworkPrivilege() {
|
public final int getFrameworkPrivilege() {
|
||||||
|
ensureAttached();
|
||||||
return mBase.getFrameworkPrivilege();
|
return mBase.getFrameworkPrivilege();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker) {
|
public final MethodUnhooker<Method> hook(@NonNull Method origin, @NonNull Class<? extends Hooker> hooker) {
|
||||||
|
ensureAttached();
|
||||||
return mBase.hook(origin, hooker);
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final MethodUnhooker<Method> hook(@NonNull Method origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
public final MethodUnhooker<Method> hook(@NonNull Method origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
||||||
|
ensureAttached();
|
||||||
return mBase.hook(origin, priority, hooker);
|
return mBase.hook(origin, priority, hooker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull Class<? extends Hooker> hooker) {
|
public final <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, @NonNull Class<? extends Hooker> hooker) {
|
||||||
|
ensureAttached();
|
||||||
return mBase.hook(origin, hooker);
|
return mBase.hook(origin, hooker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final <T> MethodUnhooker<Constructor<T>> hook(@NonNull Constructor<T> origin, int priority, @NonNull Class<? extends Hooker> hooker) {
|
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);
|
return mBase.hook(origin, priority, hooker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean deoptimize(@NonNull Method method) {
|
public final boolean deoptimize(@NonNull Method method) {
|
||||||
|
ensureAttached();
|
||||||
return mBase.deoptimize(method);
|
return mBase.deoptimize(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final <T> boolean deoptimize(@NonNull Constructor<T> constructor) {
|
public final <T> boolean deoptimize(@NonNull Constructor<T> constructor) {
|
||||||
|
ensureAttached();
|
||||||
return mBase.deoptimize(constructor);
|
return mBase.deoptimize(constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public final Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
public final Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
||||||
|
ensureAttached();
|
||||||
return mBase.invokeOrigin(method, thisObject, args);
|
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
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public final Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
public final Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException {
|
||||||
|
ensureAttached();
|
||||||
return mBase.invokeSpecial(method, thisObject, args);
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final <T> T newInstanceOrigin(@NonNull Constructor<T> constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {
|
public final <T> T newInstanceOrigin(@NonNull Constructor<T> constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {
|
||||||
|
ensureAttached();
|
||||||
return mBase.newInstanceOrigin(constructor, args);
|
return mBase.newInstanceOrigin(constructor, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public final <T, U> U newInstanceSpecial(@NonNull Constructor<T> constructor, @NonNull Class<U> subClass, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {
|
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);
|
return mBase.newInstanceSpecial(constructor, subClass, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void log(@NonNull String message) {
|
public final void log(@NonNull String message) {
|
||||||
|
ensureAttached();
|
||||||
mBase.log(message);
|
mBase.log(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void log(@NonNull String message, @NonNull Throwable throwable) {
|
public final void log(@NonNull String message, @NonNull Throwable throwable) {
|
||||||
|
ensureAttached();
|
||||||
mBase.log(message, throwable);
|
mBase.log(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public final DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException {
|
public final DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException {
|
||||||
|
ensureAttached();
|
||||||
return mBase.parseDex(dexData, includeAnnotations);
|
return mBase.parseDex(dexData, includeAnnotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SharedPreferences getRemotePreferences(@NonNull String name) {
|
public SharedPreferences getRemotePreferences(@NonNull String name) {
|
||||||
|
ensureAttached();
|
||||||
return mBase.getRemotePreferences(name);
|
return mBase.getRemotePreferences(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ApplicationInfo getApplicationInfo() {
|
public ApplicationInfo getApplicationInfo() {
|
||||||
|
ensureAttached();
|
||||||
return mBase.getApplicationInfo();
|
return mBase.getApplicationInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String[] listRemoteFiles() {
|
public String[] listRemoteFiles() {
|
||||||
|
ensureAttached();
|
||||||
return mBase.listRemoteFiles();
|
return mBase.listRemoteFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor openRemoteFile(@NonNull String name) throws FileNotFoundException {
|
public ParcelFileDescriptor openRemoteFile(@NonNull String name) throws FileNotFoundException {
|
||||||
|
ensureAttached();
|
||||||
return mBase.openRemoteFile(name);
|
return mBase.openRemoteFile(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,19 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Super class which all Xposed module entry classes should extend.<br/>
|
* 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")
|
@SuppressWarnings("unused")
|
||||||
public abstract class XposedModule extends XposedInterfaceWrapper implements XposedModuleInterface {
|
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/>
|
* Instantiates a new Xposed module.<br/>
|
||||||
* When the module is loaded into the target process, the constructor will be called.
|
* 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 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
|
* @param param Information about the process in which the module is loaded
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public XposedModule(@NonNull XposedInterface base, @NonNull ModuleLoadedParam param) {
|
public XposedModule(@NonNull XposedInterface base, @NonNull ModuleLoadedParam param) {
|
||||||
super(base);
|
this();
|
||||||
|
attachFramework(base);
|
||||||
|
onModuleLoaded(param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package io.github.libxposed.api;
|
package io.github.libxposed.api;
|
||||||
|
|
||||||
|
import android.app.AppComponentFactory;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@ public interface XposedModuleInterface {
|
||||||
/**
|
/**
|
||||||
* Wraps information about system server.
|
* Wraps information about system server.
|
||||||
*/
|
*/
|
||||||
interface SystemServerLoadedParam {
|
interface SystemServerStartingParam {
|
||||||
/**
|
/**
|
||||||
* Gets the class loader of system server.
|
* Gets the class loader of system server.
|
||||||
*
|
*
|
||||||
|
|
@ -44,6 +45,13 @@ public interface XposedModuleInterface {
|
||||||
ClassLoader getClassLoader();
|
ClassLoader getClassLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Kept for API100 compatibility.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
interface SystemServerLoadedParam extends SystemServerStartingParam {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps information about the package being loaded.
|
* Wraps information about the package being loaded.
|
||||||
*/
|
*/
|
||||||
|
|
@ -73,6 +81,27 @@ public interface XposedModuleInterface {
|
||||||
@NonNull
|
@NonNull
|
||||||
ClassLoader getDefaultClassLoader();
|
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.
|
* Gets the class loader of the package being loaded.
|
||||||
*
|
*
|
||||||
|
|
@ -82,11 +111,17 @@ public interface XposedModuleInterface {
|
||||||
ClassLoader getClassLoader();
|
ClassLoader getClassLoader();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets information about whether is this package the first and main package of the app process.
|
* Gets the app component factory of the package being loaded.
|
||||||
*
|
|
||||||
* @return {@code true} if this is the first package.
|
|
||||||
*/
|
*/
|
||||||
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
|
* @param param Information about system server
|
||||||
*/
|
*/
|
||||||
default void onSystemServerLoaded(@NonNull SystemServerLoadedParam param) {
|
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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package io.github.libxposed.api.annotations;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
public @interface AfterInvocation {
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package io.github.libxposed.api.annotations;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
public @interface BeforeInvocation {
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package io.github.libxposed.api.annotations;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target({ElementType.TYPE, ElementType.TYPE_USE})
|
|
||||||
public @interface XposedHooker {
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
tasks.register("Delete", Delete::class) {
|
tasks.register("Delete", Delete::class) {
|
||||||
delete(rootProject.buildDir)
|
delete(rootProject.layout.buildDirectory)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_21
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_21
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
[versions]
|
[versions]
|
||||||
kotlin = "1.9.10"
|
annotation = "1.8.0"
|
||||||
lint = "31.1.2"
|
kotlin = "2.0.0"
|
||||||
agp = "8.1.2"
|
lint = "31.5.1"
|
||||||
|
agp = "8.5.1"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
agp-lib = { id = "com.android.library", version.ref = "agp" }
|
agp-lib = { id = "com.android.library", version.ref = "agp" }
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
||||||
lint-api = { module = "com.android.tools.lint:lint-api", version.ref = "lint" }
|
lint-api = { module = "com.android.tools.lint:lint-api", version.ref = "lint" }
|
||||||
lint-checks = { module = "com.android.tools.lint:lint-checks", version.ref = "lint" }
|
lint-checks = { module = "com.android.tools.lint:lint-checks", version.ref = "lint" }
|
||||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,6 +1,7 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
|
@ -55,7 +57,7 @@
|
||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|
@ -83,7 +85,9 @@ done
|
||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|
@ -130,18 +134,21 @@ location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
|
|
@ -149,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
|
|
@ -198,11 +205,11 @@ fi
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command:
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# and any embedded shellness will be escaped.
|
||||||
# double quotes to make sure that they get re-expanded; and
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|
@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue