Reuse generated dex and oat files when using compat mode

This commit is contained in:
solohsu 2019-03-15 18:40:57 +08:00
parent 09af8ce0b4
commit 6d08c0b2bc
11 changed files with 66 additions and 34 deletions

View File

@ -7,6 +7,7 @@ import android.text.TextUtils;
import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.Main;
import com.elderdrivers.riru.xposed.config.ConfigManager; import com.elderdrivers.riru.xposed.config.ConfigManager;
import java.security.MessageDigest;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -226,4 +227,20 @@ public class DexMakerUtils {
TypeId<?> boxTypeId; TypeId<?> boxTypeId;
code.returnValue(resultLocals.get(returnType)); code.returnValue(resultLocals.get(returnType));
} }
public static String getSha1Hex(String text) {
final MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
byte[] result = digest.digest(text.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : result) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (Exception e) {
DexLog.e("error hashing target method: " + text, e);
}
return "";
}
} }

View File

@ -1,7 +1,6 @@
package com.elderdrivers.riru.xposed.dexmaker; package com.elderdrivers.riru.xposed.dexmaker;
import com.elderdrivers.riru.xposed.Main; import com.elderdrivers.riru.xposed.Main;
import com.elderdrivers.riru.xposed.util.FileUtils;
import java.io.File; import java.io.File;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
@ -72,8 +71,6 @@ public final class DynamicBridge {
if (!dexPathInited.compareAndSet(false, true)) { if (!dexPathInited.compareAndSet(false, true)) {
return; return;
} }
// delete previous compiled dex to prevent potential crashing
// TODO find a way to reuse them in consideration of performance
try { try {
// we always choose to use device encrypted storage data on android N and later // we always choose to use device encrypted storage data on android N and later
// in case some app is installing hooks before phone is unlocked // in case some app is installing hooks before phone is unlocked
@ -83,10 +80,6 @@ public final class DynamicBridge {
dexOptDir = new File(dexDir, "oat"); dexOptDir = new File(dexDir, "oat");
dexDir.mkdirs(); dexDir.mkdirs();
DexLog.d(Main.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath()); DexLog.d(Main.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath());
try {
FileUtils.delete(dexOptDir);
} catch (Throwable throwable) {
}
} catch (Throwable throwable) { } catch (Throwable throwable) {
DexLog.e("error when init dex path", throwable); DexLog.e("error when init dex path", throwable);
} }

View File

@ -42,7 +42,7 @@ public class HookerDexMaker {
public static final String METHOD_NAME_SETUP = "setup"; public static final String METHOD_NAME_SETUP = "setup";
public static final TypeId<Object[]> objArrayTypeId = TypeId.get(Object[].class); public static final TypeId<Object[]> objArrayTypeId = TypeId.get(Object[].class);
private static final String CLASS_DESC_PREFIX = "L"; private static final String CLASS_DESC_PREFIX = "L";
private static final String CLASS_NAME_PREFIX = "EdHooker"; private static final String CLASS_NAME_PREFIX = "ed_";
private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo"; private static final String FIELD_NAME_HOOK_INFO = "additionalHookInfo";
private static final String FIELD_NAME_METHOD = "method"; private static final String FIELD_NAME_METHOD = "method";
private static final String PARAMS_FIELD_NAME_METHOD = "method"; private static final String PARAMS_FIELD_NAME_METHOD = "method";
@ -183,26 +183,30 @@ public class HookerDexMaker {
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
private void doMake() throws Exception { private void doMake() throws Exception {
final boolean useInMemoryCl = TextUtils.isEmpty(mDexDirPath);
mDexMaker = new DexMaker(); mDexMaker = new DexMaker();
// Generate a Hooker class.
String className = CLASS_NAME_PREFIX + sClassNameSuffix.getAndIncrement();
String classDesc = CLASS_DESC_PREFIX + className + ";";
mHookerTypeId = TypeId.get(classDesc);
mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
generateFields();
generateSetupMethod();
generateBackupMethod();
generateHookMethod();
generateCallBackupMethod();
ClassLoader loader; ClassLoader loader;
if (TextUtils.isEmpty(mDexDirPath)) { // Generate a Hooker class.
// in memory dex classloader String className = CLASS_NAME_PREFIX;
if (!useInMemoryCl) {
// if not using InMemoryDexClassLoader, className is also used as dex file name
// so it should be different from each other
String suffix = DexMakerUtils.getSha1Hex(mMember.toString());
if (TextUtils.isEmpty(suffix)) { // just in case
suffix = String.valueOf(sClassNameSuffix.getAndIncrement());
}
className = className + suffix;
if (!new File(mDexDirPath, className).exists()) {
// if file exists, reuse it and skip generating
doGenerate(className);
}
// load dex file from disk
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), className);
} else {
// do everything in memory
doGenerate(className);
byte[] dexBytes = mDexMaker.generate(); byte[] dexBytes = mDexMaker.generate();
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader); loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
} else {
// Create the dex file and load it.
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath));
} }
mHookClass = loader.loadClass(className); mHookClass = loader.loadClass(className);
@ -216,6 +220,17 @@ public class HookerDexMaker {
HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod); HookMain.backupAndHook(mMember, mHookMethod, mBackupMethod);
} }
private void doGenerate(String className) {
String classDesc = CLASS_DESC_PREFIX + className + ";";
mHookerTypeId = TypeId.get(classDesc);
mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
generateFields();
generateSetupMethod();
generateBackupMethod();
generateHookMethod();
generateCallBackupMethod();
}
public Method getHookMethod() { public Method getHookMethod() {
return mHookMethod; return mHookMethod;
} }

