From 128ead6aab8861c73ae739719af669f373023e7a Mon Sep 17 00:00:00 2001 From: Nullptr Date: Mon, 28 Oct 2024 15:58:54 +0100 Subject: [PATCH 01/22] Update deps --- gradle/libs.versions.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 60cbcd5..2254578 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -annotation = "1.8.0" -kotlin = "2.0.0" -lint = "31.5.1" -agp = "8.5.1" +annotation = "1.9.0" +kotlin = "2.0.21" +lint = "31.7.1" +agp = "8.7.1" [plugins] kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } From 7d83e02b3b739dc9178df0930d3f7cf1b197e6de Mon Sep 17 00:00:00 2001 From: Nullptr Date: Mon, 28 Oct 2024 16:04:49 +0100 Subject: [PATCH 02/22] Rename MethodUnhooker to HookHandle, add invoke method --- .../github/libxposed/api/XposedInterface.java | 49 +++++++++++-------- .../libxposed/api/XposedInterfaceWrapper.java | 12 ++--- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 3c4ac86..062a935 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -211,17 +211,29 @@ interface Hooker { } /** - * Interface for canceling a hook. + * Handle for a hook. * * @param {@link Method} or {@link Constructor} */ - interface MethodUnhooker { + interface HookHandle { /** * Gets the method or constructor being hooked. */ @NonNull T getOrigin(); + /** + * Similar to {@link Method#invoke(Object, Object...)}, but skips Xposed hooks with lower priority. + * + * @param method The method or constructor to be called + * @param thisObject For non-static calls, the {@code this} pointer, otherwise {@code null} + * @param args The arguments used for the method call + * @return The result returned from the invoked method + * @see Method#invoke(Object, Object...) + */ + @Nullable + Object invoke(@NonNull T method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; + /** * Cancels the hook. The behavior of calling this method multiple times is undefined. */ @@ -263,13 +275,13 @@ interface MethodUnhooker { * * @param origin The method to be hooked * @param hooker The hooker class - * @return Unhooker for canceling the hook + * @return Handle for the hook * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, * or hooker is invalid * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - MethodUnhooker hook(@NonNull Method origin, @NonNull Class hooker); + HookHandle hook(@NonNull Method origin, @NonNull Class hooker); /** * Hook the static initializer of a class with default priority. @@ -279,12 +291,12 @@ interface MethodUnhooker { * * @param origin The class to be hooked * @param hooker The hooker class - * @return Unhooker for canceling the hook + * @return Handle for 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 - MethodUnhooker> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker); + HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker); /** * Hook the static initializer of a class with specified priority. @@ -295,12 +307,12 @@ interface MethodUnhooker { * @param origin The class to be hooked * @param priority The hook priority * @param hooker The hooker class - * @return Unhooker for canceling the hook + * @return Handle for 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 - MethodUnhooker> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); + HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); /** * Hook a method with specified priority. @@ -308,13 +320,13 @@ interface MethodUnhooker { * @param origin The method to be hooked * @param priority The hook priority * @param hooker The hooker class - * @return Unhooker for canceling the hook + * @return Handle for the hook * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, * or hooker is invalid * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - MethodUnhooker hook(@NonNull Method origin, int priority, @NonNull Class hooker); + HookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker); /** * Hook a constructor with default priority. @@ -322,13 +334,13 @@ interface MethodUnhooker { * @param The type of the constructor * @param origin The constructor to be hooked * @param hooker The hooker class - * @return Unhooker for canceling the hook + * @return Handle for the hook * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, * or hooker is invalid * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - MethodUnhooker> hook(@NonNull Constructor origin, @NonNull Class hooker); + HookHandle> hook(@NonNull Constructor origin, @NonNull Class hooker); /** * Hook a constructor with specified priority. @@ -337,13 +349,13 @@ interface MethodUnhooker { * @param origin The constructor to be hooked * @param priority The hook priority * @param hooker The hooker class - * @return Unhooker for canceling the hook + * @return Handle for the hook * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, * or hooker is invalid * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - MethodUnhooker> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker); + HookHandle> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker); /** * Deoptimizes a method in case hooked callee is not called because of inline. @@ -374,8 +386,7 @@ interface MethodUnhooker { boolean deoptimize(@NonNull Constructor constructor); /** - * Basically the same as {@link Method#invoke(Object, Object...)}, but calls the original method - * as it was before the interception by Xposed. + * Basically the same as {@link Method#invoke(Object, Object...)}, but skips all Xposed hooks. * * @param method The method to be called * @param thisObject For non-static calls, the {@code this} pointer, otherwise {@code null} @@ -387,8 +398,7 @@ interface MethodUnhooker { 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. + * Basically the same as {@link Constructor#newInstance(Object...)}, but skips all Xposed hooks. * * @param constructor The constructor to create and initialize a new instance * @param thisObject The instance to be constructed @@ -431,8 +441,7 @@ interface MethodUnhooker { void invokeSpecial(@NonNull Constructor constructor, @NonNull T 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. + * Basically the same as {@link Constructor#newInstance(Object...)}, but skips all Xposed hooks. * * @param The type of the constructor * @param constructor The constructor to create and initialize a new instance diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 425596f..630024a 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -51,37 +51,37 @@ public final int getFrameworkPrivilege() { @NonNull @Override - public final MethodUnhooker hook(@NonNull Method origin, @NonNull Class hooker) { + public final HookHandle hook(@NonNull Method origin, @NonNull Class hooker) { return mBase.hook(origin, hooker); } @NonNull @Override - public MethodUnhooker> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker) { + public HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker) { return mBase.hookClassInitializer(origin, hooker); } @NonNull @Override - public MethodUnhooker> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { + public HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { return mBase.hookClassInitializer(origin, priority, hooker); } @NonNull @Override - public final MethodUnhooker hook(@NonNull Method origin, int priority, @NonNull Class hooker) { + public final HookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } @NonNull @Override - public final MethodUnhooker> hook(@NonNull Constructor origin, @NonNull Class hooker) { + public final HookHandle> hook(@NonNull Constructor origin, @NonNull Class hooker) { return mBase.hook(origin, hooker); } @NonNull @Override - public final MethodUnhooker> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { + public final HookHandle> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } From 854af941469a69067929920311f1c9049af5a408 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Mon, 28 Oct 2024 16:07:55 +0100 Subject: [PATCH 03/22] Reorder methods --- .../github/libxposed/api/XposedInterface.java | 86 +++++++++---------- .../libxposed/api/XposedInterfaceWrapper.java | 30 +++---- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 062a935..a846f7b 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -283,37 +283,6 @@ interface HookHandle { @NonNull HookHandle hook(@NonNull Method origin, @NonNull Class hooker); - /** - * Hook the static initializer of a class with default priority. - *

- * Note: If the class is initialized, the hook will never be called. - *

- * - * @param origin The class to be hooked - * @param hooker The hooker class - * @return Handle for 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 - HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker); - - /** - * Hook the static initializer of a class with specified priority. - *

- * Note: If the class is initialized, the hook will never be called. - *

- * - * @param origin The class to be hooked - * @param priority The hook priority - * @param hooker The hooker class - * @return Handle for 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 - HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); - /** * Hook a method with specified priority. * @@ -357,6 +326,37 @@ interface HookHandle { @NonNull HookHandle> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker); + /** + * Hook the static initializer of a class with default priority. + *

+ * Note: If the class is initialized, the hook will never be called. + *

+ * + * @param origin The class to be hooked + * @param hooker The hooker class + * @return Handle for 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 + HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker); + + /** + * Hook the static initializer of a class with specified priority. + *

+ * Note: If the class is initialized, the hook will never be called. + *

+ * + * @param origin The class to be hooked + * @param priority The hook priority + * @param hooker The hooker class + * @return Handle for 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 + HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); + /** * Deoptimizes a method in case hooked callee is not called because of inline. * @@ -408,6 +408,18 @@ interface HookHandle { */ void invokeOrigin(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; + /** + * Basically the same as {@link Constructor#newInstance(Object...)}, but skips all Xposed hooks. + * + * @param The type of the constructor + * @param constructor The constructor to create and initialize a new instance + * @param args The arguments used for the construction + * @return The instance created and initialized by the constructor + * @see Constructor#newInstance(Object...) + */ + @NonNull + T newInstanceOrigin(@NonNull Constructor constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException; + /** * Invokes a special (non-virtual) method on a given object instance, similar to the functionality of * {@code CallNonVirtualMethod} in JNI, which invokes an instance (nonstatic) method on a Java @@ -440,18 +452,6 @@ interface HookHandle { */ void invokeSpecial(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; - /** - * Basically the same as {@link Constructor#newInstance(Object...)}, but skips all Xposed hooks. - * - * @param The type of the constructor - * @param constructor The constructor to create and initialize a new instance - * @param args The arguments used for the construction - * @return The instance created and initialized by the constructor - * @see Constructor#newInstance(Object...) - */ - @NonNull - T newInstanceOrigin(@NonNull Constructor constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException; - /** * Creates a new instance of the given subclass, but initialize it with a parent constructor. This could * leave the object in an invalid state, where the subclass constructor are not called and the fields diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 630024a..8a19c1a 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -57,32 +57,32 @@ public final HookHandle hook(@NonNull Method origin, @NonNull Class HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker) { - return mBase.hookClassInitializer(origin, hooker); + public final HookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { + return mBase.hook(origin, priority, hooker); } @NonNull @Override - public HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { - return mBase.hookClassInitializer(origin, priority, hooker); + public final HookHandle> hook(@NonNull Constructor origin, @NonNull Class hooker) { + return mBase.hook(origin, hooker); } @NonNull @Override - public final HookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { + public final HookHandle> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } @NonNull @Override - public final HookHandle> hook(@NonNull Constructor origin, @NonNull Class hooker) { - return mBase.hook(origin, hooker); + public HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker) { + return mBase.hookClassInitializer(origin, hooker); } @NonNull @Override - public final HookHandle> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { - return mBase.hook(origin, priority, hooker); + public HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { + return mBase.hookClassInitializer(origin, priority, hooker); } @Override @@ -106,6 +106,12 @@ public void invokeOrigin(@NonNull Constructor constructor, @NonNull T thi mBase.invokeOrigin(constructor, thisObject, args); } + @NonNull + @Override + public final T newInstanceOrigin(@NonNull Constructor constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException { + return mBase.newInstanceOrigin(constructor, args); + } + @Nullable @Override public final Object invokeSpecial(@NonNull Method method, @NonNull Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { @@ -117,12 +123,6 @@ public void invokeSpecial(@NonNull Constructor constructor, @NonNull T th mBase.invokeSpecial(constructor, thisObject, args); } - @NonNull - @Override - public final T newInstanceOrigin(@NonNull Constructor constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException { - return mBase.newInstanceOrigin(constructor, args); - } - @NonNull @Override public final U newInstanceSpecial(@NonNull Constructor constructor, @NonNull Class subClass, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException { From a69f70789b56c782d488a0328729016d734c1945 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Mon, 28 Oct 2024 16:50:36 +0100 Subject: [PATCH 04/22] Rename `getOrigin` to `getMember` --- .../io/github/libxposed/api/XposedInterface.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index a846f7b..d8cb8dd 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -64,7 +64,7 @@ public interface XposedInterface { */ interface BeforeHookCallback { /** - * Gets the method / constructor to be hooked. + * Gets the method / constructor being hooked. */ @NonNull Member getMember(); @@ -104,7 +104,7 @@ interface BeforeHookCallback { */ interface AfterHookCallback { /** - * Gets the method / constructor to be hooked. + * Gets the method / constructor being hooked. */ @NonNull Member getMember(); @@ -217,22 +217,22 @@ interface Hooker { */ interface HookHandle { /** - * Gets the method or constructor being hooked. + * Gets the method / constructor being hooked. */ @NonNull - T getOrigin(); + T getMember(); /** * Similar to {@link Method#invoke(Object, Object...)}, but skips Xposed hooks with lower priority. * - * @param method The method or constructor to be called + * @param member The method / constructor to be called * @param thisObject For non-static calls, the {@code this} pointer, otherwise {@code null} * @param args The arguments used for the method call * @return The result returned from the invoked method * @see Method#invoke(Object, Object...) */ @Nullable - Object invoke(@NonNull T method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; + Object invoke(@NonNull T member, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; /** * Cancels the hook. The behavior of calling this method multiple times is undefined. From 6767f01b2412a81d47fbc07e1d026ff0602ee4ac Mon Sep 17 00:00:00 2001 From: Nullptr Date: Tue, 29 Oct 2024 10:16:53 +0100 Subject: [PATCH 05/22] Set minSdk = 26 and refine interface --- api/build.gradle.kts | 2 +- .../github/libxposed/api/XposedInterface.java | 85 ++++--------------- .../libxposed/api/XposedInterfaceWrapper.java | 26 ++---- 3 files changed, 23 insertions(+), 90 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index fc05e88..8896e9f 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -10,7 +10,7 @@ android { buildToolsVersion = "35.0.0" defaultConfig { - minSdk = 24 + minSdk = 26 consumerProguardFiles("proguard-rules.pro") } diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index d8cb8dd..ef6713b 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -10,8 +10,8 @@ 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; @@ -62,12 +62,12 @@ public interface XposedInterface { /** * Contextual interface for before invocation callbacks. */ - interface BeforeHookCallback { + interface BeforeHookCallback { /** * Gets the method / constructor being hooked. */ @NonNull - Member getMember(); + T getExecutable(); /** * Gets the {@code this} object, or {@code null} if the method is static. @@ -102,12 +102,12 @@ interface BeforeHookCallback { /** * Contextual interface for after invocation callbacks. */ - interface AfterHookCallback { + interface AfterHookCallback { /** * Gets the method / constructor being hooked. */ @NonNull - Member getMember(); + T getExecutable(); /** * Gets the {@code this} object, or {@code null} if the method is static. @@ -215,24 +215,12 @@ interface Hooker { * * @param {@link Method} or {@link Constructor} */ - interface HookHandle { + interface HookHandle { /** * Gets the method / constructor being hooked. */ @NonNull - T getMember(); - - /** - * Similar to {@link Method#invoke(Object, Object...)}, but skips Xposed hooks with lower priority. - * - * @param member The method / constructor to be called - * @param thisObject For non-static calls, the {@code this} pointer, otherwise {@code null} - * @param args The arguments used for the method call - * @return The result returned from the invoked method - * @see Method#invoke(Object, Object...) - */ - @Nullable - Object invoke(@NonNull T member, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; + T getExecutable(); /** * Cancels the hook. The behavior of calling this method multiple times is undefined. @@ -271,9 +259,9 @@ interface HookHandle { int getFrameworkPrivilege(); /** - * Hook a method with default priority. + * Hook a method / constructor with default priority. * - * @param origin The method to be hooked + * @param origin The method / constructor to be hooked * @param hooker The hooker class * @return Handle for the hook * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, @@ -281,12 +269,12 @@ interface HookHandle { * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - HookHandle hook(@NonNull Method origin, @NonNull Class hooker); + HookHandle hook(@NonNull T origin, @NonNull Class hooker); /** - * Hook a method with specified priority. + * Hook a method / constructor with specified priority. * - * @param origin The method to be hooked + * @param origin The method / constructor to be hooked * @param priority The hook priority * @param hooker The hooker class * @return Handle for the hook @@ -295,36 +283,7 @@ interface HookHandle { * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - HookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker); - - /** - * Hook a constructor with default priority. - * - * @param The type of the constructor - * @param origin The constructor to be hooked - * @param hooker The hooker class - * @return Handle for the hook - * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, - * or hooker is invalid - * @throws HookFailedError if hook fails due to framework internal error - */ - @NonNull - HookHandle> hook(@NonNull Constructor origin, @NonNull Class hooker); - - /** - * Hook a constructor with specified priority. - * - * @param The type of the constructor - * @param origin The constructor to be hooked - * @param priority The hook priority - * @param hooker The hooker class - * @return Handle for the hook - * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, - * or hooker is invalid - * @throws HookFailedError if hook fails due to framework internal error - */ - @NonNull - HookHandle> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker); + HookHandle hook(@NonNull T origin, int priority, @NonNull Class hooker); /** * Hook the static initializer of a class with default priority. @@ -358,7 +317,7 @@ interface HookHandle { HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); /** - * Deoptimizes a method in case hooked callee is not called because of inline. + * Deoptimizes a method / constructor in case hooked callee is not called because of inline. * *

By deoptimizing the method, the method will back all callee without inlining. * For example, when a short hooked method B is invoked by method A, the callback to B is not invoked @@ -370,20 +329,10 @@ interface HookHandle { * the deoptimized callers are all you need. Otherwise, it would be better to change the hook point or * to deoptimize the whole app manually (by simply reinstalling the app without uninstall).

* - * @param method The method to deoptimize - * @return Indicate whether the deoptimizing succeed or not - */ - boolean deoptimize(@NonNull Method method); - - /** - * Deoptimizes a constructor in case hooked callee is not called because of inline. - * - * @param The type of the constructor - * @param constructor The constructor to deoptimize + * @param executable The method to deoptimize * @return Indicate whether the deoptimizing succeed or not - * @see #deoptimize(Method) */ - boolean deoptimize(@NonNull Constructor constructor); + boolean deoptimize(@NonNull Executable executable); /** * Basically the same as {@link Method#invoke(Object, Object...)}, but skips all Xposed hooks. @@ -398,7 +347,7 @@ interface HookHandle { Object invokeOrigin(@NonNull Method method, @Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; /** - * Basically the same as {@link Constructor#newInstance(Object...)}, but skips all Xposed hooks. + * Invoke the constructor as a method, but skips all Xposed hooks. * * @param constructor The constructor to create and initialize a new instance * @param thisObject The instance to be constructed diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 8a19c1a..e3099a2 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -10,6 +10,7 @@ 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; @@ -51,25 +52,13 @@ public final int getFrameworkPrivilege() { @NonNull @Override - public final HookHandle hook(@NonNull Method origin, @NonNull Class hooker) { + public final HookHandle hook(@NonNull T origin, @NonNull Class hooker) { return mBase.hook(origin, hooker); } @NonNull @Override - public final HookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { - return mBase.hook(origin, priority, hooker); - } - - @NonNull - @Override - public final HookHandle> hook(@NonNull Constructor origin, @NonNull Class hooker) { - return mBase.hook(origin, hooker); - } - - @NonNull - @Override - public final HookHandle> hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { + public final HookHandle hook(@NonNull T origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } @@ -86,13 +75,8 @@ public HookHandle> hookClassInitializer(@NonNull Class ori } @Override - public final boolean deoptimize(@NonNull Method method) { - return mBase.deoptimize(method); - } - - @Override - public final boolean deoptimize(@NonNull Constructor constructor) { - return mBase.deoptimize(constructor); + public final boolean deoptimize(@NonNull Executable executable) { + return mBase.deoptimize(executable); } @Nullable From bea4b68d5f7c1396561c9d011edca647ddd5859e Mon Sep 17 00:00:00 2001 From: Nullptr Date: Tue, 29 Oct 2024 10:27:48 +0100 Subject: [PATCH 06/22] Remove default priority hook --- .../github/libxposed/api/XposedInterface.java | 28 ------------------- .../libxposed/api/XposedInterfaceWrapper.java | 12 -------- 2 files changed, 40 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index ef6713b..12bdb69 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -258,19 +258,6 @@ interface HookHandle { */ int getFrameworkPrivilege(); - /** - * Hook a method / constructor with default priority. - * - * @param origin The method / constructor to be hooked - * @param hooker The hooker class - * @return Handle for the hook - * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, - * or hooker is invalid - * @throws HookFailedError if hook fails due to framework internal error - */ - @NonNull - HookHandle hook(@NonNull T origin, @NonNull Class hooker); - /** * Hook a method / constructor with specified priority. * @@ -285,21 +272,6 @@ interface HookHandle { @NonNull HookHandle hook(@NonNull T origin, int priority, @NonNull Class hooker); - /** - * Hook the static initializer of a class with default priority. - *

- * Note: If the class is initialized, the hook will never be called. - *

- * - * @param origin The class to be hooked - * @param hooker The hooker class - * @return Handle for 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 - HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker); - /** * Hook the static initializer of a class with specified priority. *

diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index e3099a2..169a8d8 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -50,24 +50,12 @@ public final int getFrameworkPrivilege() { return mBase.getFrameworkPrivilege(); } - @NonNull - @Override - public final HookHandle hook(@NonNull T origin, @NonNull Class hooker) { - return mBase.hook(origin, hooker); - } - @NonNull @Override public final HookHandle hook(@NonNull T origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } - @NonNull - @Override - public HookHandle> hookClassInitializer(@NonNull Class origin, @NonNull Class hooker) { - return mBase.hookClassInitializer(origin, hooker); - } - @NonNull @Override public HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { From 83cdd40c8bc4daf10e666d8340420d558192a864 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Tue, 29 Oct 2024 10:33:46 +0100 Subject: [PATCH 07/22] Add type in example --- .../java/io/github/libxposed/api/XposedInterface.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 12bdb69..a2bc870 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -185,23 +185,23 @@ interface AfterHookCallback { *

{@code
      *   public class ExampleHooker implements Hooker {
      *
-     *       public static void before(@NonNull BeforeHookCallback callback) {
+     *       public static void before(@NonNull BeforeHookCallback callback) {
      *           // Pre-hooking logic goes here
      *       }
      *
-     *       public static void after(@NonNull AfterHookCallback callback) {
+     *       public static void after(@NonNull AfterHookCallback callback) {
      *           // Post-hooking logic goes here
      *       }
      *   }
      *
      *   public class ExampleHookerWithContext implements Hooker {
      *
-     *       public static MyContext before(@NonNull BeforeHookCallback callback) {
+     *       public static MyContext before(@NonNull BeforeHookCallback callback) {
      *           // Pre-hooking logic goes here
      *           return new MyContext();
      *       }
      *
-     *       public static void after(@NonNull AfterHookCallback callback, MyContext context) {
+     *       public static void after(@NonNull AfterHookCallback callback, MyContext context) {
      *           // Post-hooking logic goes here
      *       }
      *   }

From 00865a09218d075f5a78dc94707f8983ebfddd07 Mon Sep 17 00:00:00 2001
From: Nullptr 
Date: Fri, 1 Nov 2024 15:50:50 +0100
Subject: [PATCH 08/22] Move onModuleLoaded out of constructor

---
 .../libxposed/api/XposedInterfaceWrapper.java     | 12 +++++++++---
 .../io/github/libxposed/api/XposedModule.java     | 15 ++-------------
 .../libxposed/api/XposedModuleInterface.java      | 11 +++++++++++
 3 files changed, 22 insertions(+), 16 deletions(-)

diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
index 169a8d8..2441b75 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java
@@ -22,9 +22,15 @@
  */
 public class XposedInterfaceWrapper implements XposedInterface {
 
-    private final XposedInterface mBase;
-
-    XposedInterfaceWrapper(@NonNull XposedInterface base) {
+    private XposedInterface mBase;
+
+    /**
+     * Attaches the framework interface to the module. Modules should never call this method.
+     *
+     * @param base The framework interface
+     */
+    @SuppressWarnings("unused")
+    public final void attachFramework(@NonNull XposedInterface base) {
         mBase = base;
     }
 
diff --git a/api/src/main/java/io/github/libxposed/api/XposedModule.java b/api/src/main/java/io/github/libxposed/api/XposedModule.java
index b2e1a03..475a49c 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedModule.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedModule.java
@@ -1,21 +1,10 @@
 package io.github.libxposed.api;
 
-import androidx.annotation.NonNull;
-
 /**
  * Super class which all Xposed module entry classes should extend.
- * Entry classes will be instantiated exactly once for each process. + * Entry classes will be instantiated exactly once for each process. Modules should not do initialization + * work before {@link #onModuleLoaded(ModuleLoadedParam)} is called. */ @SuppressWarnings("unused") public abstract class XposedModule extends XposedInterfaceWrapper implements XposedModuleInterface { - /** - * Instantiates a new Xposed module.
- * When the module is loaded into the target process, the constructor will be called. - * - * @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 - */ - public XposedModule(@NonNull XposedInterface base, @NonNull ModuleLoadedParam param) { - super(base); - } } diff --git a/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java b/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java index 1cb548c..ef51b45 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java @@ -1,5 +1,6 @@ package io.github.libxposed.api; +import android.app.AppComponentFactory; import android.content.pm.ApplicationInfo; import android.os.Build; @@ -89,6 +90,16 @@ interface PackageLoadedParam { boolean isFirstPackage(); } + /** + * Gets notified when the module is loaded into the target process.
+ * This callback is guaranteed to be called exactly once for a process before + * {@link AppComponentFactory} is created. + * + * @param param Information about the process in which the module is loaded + */ + default void onModuleLoaded(@NonNull ModuleLoadedParam param) { + } + /** * Gets notified when a package is loaded into the app process.
* This callback could be invoked multiple times for the same process on each package. From 656f5ca268cba4fa6bd2c2cac2de52b32baf5d1c Mon Sep 17 00:00:00 2001 From: Nullptr Date: Fri, 20 Feb 2026 21:32:48 +0100 Subject: [PATCH 09/22] Refine HookHandle --- .../github/libxposed/api/XposedInterface.java | 66 +++++++++++++++++-- .../libxposed/api/XposedInterfaceWrapper.java | 10 ++- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index e250e95..1daafc6 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -228,6 +228,47 @@ interface HookHandle { void unhook(); } + /** + * Handle for a method hook. + */ + interface MethodHookHandle extends HookHandle { + /** + * Invoke the original method, but keeps all higher priority hooks. + * + * @param thisObject For non-static calls, the {@code this} pointer, otherwise {@code null} + * @param args The arguments used for the method call + * @return The result returned from the invoked method + * @see Method#invoke(Object, Object...) + */ + @Nullable + Object invokeOrigin(@Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; + } + + /** + * Handle for a constructor hook. + * + * @param The type of the constructor + */ + interface CtorHookHandle extends HookHandle> { + /** + * Invoke the original constructor as a method, but keeps all higher priority hooks. + * + * @param thisObject The instance to be constructed + * @param args The arguments used for the construction + * @see Constructor#newInstance(Object...) + */ + void invokeOrigin(@Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; + + /** + * Invoke the original constructor, but keeps all higher priority hooks. + * + * @param args The arguments used for the construction + * @return The instance created and initialized by the constructor + * @see Constructor#newInstance(Object...) + */ + T newInstanceOrigin(Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException; + } + /** * Gets the Xposed framework name of current implementation. * @@ -259,9 +300,9 @@ interface HookHandle { int getFrameworkPrivilege(); /** - * Hook a method / constructor with specified priority. + * Hook a method with specified priority. * - * @param origin The method / constructor to be hooked + * @param origin The method to be hooked * @param priority The hook priority * @param hooker The hooker class * @return Handle for the hook @@ -270,7 +311,21 @@ interface HookHandle { * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - HookHandle hook(@NonNull T origin, int priority, @NonNull Class hooker); + MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker); + + /** + * Hook a constructor with specified priority. + * + * @param origin The constructor to be hooked + * @param priority The hook priority + * @param hooker The hooker class + * @return Handle for the hook + * @throws IllegalArgumentException if origin is framework internal or {@link Constructor#newInstance}, + * or hooker is invalid + * @throws HookFailedError if hook fails due to framework internal error + */ + @NonNull + CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Class hooker); /** * Hook the static initializer of a class with specified priority. @@ -286,7 +341,7 @@ interface HookHandle { * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); + MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); /** * Deoptimizes a method / constructor in case hooked callee is not called because of inline. @@ -308,6 +363,7 @@ interface HookHandle { /** * Basically the same as {@link Method#invoke(Object, Object...)}, but skips all Xposed hooks. + * If you do not want to skip higher priority hooks, use {@link MethodHookHandle#invokeOrigin(Object, Object...)} instead. * * @param method The method to be called * @param thisObject For non-static calls, the {@code this} pointer, otherwise {@code null} @@ -320,6 +376,7 @@ interface HookHandle { /** * Invoke the constructor as a method, but skips all Xposed hooks. + * If you do not want to skip higher priority hooks, use {@link CtorHookHandle#invokeOrigin(Object, Object...)} instead. * * @param constructor The constructor to create and initialize a new instance * @param thisObject The instance to be constructed @@ -331,6 +388,7 @@ interface HookHandle { /** * Basically the same as {@link Constructor#newInstance(Object...)}, but skips all Xposed hooks. + * If you do not want to skip higher priority hooks, use {@link CtorHookHandle#newInstanceOrigin(Object...)} instead. * * @param The type of the constructor * @param constructor The constructor to create and initialize a new instance diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index ac5caba..90431ef 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -58,13 +58,19 @@ public final int getFrameworkPrivilege() { @NonNull @Override - public final HookHandle hook(@NonNull T origin, int priority, @NonNull Class hooker) { + public MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } @NonNull @Override - public HookHandle> hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { + public CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { + return mBase.hook(origin, priority, hooker); + } + + @NonNull + @Override + public MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { return mBase.hookClassInitializer(origin, priority, hooker); } From 7ce14b8895bb14dcb9b4b5d0da882c8fc92d974c Mon Sep 17 00:00:00 2001 From: Nullptr Date: Fri, 20 Feb 2026 21:33:16 +0100 Subject: [PATCH 10/22] RFC: Remove deprecated api --- .../github/libxposed/api/XposedInterface.java | 20 ------------------- .../libxposed/api/XposedInterfaceWrapper.java | 10 ---------- 2 files changed, 30 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 1daafc6..1d56e4f 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -459,26 +459,6 @@ interface CtorHookHandle extends HookHandle> { */ void log(int priority, @Nullable String tag, @NonNull String msg, @Nullable Throwable tr); - /** - * Writes a message to the Xposed log. - * @deprecated Use {@link #log(int, String, String, Throwable)} instead. - * This method is kept for compatibility with old hooker classes and will be removed in first release version. - * - * @param message The log message - */ - @Deprecated - void log(@NonNull String message); - - /** - * Writes a message with a stack trace to the Xposed log. - * @deprecated Use {@link #log(int, String, String, Throwable)} instead. - * - * @param message The log message - * @param throwable The Throwable object for the stack trace - */ - @Deprecated - void log(@NonNull String message, @NonNull Throwable throwable); - /** * Parse a dex file in memory. * diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 90431ef..92383f4 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -118,16 +118,6 @@ public final void log(int priority, @Nullable String tag, @NonNull String msg, @ mBase.log(priority, tag, msg, tr); } - @Override - public final void log(@NonNull String message) { - mBase.log(message); - } - - @Override - public final void log(@NonNull String message, @NonNull Throwable throwable) { - mBase.log(message, throwable); - } - @Nullable @Override public final DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException { From 4f6cc32a0e1bca25fdd5bcd67c0a5aecbb2870e6 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Fri, 20 Feb 2026 21:49:29 +0100 Subject: [PATCH 11/22] RFC: Make wrapper methods final --- .../libxposed/api/XposedInterfaceWrapper.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 92383f4..01928fd 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -58,19 +58,19 @@ public final int getFrameworkPrivilege() { @NonNull @Override - public MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { + public final MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } @NonNull @Override - public CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { + public final CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { return mBase.hook(origin, priority, hooker); } @NonNull @Override - public MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { + public final MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { return mBase.hookClassInitializer(origin, priority, hooker); } @@ -86,7 +86,7 @@ public final Object invokeOrigin(@NonNull Method method, @Nullable Object thisOb } @Override - public void invokeOrigin(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { + public final void invokeOrigin(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { mBase.invokeOrigin(constructor, thisObject, args); } @@ -103,7 +103,7 @@ public final Object invokeSpecial(@NonNull Method method, @NonNull Object thisOb } @Override - public void invokeSpecial(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { + public final void invokeSpecial(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { mBase.invokeSpecial(constructor, thisObject, args); } @@ -126,25 +126,25 @@ public final DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnno @NonNull @Override - public SharedPreferences getRemotePreferences(@NonNull String name) { + public final SharedPreferences getRemotePreferences(@NonNull String name) { return mBase.getRemotePreferences(name); } @NonNull @Override - public ApplicationInfo getApplicationInfo() { + public final ApplicationInfo getApplicationInfo() { return mBase.getApplicationInfo(); } @NonNull @Override - public String[] listRemoteFiles() { + public final String[] listRemoteFiles() { return mBase.listRemoteFiles(); } @NonNull @Override - public ParcelFileDescriptor openRemoteFile(@NonNull String name) throws FileNotFoundException { + public final ParcelFileDescriptor openRemoteFile(@NonNull String name) throws FileNotFoundException { return mBase.openRemoteFile(name); } } From bc0bd04faefe0ad871b07facadb41a907b329dba Mon Sep 17 00:00:00 2001 From: Nullptr Date: Fri, 20 Feb 2026 21:54:05 +0100 Subject: [PATCH 12/22] Update api/src/main/java/io/github/libxposed/api/XposedInterface.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/src/main/java/io/github/libxposed/api/XposedInterface.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 1d56e4f..e9cfccc 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -257,7 +257,7 @@ interface CtorHookHandle extends HookHandle> { * @param args The arguments used for the construction * @see Constructor#newInstance(Object...) */ - void invokeOrigin(@Nullable Object thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; + void invokeOrigin(@NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException; /** * Invoke the original constructor, but keeps all higher priority hooks. From 4bcbd4bd42863dd2548e2c5210dfa1e4f8f23ccc Mon Sep 17 00:00:00 2001 From: Nullptr Date: Fri, 20 Feb 2026 22:16:21 +0100 Subject: [PATCH 13/22] RFC: Copilot suggestions --- .../github/libxposed/api/XposedInterface.java | 3 +- .../libxposed/api/XposedInterfaceWrapper.java | 29 +++++++++++++++++++ .../libxposed/api/XposedModuleInterface.java | 3 +- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index e9cfccc..4344102 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -266,6 +266,7 @@ interface CtorHookHandle extends HookHandle> { * @return The instance created and initialized by the constructor * @see Constructor#newInstance(Object...) */ + @NonNull T newInstanceOrigin(Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException; } @@ -356,7 +357,7 @@ interface CtorHookHandle extends HookHandle> { * the deoptimized callers are all you need. Otherwise, it would be better to change the hook point or * to deoptimize the whole app manually (by simply reinstalling the app without uninstall).

* - * @param executable The method to deoptimize + * @param executable The method / constructor to deoptimize * @return Indicate whether the deoptimizing succeed or not */ boolean deoptimize(@NonNull Executable executable); diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 01928fd..e14c33b 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -31,120 +31,149 @@ public class XposedInterfaceWrapper implements XposedInterface { */ @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 MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { + ensureAttached(); return mBase.hook(origin, priority, hooker); } @NonNull @Override public final CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { + ensureAttached(); return mBase.hook(origin, priority, hooker); } @NonNull @Override public final MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { + ensureAttached(); return mBase.hookClassInitializer(origin, priority, hooker); } @Override public final boolean deoptimize(@NonNull Executable executable) { + ensureAttached(); return mBase.deoptimize(executable); } @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 final void invokeOrigin(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { + ensureAttached(); mBase.invokeOrigin(constructor, thisObject, args); } @NonNull @Override public final T newInstanceOrigin(@NonNull Constructor constructor, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException { + ensureAttached(); return mBase.newInstanceOrigin(constructor, 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 final void invokeSpecial(@NonNull Constructor constructor, @NonNull T thisObject, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException { + ensureAttached(); mBase.invokeSpecial(constructor, thisObject, args); } @NonNull @Override public final U newInstanceSpecial(@NonNull Constructor constructor, @NonNull Class subClass, Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException { + ensureAttached(); return mBase.newInstanceSpecial(constructor, subClass, args); } @Override public final void log(int priority, @Nullable String tag, @NonNull String msg, @Nullable Throwable tr) { + ensureAttached(); mBase.log(priority, tag, msg, tr); } @Nullable @Override public final DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException { + ensureAttached(); return mBase.parseDex(dexData, includeAnnotations); } @NonNull @Override public final SharedPreferences getRemotePreferences(@NonNull String name) { + ensureAttached(); return mBase.getRemotePreferences(name); } @NonNull @Override public final ApplicationInfo getApplicationInfo() { + ensureAttached(); return mBase.getApplicationInfo(); } @NonNull @Override public final String[] listRemoteFiles() { + ensureAttached(); return mBase.listRemoteFiles(); } @NonNull @Override public final ParcelFileDescriptor openRemoteFile(@NonNull String name) throws FileNotFoundException { + ensureAttached(); return mBase.openRemoteFile(name); } } diff --git a/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java b/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java index ef51b45..941f5d1 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedModuleInterface.java @@ -1,6 +1,5 @@ package io.github.libxposed.api; -import android.app.AppComponentFactory; import android.content.pm.ApplicationInfo; import android.os.Build; @@ -93,7 +92,7 @@ interface PackageLoadedParam { /** * Gets notified when the module is loaded into the target process.
* This callback is guaranteed to be called exactly once for a process before - * {@link AppComponentFactory} is created. + * {@link android.app.AppComponentFactory} is created. * * @param param Information about the process in which the module is loaded */ From aa8751b98e4c3c2da97f85b7175eb2dd201bdde8 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Fri, 20 Feb 2026 23:30:12 +0100 Subject: [PATCH 14/22] RFC: Replace framework privilege with capabilities --- .../github/libxposed/api/XposedInterface.java | 24 ++++++++----------- .../libxposed/api/XposedInterfaceWrapper.java | 4 ++-- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 4344102..5a55fe4 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -29,22 +29,17 @@ public interface XposedInterface { int API = 100; /** - * Indicates that the framework is running as root. + * The framework has the capability to hook system_server and other system processes. */ - int FRAMEWORK_PRIVILEGE_ROOT = 0; + long CAP_SYSTEM = 1; /** - * Indicates that the framework is running in a container with a fake system_server. + * The framework provides remote preferences and remote files support. */ - int FRAMEWORK_PRIVILEGE_CONTAINER = 1; + long CAP_REMOTE = 1 << 1; /** - * Indicates that the framework is running as a different app, which may have at most shell permission. + * The framework allows dynamically loaded code to use Xposed APIs. */ - int FRAMEWORK_PRIVILEGE_APP = 2; - /** - * Indicates that the framework is embedded in the hooked app, - * which means {@link #getRemotePreferences} will be null and remote file is unsupported. - */ - int FRAMEWORK_PRIVILEGE_EMBEDDED = 3; + long CAP_RT_API_REFLECTION = 1 << 2; /** * The default hook priority. @@ -294,11 +289,12 @@ interface CtorHookHandle extends HookHandle> { long getFrameworkVersionCode(); /** - * Gets the Xposed framework privilege of current implementation. + * Gets the Xposed framework capabilities. + * Capabilities with prefix CAP_RT_ may change among launches. * - * @return Framework privilege + * @return Framework capabilities */ - int getFrameworkPrivilege(); + long getFrameworkCapabilities(); /** * Hook a method with specified priority. diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index e14c33b..9820fea 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -64,9 +64,9 @@ public final long getFrameworkVersionCode() { } @Override - public final int getFrameworkPrivilege() { + public final long getFrameworkCapabilities() { ensureAttached(); - return mBase.getFrameworkPrivilege(); + return mBase.getFrameworkCapabilities(); } @NonNull From ad48420018841b0369d567f892507c11dcf60e3d Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sat, 21 Feb 2026 07:50:04 +0100 Subject: [PATCH 15/22] RFC: Abandon the use of hooker class. Use callback object instead. The side effects of individual hooker class are too severe while introducing minimum advantage. Because dsl and lambda becomes impossible, every module would implement its own hook dispatchers, making cooperation impossible and destroy hook dump readability. --- .../github/libxposed/api/XposedInterface.java | 80 +++++++++++-------- .../libxposed/api/XposedInterfaceWrapper.java | 6 +- 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 5a55fe4..aeab3c2 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -152,57 +152,67 @@ interface AfterHookCallback { } /** - * Interface for method / constructor hooking. Xposed modules should define their own hooker class - * and implement this interface. Normally, a hooker class corresponds to a method / constructor, but - * there could also be a single hooker class for all of them. By this way you can implement an interface - * like the old API. - * - *

- * Classes implementing this interface should should provide two public static methods named - * before and after for before invocation and after invocation respectively. - *

- * - *

- * The before invocation method should have the following signature:
- * Param {@code callback}: The {@link BeforeHookCallback} of the procedure call.
- * Return value: If you want to save contextual information of one procedure call between the before - * and after callback, it could be a self-defined class, otherwise it should be {@code void}. - *

- * - *

- * The after invocation method should have the following signature:
- * Param {@code callback}: The {@link AfterHookCallback} of the procedure call.
- * Param {@code context} (optional): The contextual object returned by the before invocation. - *

+ * Interface for method / constructor hooking. * *

Example usage:

* *
{@code
-     *   public class ExampleHooker implements Hooker {
+     *   public class ExampleHooker implements VoidHooker {
      *
-     *       public static void before(@NonNull BeforeHookCallback callback) {
+     *       @Override
+     *       void before(@NonNull BeforeHookCallback callback) {
      *           // Pre-hooking logic goes here
      *       }
      *
-     *       public static void after(@NonNull AfterHookCallback callback) {
+     *       @Override
+     *       void after(@NonNull AfterHookCallback callback) {
      *           // Post-hooking logic goes here
      *       }
      *   }
      *
-     *   public class ExampleHookerWithContext implements Hooker {
+     *   public class ExampleHookerWithContext implements ContextualHooker {
      *
-     *       public static MyContext before(@NonNull BeforeHookCallback callback) {
+     *       @Override
+     *       MyContext before(@NonNull BeforeHookCallback callback) {
      *           // Pre-hooking logic goes here
      *           return new MyContext();
      *       }
      *
-     *       public static void after(@NonNull AfterHookCallback callback, MyContext context) {
+     *       @Override
+     *       void after(@NonNull AfterHookCallback callback, MyContext context) {
      *           // Post-hooking logic goes here
      *       }
      *   }
      * }
*/ - interface Hooker { + interface Hooker { + } + + /** + * A hooker without context. + */ + interface VoidHooker extends Hooker { + + default void before(@NonNull BeforeHookCallback callback) { + } + + default void after(@NonNull AfterHookCallback callback) { + } + } + + /** + * A hooker with context. The context object is guaranteed to be the same between before and after + * invocation. + * + * @param The type of the context + */ + interface ContextualHooker extends Hooker { + default C before(@NonNull BeforeHookCallback callback) { + return null; + } + + default void after(@NonNull AfterHookCallback callback, @Nullable C context) { + } } /** @@ -301,28 +311,28 @@ interface CtorHookHandle extends HookHandle> { * * @param origin The method to be hooked * @param priority The hook priority - * @param hooker The hooker class + * @param hooker The hooker object * @return Handle for the hook * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, * or hooker is invalid * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker); + MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Hooker hooker); /** * Hook a constructor with specified priority. * * @param origin The constructor to be hooked * @param priority The hook priority - * @param hooker The hooker class + * @param hooker The hooker object * @return Handle for the hook * @throws IllegalArgumentException if origin is framework internal or {@link Constructor#newInstance}, * or hooker is invalid * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Class hooker); + CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Hooker> hooker); /** * Hook the static initializer of a class with specified priority. @@ -332,13 +342,13 @@ interface CtorHookHandle extends HookHandle> { * * @param origin The class to be hooked * @param priority The hook priority - * @param hooker The hooker class + * @param hooker The hooker object * @return Handle for 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 - MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker); + MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker); /** * Deoptimizes a method / constructor in case hooked callee is not called because of inline. diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 9820fea..44b6834 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -71,21 +71,21 @@ public final long getFrameworkCapabilities() { @NonNull @Override - public final MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Class hooker) { + public final MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Hooker hooker) { ensureAttached(); return mBase.hook(origin, priority, hooker); } @NonNull @Override - public final CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Class hooker) { + public final CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Hooker> hooker) { ensureAttached(); return mBase.hook(origin, priority, hooker); } @NonNull @Override - public final MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Class hooker) { + public final MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker) { ensureAttached(); return mBase.hookClassInitializer(origin, priority, hooker); } From 2b92a76ac3c3b5b2ac7055c55fef3cd646e850fa Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sat, 21 Feb 2026 09:52:54 +0100 Subject: [PATCH 16/22] RFC: Add getApiVersion --- .../io/github/libxposed/api/XposedInterface.java | 12 +++++++----- .../github/libxposed/api/XposedInterfaceWrapper.java | 6 ++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index aeab3c2..fdfff98 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -23,11 +23,6 @@ */ @SuppressWarnings("unused") public interface XposedInterface { - /** - * SDK API version. - */ - int API = 100; - /** * The framework has the capability to hook system_server and other system processes. */ @@ -275,6 +270,13 @@ interface CtorHookHandle extends HookHandle> { T newInstanceOrigin(Object... args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException; } + /** + * Gets the Xposed API version of current implementation. + * + * @return API version + */ + int getApiVersion(); + /** * Gets the Xposed framework name of current implementation. * diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 44b6834..867ec9d 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -43,6 +43,12 @@ private void ensureAttached() { } } + @Override + public int getApiVersion() { + ensureAttached(); + return mBase.getApiVersion(); + } + @NonNull @Override public final String getFrameworkName() { From 44e46c0aae151649ef88316969acff2b823b57ab Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sat, 21 Feb 2026 12:51:53 +0100 Subject: [PATCH 17/22] RFC: Simplify hookClassInitializer --- api/src/main/java/io/github/libxposed/api/XposedInterface.java | 2 +- .../java/io/github/libxposed/api/XposedInterfaceWrapper.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index fdfff98..788a673 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -350,7 +350,7 @@ interface CtorHookHandle extends HookHandle> { * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker); + MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker); /** * Deoptimizes a method / constructor in case hooked callee is not called because of inline. diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index 867ec9d..d08d8d5 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -91,7 +91,7 @@ public final CtorHookHandle hook(@NonNull Constructor origin, int prio @NonNull @Override - public final MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker) { + public final MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker) { ensureAttached(); return mBase.hookClassInitializer(origin, priority, hooker); } From 570343d127f5d5509a3a845d63ec5aa842bd6bf5 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sun, 22 Feb 2026 08:09:09 +0100 Subject: [PATCH 18/22] RFC: Move priority into hooker, simplify hook interface --- .../github/libxposed/api/XposedInterface.java | 19 +++++++++++++------ .../libxposed/api/XposedInterfaceWrapper.java | 12 ++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 788a673..98be0a8 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -181,6 +181,16 @@ interface AfterHookCallback { * }
*/ interface Hooker { + /** + * Returns the priority of the hook. Hooks with higher priority will be executed first. The default + * priority is {@link #PRIORITY_DEFAULT}. Make sure the value is consistent after the hooker is installed, + * otherwise the behavior is undefined. + * + * @return The priority of the hook + */ + default int getPriority() { + return PRIORITY_DEFAULT; + } } /** @@ -312,7 +322,6 @@ interface CtorHookHandle extends HookHandle> { * Hook a method with specified priority. * * @param origin The method to be hooked - * @param priority The hook priority * @param hooker The hooker object * @return Handle for the hook * @throws IllegalArgumentException if origin is abstract, framework internal or {@link Method#invoke}, @@ -320,13 +329,12 @@ interface CtorHookHandle extends HookHandle> { * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Hooker hooker); + MethodHookHandle hook(@NonNull Method origin, @NonNull Hooker hooker); /** * Hook a constructor with specified priority. * * @param origin The constructor to be hooked - * @param priority The hook priority * @param hooker The hooker object * @return Handle for the hook * @throws IllegalArgumentException if origin is framework internal or {@link Constructor#newInstance}, @@ -334,7 +342,7 @@ interface CtorHookHandle extends HookHandle> { * @throws HookFailedError if hook fails due to framework internal error */ @NonNull - CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Hooker> hooker); + CtorHookHandle hook(@NonNull Constructor origin, @NonNull Hooker> hooker); /** * Hook the static initializer of a class with specified priority. @@ -343,14 +351,13 @@ interface CtorHookHandle extends HookHandle> { *

* * @param origin The class to be hooked - * @param priority The hook priority * @param hooker The hooker object * @return Handle for 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 - MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker); + MethodHookHandle hookClassInitializer(@NonNull Class origin, @NonNull Hooker hooker); /** * Deoptimizes a method / constructor in case hooked callee is not called because of inline. diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java index d08d8d5..e3377c5 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterfaceWrapper.java @@ -77,23 +77,23 @@ public final long getFrameworkCapabilities() { @NonNull @Override - public final MethodHookHandle hook(@NonNull Method origin, int priority, @NonNull Hooker hooker) { + public final MethodHookHandle hook(@NonNull Method origin, @NonNull Hooker hooker) { ensureAttached(); - return mBase.hook(origin, priority, hooker); + return mBase.hook(origin, hooker); } @NonNull @Override - public final CtorHookHandle hook(@NonNull Constructor origin, int priority, @NonNull Hooker> hooker) { + public final CtorHookHandle hook(@NonNull Constructor origin, @NonNull Hooker> hooker) { ensureAttached(); - return mBase.hook(origin, priority, hooker); + return mBase.hook(origin, hooker); } @NonNull @Override - public final MethodHookHandle hookClassInitializer(@NonNull Class origin, int priority, @NonNull Hooker hooker) { + public final MethodHookHandle hookClassInitializer(@NonNull Class origin, @NonNull Hooker hooker) { ensureAttached(); - return mBase.hookClassInitializer(origin, priority, hooker); + return mBase.hookClassInitializer(origin, hooker); } @Override From c1612ac05d48c2fe69a16ad1622d48995fc04bf7 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sun, 22 Feb 2026 08:26:31 +0100 Subject: [PATCH 19/22] RFC: Refine docs --- .../main/java/io/github/libxposed/api/XposedInterface.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 98be0a8..5b15def 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -319,7 +319,7 @@ interface CtorHookHandle extends HookHandle> { long getFrameworkCapabilities(); /** - * Hook a method with specified priority. + * Hook a method. * * @param origin The method to be hooked * @param hooker The hooker object @@ -332,7 +332,7 @@ interface CtorHookHandle extends HookHandle> { MethodHookHandle hook(@NonNull Method origin, @NonNull Hooker hooker); /** - * Hook a constructor with specified priority. + * Hook a constructor. * * @param origin The constructor to be hooked * @param hooker The hooker object @@ -345,7 +345,7 @@ interface CtorHookHandle extends HookHandle> { CtorHookHandle hook(@NonNull Constructor origin, @NonNull Hooker> hooker); /** - * Hook the static initializer of a class with specified priority. + * Hook the static initializer of a class. *

* Note: If the class is initialized, the hook will never be called. *

From 6386db99828effe8130ca43a8eb901a5f63b8460 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sun, 22 Feb 2026 08:41:25 +0100 Subject: [PATCH 20/22] RFC: Fix wrong type --- .../main/java/io/github/libxposed/api/XposedInterface.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 5b15def..92fd43b 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -198,10 +198,10 @@ default int getPriority() { */ interface VoidHooker extends Hooker { - default void before(@NonNull BeforeHookCallback callback) { + default void before(@NonNull BeforeHookCallback callback) { } - default void after(@NonNull AfterHookCallback callback) { + default void after(@NonNull AfterHookCallback callback) { } } From bc2b440ba1b8b80a66b785d8a93bb6ea8d2956ec Mon Sep 17 00:00:00 2001 From: Nullptr Date: Sun, 22 Feb 2026 09:11:05 +0100 Subject: [PATCH 21/22] RFC: Rename VoidHooker -> SimpleHooker to prevent misunderstanding --- .../main/java/io/github/libxposed/api/XposedInterface.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java index 92fd43b..2a8de8c 100644 --- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java +++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java @@ -152,7 +152,7 @@ interface AfterHookCallback { *

Example usage:

* *
{@code
-     *   public class ExampleHooker implements VoidHooker {
+     *   public class ExampleHooker implements SimpleHooker {
      *
      *       @Override
      *       void before(@NonNull BeforeHookCallback callback) {
@@ -196,7 +196,7 @@ default int getPriority() {
     /**
      * A hooker without context.
      */
-    interface VoidHooker extends Hooker {
+    interface SimpleHooker extends Hooker {
 
         default void before(@NonNull BeforeHookCallback callback) {
         }

From 3e7973a9894dbca94ee9b20560651882e783a314 Mon Sep 17 00:00:00 2001
From: Nullptr 
Date: Sun, 22 Feb 2026 09:43:36 +0100
Subject: [PATCH 22/22] RFC: Correct long value

---
 .../main/java/io/github/libxposed/api/XposedInterface.java  | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/api/src/main/java/io/github/libxposed/api/XposedInterface.java b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
index 2a8de8c..5c4b956 100644
--- a/api/src/main/java/io/github/libxposed/api/XposedInterface.java
+++ b/api/src/main/java/io/github/libxposed/api/XposedInterface.java
@@ -26,15 +26,15 @@ public interface XposedInterface {
     /**
      * The framework has the capability to hook system_server and other system processes.
      */
-    long CAP_SYSTEM = 1;
+    long CAP_SYSTEM = 1L;
     /**
      * The framework provides remote preferences and remote files support.
      */
-    long CAP_REMOTE = 1 << 1;
+    long CAP_REMOTE = 1L << 1;
     /**
      * The framework allows dynamically loaded code to use Xposed APIs.
      */
-    long CAP_RT_API_REFLECTION = 1 << 2;
+    long CAP_RT_API_REFLECTION = 1L << 2;
 
     /**
      * The default hook priority.