Usually, anonymous subclasses of this class are created which override + * {@link #beforeHookedMethod} and/or {@link #afterHookedMethod}. + */ +@SuppressWarnings({"unused", "JavaDoc"}) +public abstract class XC_MethodHook extends XCallback { + /** + * Creates a new callback with default priority. + */ + @SuppressWarnings("deprecation") + public XC_MethodHook() { + super(); + } + + /** + * Creates a new callback with a specific priority. + * + *
Note that {@link #afterHookedMethod} will be called in reversed order, i.e. + * the callback with the highest priority will be called last. This way, the callback has the + * final control over the return value. {@link #beforeHookedMethod} is called as usual, i.e. + * highest priority first. + * + * @param priority See {@link XCallback#priority}. + */ + public XC_MethodHook(int priority) { + super(priority); + } + + /** + * Called before the invocation of the method. + * + *
You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable} + * to prevent the original method from being called. + * + *
Note that implementations shouldn't call {@code super(param)}, it's not necessary. + * + * @param param Information about the method call. + * @throws Throwable Everything the callback throws is caught and logged. + */ + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + } + + /** + * Called after the invocation of the method. + * + *
You can use {@link MethodHookParam#setResult} and {@link MethodHookParam#setThrowable} + * to modify the return value of the original method. + * + *
Note that implementations shouldn't call {@code super(param)}, it's not necessary. + * + * @param param Information about the method call. + * @throws Throwable Everything the callback throws is caught and logged. + */ + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + } + + /** + * Wraps information about the method call and allows to influence it. + */ + public static final class MethodHookParam extends XCallback.Param { + /** + * @hide + */ + public MethodHookParam() { + super(); + } + + /** + * The hooked method/constructor. + */ + public Member method; + + /** + * The {@code this} reference for an instance method, or {@code null} for static methods. + */ + public Object thisObject; + + /** + * Arguments to the method call. + */ + public Object[] args; + + private Object result = null; + private Throwable throwable = null; + /* package */ boolean returnEarly = false; + + /** + * Returns the result of the method call. + */ + public Object getResult() { + return result; + } + + /** + * Modify the result of the method call. + * + *
If called from {@link #beforeHookedMethod}, it prevents the call to the original method. + */ + public void setResult(Object result) { + this.result = result; + this.throwable = null; + this.returnEarly = true; + } + + /** + * Returns the {@link Throwable} thrown by the method, or {@code null}. + */ + public Throwable getThrowable() { + return throwable; + } + + /** + * Returns true if an exception was thrown by the method. + */ + public boolean hasThrowable() { + return throwable != null; + } + + /** + * Modify the exception thrown of the method call. + * + *
If called from {@link #beforeHookedMethod}, it prevents the call to the original method.
+ */
+ public void setThrowable(Throwable throwable) {
+ this.throwable = throwable;
+ this.result = null;
+ this.returnEarly = true;
+ }
+
+ /**
+ * Returns the result of the method call, or throws the Throwable caused by it.
+ */
+ public Object getResultOrThrowable() throws Throwable {
+ if (throwable != null)
+ throw throwable;
+ return result;
+ }
+ }
+
+ /**
+ * An object with which the method/constructor can be unhooked.
+ */
+ public class Unhook implements IXUnhook Note that implementations shouldn't call {@code super(param)}, it's not necessary.
+ *
+ * @param param Information about the method call.
+ * @throws Throwable Anything that is thrown by the callback will be passed on to the original caller.
+ */
+ @SuppressWarnings("UnusedParameters")
+ protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable;
+
+ /**
+ * Predefined callback that skips the method without replacements.
+ */
+ public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST * 2) {
+ @Override
+ protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
+ return null;
+ }
+ };
+
+ /**
+ * Creates a callback which always returns a specific value.
+ *
+ * @param result The value that should be returned to callers of the hooked method.
+ */
+ public static XC_MethodReplacement returnConstant(final Object result) {
+ return returnConstant(PRIORITY_DEFAULT, result);
+ }
+
+ /**
+ * Like {@link #returnConstant(Object)}, but allows to specify a priority for the callback.
+ *
+ * @param priority See {@link XCallback#priority}.
+ * @param result The value that should be returned to callers of the hooked method.
+ */
+ public static XC_MethodReplacement returnConstant(int priority, final Object result) {
+ return new XC_MethodReplacement(priority) {
+ @Override
+ protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
+ return result;
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/feature/aliuhook/src/main/java/de/robv/android/xposed/XposedBridge.java b/feature/aliuhook/src/main/java/de/robv/android/xposed/XposedBridge.java
new file mode 100644
index 000000000..f352d4af0
--- /dev/null
+++ b/feature/aliuhook/src/main/java/de/robv/android/xposed/XposedBridge.java
@@ -0,0 +1,413 @@
+/*
+ * This file is part of AliuHook, a library providing XposedAPI bindings to LSPlant
+ * Copyright (c) 2021 Juby210 & Vendicated
+ * Licensed under the Open Software License version 3.0
+ *
+ * Originally written by rovo89 as part of the original Xposed
+ * Copyright 2013 rovo89, Tungstwenty
+ * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+package de.robv.android.xposed;
+
+import android.util.Log;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@SuppressWarnings({"unused", "JavaDoc"})
+public class XposedBridge {
+ private static final String TAG = "AliuHook-XposedBridge";
+
+ static {
+ try {
+ callbackMethod = XposedBridge.HookInfo.class.getMethod("callback", Object[].class);
+ } catch (Throwable t) {
+ throw new RuntimeException("Failed to initialize", t);
+ }
+
+ System.loadLibrary("aliuhook");
+ }
+
+ private static final Object[] EMPTY_ARRAY = new Object[0];
+
+ private static final Map
+ * You could also try deleting /data/misc/profiles/cur/0/com.YOURPACKAGE/primary.prof
+ *
+ * See https://source.android.com/devices/tech/dalvik/configure#how_art_works for more info
+ *
+ * @return Whether disabling profile saver succeeded
+ */
+ public static native boolean disableProfileSaver();
+
+ /**
+ * Disables HiddenApi restrictions, thus allowing you access to all private interfaces.
+ *
+ *
+ * @return Whether disabling hidden api succeeded
+ * @see https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces
+ */
+ public static native boolean disableHiddenApiRestrictions();
+
+ private static void checkMethod(Member method) {
+ if (method == null)
+ throw new NullPointerException("method must not be null");
+ if (!(method instanceof Method || method instanceof Constructor>))
+ throw new IllegalArgumentException("method must be a Method or Constructor");
+
+ var modifiers = method.getModifiers();
+ if (Modifier.isAbstract(modifiers))
+ throw new IllegalArgumentException("method must not be abstract");
+ }
+
+ /**
+ * Check if a method is hooked
+ *
+ * @param method The method to check
+ * @return true if method is hooked
+ */
+ public static boolean isHooked(Member method) {
+ return hookRecords.containsKey(method);
+ }
+
+ /**
+ * Make a final class inheritable. Removes final modifier from class and its constructors and makes
+ * constructors accessible (private -> protected)
+ *
+ * @param clazz Class to make inheritable
+ */
+ public static boolean makeClassInheritable(Class> clazz) {
+ if (clazz == null) throw new NullPointerException("class must not be null");
+
+ return makeClassInheritable0(clazz);
+ }
+
+ /**
+ * Deoptimize a method to avoid inlining
+ *
+ * @param method The method to deoptimize. Generally it should be a caller of a method
+ * that is inlined.
+ */
+ public static boolean deoptimizeMethod(Member method) {
+ checkMethod(method);
+ return deoptimize0(method);
+ }
+
+ /**
+ * Hook any method (or constructor) with the specified callback.
+ *
+ * @param method The method to be hooked.
+ * @param callback The callback to be executed when the hooked method is called.
+ * @return An object that can be used to remove the hook.
+ */
+ public static XC_MethodHook.Unhook hookMethod(Member method, XC_MethodHook callback) {
+ checkMethod(method);
+ if (callback == null) throw new NullPointerException("callback must not be null");
+
+ HookInfo hookRecord;
+ synchronized (hookRecords) {
+ hookRecord = hookRecords.get(method);
+ if (hookRecord == null) {
+ hookRecord = new HookInfo(method);
+ var backup = hook0(hookRecord, method, callbackMethod);
+ if (backup == null) throw new IllegalStateException("Failed to hook method");
+ hookRecord.backup = backup;
+ hookRecords.put(method, hookRecord);
+ }
+ }
+
+ hookRecord.callbacks.add(callback);
+
+ return callback.new Unhook(method);
+ }
+
+ /**
+ * Hooks all methods with a certain name that were declared in the specified class. Inherited
+ * methods and constructors are not considered. For constructors, use
+ * {@link #hookAllConstructors} instead.
+ *
+ * @param hookClass The class to check for declared methods.
+ * @param methodName The name of the method(s) to hook.
+ * @param callback The callback to be executed when the hooked methods are called.
+ * @return A set containing one object for each found method which can be used to unhook it.
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ public static Set There are very few cases where this method is needed. A common mistake is
+ * to replace a method and then invoke the original one based on dynamic conditions. This
+ * creates overhead and skips further hooks by other modules. Instead, just hook (don't replace)
+ * the method and call {@code param.setResult(null)} in {@link XC_MethodHook#beforeHookedMethod}
+ * if the original method should be skipped.
+ *
+ * @param method The method to be called.
+ * @param thisObject For non-static calls, the "this" pointer, otherwise {@code null}.
+ * @param args Arguments for the method call as Object[] array.
+ * @return The result returned from the invoked method.
+ * @throws NullPointerException if {@code receiver == null} for a non-static method
+ * @throws IllegalAccessException if this method is not accessible (see {@link AccessibleObject})
+ * @throws IllegalArgumentException if the number of arguments doesn't match the number of parameters, the receiver
+ * is incompatible with the declaring class, or an argument could not be unboxed
+ * or converted by a widening conversion to the corresponding parameter type
+ * @throws InvocationTargetException if an exception was thrown by the invoked method
+ */
+ public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
+ throws NullPointerException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ if (args == null)
+ args = EMPTY_ARRAY;
+
+ var hookRecord = hookRecords.get(method);
+ try {
+ // Checking method is not needed if we found hookRecord
+ if (hookRecord != null)
+ return invokeMethod(hookRecord.backup, thisObject, args);
+
+ checkMethod(method);
+ return invokeMethod(method, thisObject, args);
+ } catch (InstantiationException ex) {
+ // This should never be reached
+ throw new IllegalArgumentException("The class this Constructor belongs to is abstract and cannot be instantiated");
+ }
+ }
+
+ private static Object invokeMethod(Member member, Object thisObject, Object[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
+ if (member instanceof Method) {
+ var method = (Method) member;
+ method.setAccessible(true);
+ return method.invoke(thisObject, args);
+ } else {
+ var ctor = (Constructor>) member;
+ ctor.setAccessible(true);
+ return ctor.newInstance(args);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static final class CopyOnWriteSortedSet
+ * The actual (abstract) callback methods are added by subclasses.
+ */
+@SuppressWarnings({"unused", "JavaDoc"})
+public abstract class XCallback implements Comparable This is usually set to {@link #PRIORITY_DEFAULT}. However, in case a certain callback should
+ * be executed earlier or later a value between {@link #PRIORITY_HIGHEST} and {@link #PRIORITY_LOWEST}
+ * can be set instead. The values are just for orientation though, Xposed doesn't enforce any
+ * boundaries on the priority values.
+ */
+ public final int priority;
+
+ /**
+ * @deprecated This constructor can't be hidden for technical reasons. Nevertheless, don't use it!
+ */
+ @SuppressWarnings("DeprecatedIsStillUsed")
+ @Deprecated
+ public XCallback() {
+ this.priority = PRIORITY_DEFAULT;
+ }
+
+ /**
+ * @hide
+ */
+ public XCallback(int priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * Base class for Xposed callback parameters.
+ */
+ public static abstract class Param {
+ protected Param() {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int compareTo(XCallback other) {
+ if (this == other)
+ return 0;
+
+ // order descending by priority
+ if (other.priority != this.priority)
+ return other.priority - this.priority;
+ // then randomly
+ else if (System.identityHashCode(this) < System.identityHashCode(other))
+ return -1;
+ else
+ return 1;
+ }
+
+ /**
+ * The default priority, see {@link #priority}.
+ */
+ public static final int PRIORITY_DEFAULT = 50;
+
+ /**
+ * Execute this callback late, see {@link #priority}.
+ */
+ public static final int PRIORITY_LOWEST = -10000;
+
+ /**
+ * Execute this callback early, see {@link #priority}.
+ */
+ public static final int PRIORITY_HIGHEST = 10000;
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 3b1144223..ca543f6e8 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -12,6 +12,13 @@
* You should have received a copy of the GNU General Public License along with Cosmic IDE. If not, see