());
+ if (result != null) result.removeAll(WHITE_LIST);
+ return result;
+ } catch (Throwable throwable) {
+ Utils.logE("error when reading black list", throwable);
+ return new HashSet<>();
+ }
+ }
+
+ public static void hook(ClassLoader classLoader) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+ return;
+ }
+ try {
+ XposedHelpers.findAndHookMethod(ContextWrapper.class, "getSharedPreferences", String.class, int.class, new XC_MethodHook() {
+ @TargetApi(Build.VERSION_CODES.N)
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ try {
+ String prefName = (String) param.args[0];
+ if (!prefName.equals(BLACK_LIST_PREF_NAME)) {
+ return;
+ }
+ Activity activity = (Activity) param.thisObject;
+ Context context = activity.createDeviceProtectedStorageContext();
+ context.moveSharedPreferencesFrom(activity, prefName);
+ param.setResult(context.getSharedPreferences(prefName, (int) param.args[1]));
+ } catch (Throwable throwable) {
+ Utils.logE("error hooking Xposed BlackList", throwable);
+ }
+ }
+ });
+ } catch (Throwable throwable) {
+ Utils.logE("error hooking Xposed BlackList", throwable);
+ }
+ }
+}
diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/XposedInstallerHooker.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/XposedInstallerHooker.java
new file mode 100644
index 00000000..07931306
--- /dev/null
+++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/entry/hooker/XposedInstallerHooker.java
@@ -0,0 +1,64 @@
+package com.elderdrivers.riru.edxp.yahfa.entry.hooker;
+
+import com.elderdrivers.riru.edxp.util.Utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import de.robv.android.xposed.XC_MethodHook;
+import de.robv.android.xposed.XC_MethodReplacement;
+import de.robv.android.xposed.XposedBridge;
+import de.robv.android.xposed.XposedHelpers;
+
+import static com.elderdrivers.riru.edxp.config.InstallerChooser.LEGACY_INSTALLER_PACKAGE_NAME;
+
+public class XposedInstallerHooker {
+
+ public static void hookXposedInstaller(ClassLoader classLoader) {
+ try {
+ final String xposedAppClass = LEGACY_INSTALLER_PACKAGE_NAME + ".XposedApp";
+ final Class InstallZipUtil = XposedHelpers.findClass(LEGACY_INSTALLER_PACKAGE_NAME
+ + ".util.InstallZipUtil", classLoader);
+ XposedHelpers.findAndHookMethod(xposedAppClass, classLoader, "getActiveXposedVersion",
+ XC_MethodReplacement.returnConstant(XposedBridge.getXposedVersion()));
+ XposedHelpers.findAndHookMethod(xposedAppClass, classLoader,
+ "reloadXposedProp", new XC_MethodHook() {
+ @Override
+ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
+ Utils.logD("before reloadXposedProp...");
+ final String propFieldName = "mXposedProp";
+ final Object thisObject = param.thisObject;
+ if (XposedHelpers.getObjectField(thisObject, propFieldName) != null) {
+ param.setResult(null);
+ Utils.logD("reloadXposedProp already done, skip...");
+ return;
+ }
+ File file = new File("/system/framework/edconfig.jar");
+ FileInputStream is = null;
+ try {
+ is = new FileInputStream(file);
+ Object props = XposedHelpers.callStaticMethod(InstallZipUtil,
+ "parseXposedProp", is);
+ synchronized (thisObject) {
+ XposedHelpers.setObjectField(thisObject, propFieldName, props);
+ }
+ Utils.logD("reloadXposedProp done...");
+ param.setResult(null);
+ } catch (IOException e) {
+ Utils.logE("Could not read " + file.getPath(), e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+ });
+ } catch (Throwable t) {
+ Utils.logE("Could not hook Xposed Installer", t);
+ }
+ }
+}
diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java
new file mode 100644
index 00000000..ba9842d9
--- /dev/null
+++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/BlackWhiteListProxy.java
@@ -0,0 +1,132 @@
+package com.elderdrivers.riru.edxp.yahfa.proxy;
+
+import android.text.TextUtils;
+
+import com.elderdrivers.riru.edxp.Main;
+import com.elderdrivers.riru.edxp.config.ConfigManager;
+import com.elderdrivers.riru.edxp.yahfa.entry.Router;
+import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter;
+import com.elderdrivers.riru.edxp.util.ProcessUtils;
+import com.elderdrivers.riru.edxp.util.Utils;
+
+import de.robv.android.xposed.XposedBridge;
+
+import static com.elderdrivers.riru.edxp.Main.isAppNeedHook;
+import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
+
+/**
+ * 1. Non dynamic mode
+ * - system_server is whitelisted
+ * * for all child processes of main zygote
+ * What've been done in main zygote pre-forking system_server
+ * 1) non dynamic flag set (no need to reset)
+ * 2) boot image methods deopted (no need to redo)
+ * 3) startSystemServer flag set to true (need to reset)
+ * 4) workaround hooks installed (need to redo)
+ * 5) module list loaded and initZygote called (no need to redo)
+ * 6) close all fds (no need to redo because of 5))
+ * * for all child processes of secondary zygote
+ * 1) do the same things pre-forking first child process
+ * - system_server is blacklisted:
+ * * for all child processes of both main zygote and secondary zygote
+ * 1) do the same things pre-forking first child process
+ * 2. Dynamic mode:
+ * to be continued
+ */
+public class BlackWhiteListProxy {
+
+ public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, int mountExternal, String seInfo,
+ String niceName, int[] fdsToClose, int[] fdsToIgnore,
+ boolean startChildZygote, String instructionSet,
+ String appDataDir) {
+ final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
+ if (isDynamicModulesMode) {
+ // should never happen
+ return;
+ }
+ // only enter here when isDynamicModulesMode is off
+ onForkPreForNonDynamicMode(false);
+ }
+
+ public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
+ onForkPostCommon(false, appDataDir, niceName);
+ }
+
+ public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, long permittedCapabilities,
+ long effectiveCapabilities) {
+ final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
+ if (isDynamicModulesMode) {
+ // should never happen
+ return;
+ }
+ // only enter here when isDynamicModulesMode is off
+ onForkPreForNonDynamicMode(true);
+ }
+
+ public static void forkSystemServerPost(int pid) {
+ onForkPostCommon(true, getDataPathPrefix() + "android", "system_server");
+ }
+
+ /**
+ * Some details are different between main zygote and secondary zygote.
+ */
+ private static void onForkPreForNonDynamicMode(boolean isSystemServer) {
+ ConfigManager.setDynamicModulesMode(false);
+ // set startsSystemServer flag used when loadModules
+ Router.prepare(isSystemServer);
+ // deoptBootMethods once for all child processes of zygote
+ PrebuiltMethodsDeopter.deoptBootMethods();
+ // we never install bootstrap hooks here in black/white list mode except workaround hooks
+ // because installed hooks would be propagated to all child processes of zygote
+ Router.startWorkAroundHook();
+ // loadModules once for all child processes of zygote
+ // TODO maybe just save initZygote callbacks and call them when whitelisted process forked?
+ Router.loadModulesSafely(true);
+ Main.closeFilesBeforeForkNative();
+ }
+
+ private static void onForkPostCommon(boolean isSystemServer, String appDataDir, String niceName) {
+ Main.appDataDir = appDataDir;
+ Main.niceName = niceName;
+ final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
+ ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
+ Router.onEnterChildProcess();
+ if (!isDynamicModulesMode) {
+ Main.reopenFilesAfterForkNative();
+ }
+ if (!checkNeedHook(appDataDir, niceName)) {
+ // if is blacklisted, just stop here
+ return;
+ }
+ Router.prepare(isSystemServer);
+ PrebuiltMethodsDeopter.deoptBootMethods();
+ Router.installBootstrapHooks(isSystemServer);
+ if (isDynamicModulesMode) {
+ Router.loadModulesSafely(false);
+ }
+ }
+
+ private static boolean checkNeedHook(String appDataDir, String niceName) {
+ boolean needHook;
+ if (TextUtils.isEmpty(appDataDir)) {
+ Utils.logE("niceName:" + niceName + ", procName:"
+ + ProcessUtils.getCurrentProcessName(Main.appProcessName) + ", appDataDir is null, blacklisted!");
+ needHook = false;
+ } else {
+ // FIXME some process cannot read app_data_file because of MLS, e.g. bluetooth
+ needHook = isAppNeedHook(appDataDir);
+ }
+ if (!needHook) {
+ // clean up the scene
+ onBlackListed();
+ }
+ return needHook;
+ }
+
+ private static void onBlackListed() {
+ XposedBridge.clearLoadedPackages();
+ XposedBridge.clearInitPackageResources();
+ }
+}
diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java
new file mode 100644
index 00000000..9c1d3dba
--- /dev/null
+++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/proxy/NormalProxy.java
@@ -0,0 +1,70 @@
+package com.elderdrivers.riru.edxp.yahfa.proxy;
+
+import com.elderdrivers.riru.edxp.Main;
+import com.elderdrivers.riru.edxp.config.ConfigManager;
+import com.elderdrivers.riru.edxp.yahfa.util.PrebuiltMethodsDeopter;
+import com.elderdrivers.riru.edxp.yahfa.entry.Router;
+
+import static com.elderdrivers.riru.edxp.util.FileUtils.getDataPathPrefix;
+
+public class NormalProxy {
+
+ public static void forkAndSpecializePre(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, int mountExternal, String seInfo,
+ String niceName, int[] fdsToClose, int[] fdsToIgnore,
+ boolean startChildZygote, String instructionSet,
+ String appDataDir) {
+ // mainly for secondary zygote
+ final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
+ ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
+ // call this to ensure the flag is set to false ASAP
+ Router.prepare(false);
+ PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
+ // install bootstrap hooks for secondary zygote
+ Router.installBootstrapHooks(false);
+ // only load modules for secondary zygote
+ Router.loadModulesSafely(true);
+ Main.closeFilesBeforeForkNative();
+ }
+
+ public static void forkAndSpecializePost(int pid, String appDataDir, String niceName) {
+ // TODO consider processes without forkAndSpecializePost called
+ Main.appDataDir = appDataDir;
+ Main.niceName = niceName;
+ Router.prepare(false);
+ Main.reopenFilesAfterForkNative();
+ Router.onEnterChildProcess();
+ // load modules for each app process on its forked if dynamic modules mode is on
+ Router.loadModulesSafely(false);
+ }
+
+ public static void forkSystemServerPre(int uid, int gid, int[] gids, int debugFlags, int[][] rlimits,
+ long permittedCapabilities, long effectiveCapabilities) {
+ final boolean isDynamicModulesMode = Main.isDynamicModulesEnabled();
+ ConfigManager.setDynamicModulesMode(isDynamicModulesMode);
+ // set startsSystemServer flag used when loadModules
+ Router.prepare(true);
+ PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for main zygote
+ // install bootstrap hooks for main zygote as early as possible
+ // in case we miss some processes not forked via forkAndSpecialize
+ // for instance com.android.phone
+ Router.installBootstrapHooks(true);
+ // loadModules have to be executed in zygote even isDynamicModules is false
+ // because if not global hooks installed in initZygote might not be
+ // propagated to processes not forked via forkAndSpecialize
+ Router.loadModulesSafely(true);
+ Main.closeFilesBeforeForkNative();
+ }
+
+ public static void forkSystemServerPost(int pid) {
+ // in system_server process
+ Main.appDataDir = getDataPathPrefix() + "android";
+ Main.niceName = "system_server";
+ Router.prepare(true);
+ Main.reopenFilesAfterForkNative();
+ Router.onEnterChildProcess();
+ // reload module list if dynamic mode is on
+ Router.loadModulesSafely(false);
+ }
+
+}
diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/InlinedMethodCallers.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/InlinedMethodCallers.java
new file mode 100644
index 00000000..15fffeb0
--- /dev/null
+++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/InlinedMethodCallers.java
@@ -0,0 +1,49 @@
+package com.elderdrivers.riru.edxp.yahfa.util;
+
+import java.util.HashMap;
+
+/**
+ * Providing a whitelist of methods which are the callers of the target methods we want to hook.
+ * Because the target methods are inlined into the callers, we deoptimize the callers to
+ * run in intercept mode to make target methods hookable.
+ *
+ * Only for methods which are included in pre-compiled framework codes.
+ * TODO recompile system apps and priv-apps since their original dex files are available
+ */
+public class InlinedMethodCallers {
+
+ public static final String KEY_BOOT_IMAGE = "boot_image";
+ public static final String KEY_SYSTEM_SERVER = "system_server";
+
+ /**
+ * Key should be {@link #KEY_BOOT_IMAGE}, {@link #KEY_SYSTEM_SERVER}, or a package name
+ * of system apps or priv-apps i.e. com.android.systemui
+ */
+ private static final HashMap CALLERS = new HashMap<>();
+
+ /**
+ * format for each row: {className, methodName, methodSig}
+ */
+ private static final String[][] BOOT_IMAGE = {
+ // callers of Application#attach(Context)
+ {"android.app.Instrumentation", "newApplication", "(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Context;)Landroid/app/Application;"}
+ };
+
+ private static final String[][] SYSTEM_SERVER = {};
+
+ private static final String[][] SYSTEM_UI = {};
+
+ static {
+ CALLERS.put(KEY_BOOT_IMAGE, BOOT_IMAGE);
+ CALLERS.put(KEY_SYSTEM_SERVER, SYSTEM_SERVER);
+ CALLERS.put("com.android.systemui", SYSTEM_UI);
+ }
+
+ public static HashMap getAll() {
+ return CALLERS;
+ }
+
+ public static String[][] get(String where) {
+ return CALLERS.get(where);
+ }
+}
diff --git a/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/PrebuiltMethodsDeopter.java b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/PrebuiltMethodsDeopter.java
new file mode 100644
index 00000000..ecd8813d
--- /dev/null
+++ b/edxp-whale/src/main/java/com/elderdrivers/riru/edxp/yahfa/util/PrebuiltMethodsDeopter.java
@@ -0,0 +1,41 @@
+package com.elderdrivers.riru.edxp.yahfa.util;
+
+import com.elderdrivers.riru.edxp.util.Utils;
+import com.elderdrivers.riru.edxp.Main;
+
+import java.util.Arrays;
+
+import de.robv.android.xposed.XposedHelpers;
+
+import static com.elderdrivers.riru.edxp.yahfa.util.InlinedMethodCallers.KEY_BOOT_IMAGE;
+import static com.elderdrivers.riru.edxp.yahfa.util.InlinedMethodCallers.KEY_SYSTEM_SERVER;
+
+public class PrebuiltMethodsDeopter {
+
+ public static void deoptMethods(String where, ClassLoader cl) {
+ String[][] callers = InlinedMethodCallers.get(where);
+ if (callers == null) {
+ return;
+ }
+ for (String[] caller : callers) {
+ try {
+ Object method = Main.findMethodNative(
+ XposedHelpers.findClass(caller[0], cl), caller[1], caller[2]);
+ if (method != null) {
+ Main.deoptMethodNative(method);
+ }
+ } catch (Throwable throwable) {
+ Utils.logE("error when deopting method: " + Arrays.toString(caller), throwable);
+ }
+ }
+ }
+
+ public static void deoptBootMethods() {
+ // todo check if has been done before
+ deoptMethods(KEY_BOOT_IMAGE, null);
+ }
+
+ public static void deoptSystemServerMethods(ClassLoader sysCL) {
+ deoptMethods(KEY_SYSTEM_SERVER, sysCL);
+ }
+}
diff --git a/edxp-whale/src/main/java/com/lody/whale/VMHelper.java b/edxp-whale/src/main/java/com/lody/whale/VMHelper.java
new file mode 100644
index 00000000..392d7fef
--- /dev/null
+++ b/edxp-whale/src/main/java/com/lody/whale/VMHelper.java
@@ -0,0 +1,105 @@
+package com.lody.whale;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * @author Lody
+ */
+class VMHelper {
+
+ // Holds a mapping from Java type names to native type codes.
+ private static final HashMap, String> PRIMITIVE_TO_SIGNATURE;
+
+ static {
+ PRIMITIVE_TO_SIGNATURE = new HashMap<>(9);
+ PRIMITIVE_TO_SIGNATURE.put(byte.class, "B");
+ PRIMITIVE_TO_SIGNATURE.put(char.class, "C");
+ PRIMITIVE_TO_SIGNATURE.put(short.class, "S");
+ PRIMITIVE_TO_SIGNATURE.put(int.class, "I");
+ PRIMITIVE_TO_SIGNATURE.put(long.class, "J");
+ PRIMITIVE_TO_SIGNATURE.put(float.class, "F");
+ PRIMITIVE_TO_SIGNATURE.put(double.class, "D");
+ PRIMITIVE_TO_SIGNATURE.put(void.class, "V");
+ PRIMITIVE_TO_SIGNATURE.put(boolean.class, "Z");
+ }
+
+ /**
+ * Returns the internal name of {@code clazz} (also known as the
+ * descriptor).
+ */
+ private static String getSignature(final Class> clazz) {
+ final String primitiveSignature = PRIMITIVE_TO_SIGNATURE.get(clazz);
+ if (primitiveSignature != null) {
+ return primitiveSignature;
+ } else if (clazz.isArray()) {
+ return "[" + getSignature(clazz.getComponentType());
+ } else {
+ return "L" + clazz.getName().replace('.', '/') + ";";
+ }
+ }
+
+ /**
+ * Returns the native type codes of {@code clazz}.
+ */
+ private static String getShortyType(final Class> clazz) {
+ final String primitiveSignature = PRIMITIVE_TO_SIGNATURE.get(clazz);
+ if (primitiveSignature != null) {
+ return primitiveSignature;
+ }
+ return "L";
+ }
+
+ // @SuppressWarnings("ConstantConditions")
+ private static String getSignature(final Class> retType,
+ final Class>[] parameterTypes) {
+ final StringBuilder result = new StringBuilder();
+
+ result.append('(');
+ for (final Class> parameterType : parameterTypes) {
+ result.append(getSignature(parameterType));
+ }
+ result.append(")");
+ result.append(getSignature(retType));
+
+ return result.toString();
+ }
+
+ private static String getShorty(final Class> retType,
+ final Class>[] parameterTypes) {
+ final StringBuilder result = new StringBuilder();
+
+ result.append(getShortyType(retType));
+ for (final Class> parameterType : parameterTypes) {
+ result.append(getShortyType(parameterType));
+ }
+
+ return result.toString();
+ }
+
+ static String getSignature(final Member m) {
+ if (m instanceof Method) {
+ final Method md = (Method) m;
+ return getSignature(md.getReturnType(), md.getParameterTypes());
+ }
+ if (m instanceof Constructor) {
+ final Constructor> c = (Constructor>) m;
+ return getSignature(void.class, c.getParameterTypes());
+ }
+ return null;
+ }
+
+ static String getShorty(final Member m) {
+ if (m instanceof Method) {
+ final Method md = (Method) m;
+ return getShorty(md.getReturnType(), md.getParameterTypes());
+ }
+ if (m instanceof Constructor) {
+ final Constructor> c = (Constructor>) m;
+ return getShorty(void.class, c.getParameterTypes());
+ }
+ return null;
+ }
+}
diff --git a/edxp-whale/src/main/java/com/lody/whale/WhaleRuntime.java b/edxp-whale/src/main/java/com/lody/whale/WhaleRuntime.java
new file mode 100644
index 00000000..2c0904e4
--- /dev/null
+++ b/edxp-whale/src/main/java/com/lody/whale/WhaleRuntime.java
@@ -0,0 +1,74 @@
+package com.lody.whale;
+
+import android.os.Build;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+import de.robv.android.xposed.XposedBridge;
+
+/**
+ * @author Lody
+ *
+ * NOTICE: Do not move or rename any methods in this class.
+ */
+public class WhaleRuntime {
+
+ static {
+ System.loadLibrary("whale.edxp");
+ }
+
+ private static String getShorty(Member member) {
+ return VMHelper.getShorty(member);
+ }
+
+ public static long[] countInstancesOfClasses(Class[] classes, boolean assignable) {
+ if (Build.VERSION.SDK_INT < 27) {
+ throw new UnsupportedOperationException("Not support countInstancesOfClasses on your device yet.");
+ }
+ try {
+ Class> clazz = Class.forName("dalvik.system.VMDebug");
+ Method method = clazz.getDeclaredMethod("countInstancesOfClasses", Class[].class, boolean.class);
+ return (long[]) method.invoke(null, classes, assignable);
+ } catch (Throwable e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public static Object[][] getInstancesOfClasses(Class[] classes, boolean assignable) {
+ if (Build.VERSION.SDK_INT < 28) {
+ throw new UnsupportedOperationException("Not support getInstancesOfClasses on your device yet.");
+ }
+ try {
+ Class> clazz = Class.forName("dalvik.system.VMDebug");
+ Method method = clazz.getDeclaredMethod("getInstancesOfClasses", Class[].class, boolean.class);
+ return (Object[][]) method.invoke(null, classes, assignable);
+ } catch (Throwable e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public static Object handleHookedMethod(Member member, long slot, Object additionInfo, Object thisObject, Object[] args) throws Throwable {
+ return XposedBridge.handleHookedMethod(member, slot, additionInfo, thisObject, args);
+ }
+
+ public static native Object invokeOriginalMethodNative(long slot, Object thisObject, Object[] args)
+ throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
+
+ public static native long getMethodSlot(Member member) throws IllegalArgumentException;
+
+ public static native long hookMethodNative(Class> declClass, Member method, Object additionInfo);
+
+ public static native void setObjectClassNative(Object object, Class> parent);
+
+ public static native Object cloneToSubclassNative(Object object, Class> subClass);
+
+ public static native void removeFinalFlagNative(Class> cl);
+
+ public static native void enforceDisableHiddenAPIPolicy();
+
+ private static native void reserved0();
+
+ private static native void reserved1();
+}
diff --git a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java
index 2f0709ae..cf51c5f5 100644
--- a/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java
+++ b/edxp-yahfa/src/main/java/com/elderdrivers/riru/edxp/yahfa/config/YahfaHookProvider.java
@@ -16,7 +16,7 @@ public class YahfaHookProvider implements HookProvider {
}
@Override
- public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable {
+ public Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args) throws Throwable {
return DynamicBridge.invokeOriginalMethod(method, thisObject, args);
}
@@ -29,4 +29,9 @@ public class YahfaHookProvider implements HookProvider {
public void deoptMethods(String packageName, ClassLoader classLoader) {
PrebuiltMethodsDeopter.deoptMethods(packageName, classLoader);
}
+
+ @Override
+ public long getMethodId(Member member) {
+ return 0;
+ }
}
diff --git a/settings.gradle b/settings.gradle
index c96662e1..c40d4f51 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook'
\ No newline at end of file
+include ':edxp-core', ':xposed-bridge', ':hiddenapi-stubs', ':dexmaker', ':dalvikdx', ':edxp-common', ':edxp-yahfa', ':edxp-sandhook', ':edxp-whale'
\ No newline at end of file
diff --git a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java
index 6fc716b2..cc88f03f 100644
--- a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java
+++ b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/config/EdXpConfigGlobal.java
@@ -54,7 +54,7 @@ public class EdXpConfigGlobal {
}
@Override
- public Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)
+ public Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return null;
}
@@ -68,5 +68,10 @@ public class EdXpConfigGlobal {
public void deoptMethods(String packageName, ClassLoader classLoader) {
}
+
+ @Override
+ public long getMethodId(Member member) {
+ return 0;
+ }
};
}
diff --git a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java
index 671bd828..9e630e78 100644
--- a/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java
+++ b/xposed-bridge/src/main/java/com/elderdrivers/riru/edxp/hook/HookProvider.java
@@ -1,6 +1,5 @@
package com.elderdrivers.riru.edxp.hook;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import de.robv.android.xposed.XposedBridge;
@@ -9,9 +8,11 @@ public interface HookProvider {
void hookMethod(Member method, XposedBridge.AdditionalHookInfo additionalInfo);
- Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) throws Throwable;
+ Object invokeOriginalMethod(Member method, long methodId, Object thisObject, Object[] args) throws Throwable;
Member findMethodNative(Member hookMethod);
void deoptMethods(String packageName, ClassLoader classLoader);
+
+ long getMethodId(Member member);
}
diff --git a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java
index 92d9fc2b..204a0b04 100644
--- a/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java
+++ b/xposed-bridge/src/main/java/de/robv/android/xposed/XposedBridge.java
@@ -260,7 +260,7 @@ public final class XposedBridge {
/**
* This method is called as a replacement for hooked methods.
*/
- private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,
+ public static Object handleHookedMethod(Member method, long originalMethodId, Object additionalInfoObj,
Object thisObject, Object[] args) throws Throwable {
AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;
@@ -398,12 +398,12 @@ public final class XposedBridge {
EdXpConfigGlobal.getHookProvider().hookMethod(method, (AdditionalHookInfo) additionalInfoObj);
}
- private static Object invokeOriginalMethodNative(Member method, int methodId,
+ private static Object invokeOriginalMethodNative(Member method, long methodId,
Class>[] parameterTypes,
Class> returnType,
Object thisObject, Object[] args)
throws Throwable {
- return EdXpConfigGlobal.getHookProvider().invokeOriginalMethod(method, thisObject, args);
+ return EdXpConfigGlobal.getHookProvider().invokeOriginalMethod(method, methodId, thisObject, args);
}
/**
@@ -452,7 +452,8 @@ public final class XposedBridge {
throw new IllegalArgumentException("method must be of type Method or Constructor");
}
- return invokeOriginalMethodNative(method, 0, parameterTypes, returnType, thisObject, args);
+ long methodId = EdXpConfigGlobal.getHookProvider().getMethodId(method);
+ return invokeOriginalMethodNative(method, methodId, parameterTypes, returnType, thisObject, args);
}
/*package*/ static void setObjectClass(Object obj, Class> clazz) {