View File

@ -1,7 +1,7 @@
import org.gradle.internal.os.OperatingSystem; import org.gradle.internal.os.OperatingSystem;
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
version "v0.3.1.3_beta-SNAPSHOT" version "v0.3.1.4_beta-SNAPSHOT"
extensions["module_name"] = "EdXposed" extensions["module_name"] = "EdXposed"
android { android {
compileSdkVersion 28 compileSdkVersion 28

View File

@ -1,6 +1,6 @@
#!/system/bin/sh #!/system/bin/sh
EDXP_VERSION="0.3.1.3_beta-SNAPSHOT (3130)" EDXP_VERSION="0.3.1.4_beta-SNAPSHOT (3140)"
ANDROID_SDK=`getprop ro.build.version.sdk` ANDROID_SDK=`getprop ro.build.version.sdk`
BUILD_DESC=`getprop ro.build.description` BUILD_DESC=`getprop ro.build.description`
PRODUCT=`getprop ro.build.product` PRODUCT=`getprop ro.build.product`

View File

@ -41,7 +41,7 @@ LATESTARTSERVICE=false
print_modname() { print_modname() {
ui_print "************************************" ui_print "************************************"
ui_print " Riru - Ed Xposed v0.3.1.3 " ui_print " Riru - Ed Xposed v0.3.1.4 "
ui_print "************************************" ui_print "************************************"
} }

View File

@ -1,7 +1,7 @@
id=riru_edxposed id=riru_edxposed
name=Riru - Ed Xposed name=Riru - Ed Xposed
version=v0.3.1.3_beta-SNAPSHOT version=v0.3.1.4_beta-SNAPSHOT
versionCode=3130 versionCode=3140
author=solohsu & MlgmXyysd author=solohsu & MlgmXyysd
description=Magisk version of Xposed. Require Riru - Core installed. description=Magisk version of Xposed. Require Riru - Core installed.
minMagisk=17000 minMagisk=17000

View File

@ -1,5 +1,5 @@
name=Ed Xposed name=Ed Xposed
version=v0.3.1.3_beta-SNAPSHOT version=v0.3.1.4_beta-SNAPSHOT
versionCode=3130 versionCode=3140
author=solohsu & MlgmXyysd author=solohsu & MlgmXyysd
description=Magisk version of Xposed. Require Riru - Core installed. description=Magisk version of Xposed. Require Riru - Core installed.

View File

@ -1,4 +1,4 @@
version=90.0-0.3.1.3-beta-SNAPSHOT version=90.0-0.3.1.4-beta-SNAPSHOT
arch=arm64 arch=arm64
minsdk=23 minsdk=23
maxsdk=28 maxsdk=28

View File

@ -471,6 +471,10 @@ public final class DexMaker {
} }
} }
public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException {
return generateAndLoad(parent, dexCache, null);
}
/** /**
* Generates a dex file and loads its types into the current process. * Generates a dex file and loads its types into the current process.
* *
@ -496,8 +500,9 @@ public final class DexMaker {
* @param dexCache the destination directory where generated and optimized * @param dexCache the destination directory where generated and optimized
* dex files will be written. If null, this class will try to guess the * dex files will be written. If null, this class will try to guess the
* application's private data dir. * application's private data dir.
* @param fileName the name of dex file
*/ */
public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException { public ClassLoader generateAndLoad(ClassLoader parent, File dexCache, String fileName) throws IOException {
if (dexCache == null) { if (dexCache == null) {
String property = System.getProperty("dexmaker.dexcache"); String property = System.getProperty("dexmaker.dexcache");
if (property != null) { if (property != null) {
@ -511,7 +516,9 @@ public final class DexMaker {
} }
} }
File result = new File(dexCache, generateFileName()); if (fileName == null || fileName.isEmpty())
fileName = generateFileName();
File result = new File(dexCache, fileName);
// Check that the file exists. If it does, return a DexClassLoader and skip all // Check that the file exists. If it does, return a DexClassLoader and skip all
// the dex bytecode generation. // the dex bytecode generation.
if (result.exists()) { if (result.exists()) {