Reduce unnecessary IO when using compat mode

This commit is contained in:
solohsu 2019-03-02 01:07:42 +08:00
parent 628a3ce5da
commit 03ec9fa13f
6 changed files with 68 additions and 36 deletions

View File

@ -1,6 +1,7 @@
package com.elderdrivers.riru.xposed.config;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;
import de.robv.android.xposed.SELinuxHelper;
@ -16,24 +17,32 @@ public class ConfigManager {
private static final String USE_WHITE_LIST = INSTALLER_DATA_BASE_DIR + "conf/usewhitelist";
private static final String DYNAMIC_MODULES = INSTALLER_DATA_BASE_DIR + "conf/dynamicmodules";
private static final Set<String> WHITE_LIST = Collections.singleton(INSTALLER_PACKAGE_NAME);
private static final HashMap<String, Boolean> compatModeCache = new HashMap<>();
private static volatile boolean IS_DYNAMIC_MODULES = false;
public static boolean isDynamicModulesMode() {
return IS_DYNAMIC_MODULES;
}
public static synchronized void setDynamicModulesMode(boolean isDynamicModulesMode) {
if (isDynamicModulesMode != IS_DYNAMIC_MODULES) {
IS_DYNAMIC_MODULES = isDynamicModulesMode;
}
}
public static boolean isDynamicModulesMode() {
return IS_DYNAMIC_MODULES;
}
public static boolean shouldUseWhitelist() {
return isFileExists(USE_WHITE_LIST);
}
public static boolean shouldUseCompatMode(String packageName) {
return isFileExists(COMPAT_LIST_PATH + packageName);
Boolean result;
if (compatModeCache.containsKey(packageName)
&& (result = compatModeCache.get(packageName)) != null) {
return result;
}
result = isFileExists(COMPAT_LIST_PATH + packageName);
compatModeCache.put(packageName, result);
return result;
}
public static boolean shouldHook(String packageName) {

View File

@ -10,7 +10,6 @@ import com.elderdrivers.riru.xposed.config.ConfigManager;
import java.util.HashMap;
import java.util.Map;
import de.robv.android.xposed.SELinuxHelper;
import external.com.android.dx.Code;
import external.com.android.dx.Local;
import external.com.android.dx.TypeId;
@ -156,8 +155,8 @@ public class DexMakerUtils {
code.loadConstant(booleanLocal, false);
code.loadConstant(byteLocal, (byte) 0);
code.loadConstant(charLocal, '\0');
code.loadConstant(doubleLocal,0.0);
code.loadConstant(floatLocal,0.0f);
code.loadConstant(doubleLocal, 0.0);
code.loadConstant(floatLocal, 0.0f);
code.loadConstant(intLocal, 0);
code.loadConstant(longLocal, 0L);
code.loadConstant(shortLocal, (short) 0);

View File

@ -27,6 +27,10 @@ public final class DynamicBridge {
private static File dexDir;
private static File dexOptDir;
/**
* Reset dexPathInited flag once we enter child process
* since it might have been set to true in zygote process
*/
public static void onForkPost() {
dexPathInited.set(false);
}
@ -46,36 +50,48 @@ public final class DynamicBridge {
try {
// for Android Oreo and later use InMemoryClassLoader
if (!shouldUseInMemoryHook()) {
// using file based DexClassLoader
if (dexPathInited.compareAndSet(false, true)) {
// delete previous compiled dex to prevent potential crashing
// TODO find a way to reuse them in consideration of performance
try {
// 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
String fixedAppDataDir = getDataPathPrefix() + getPackageName(Main.appDataDir) + "/";
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
+ getCurrentProcessName().replace(":", "_") + "/");
dexOptDir = new File(dexDir, "oat");
dexDir.mkdirs();
DexLog.d(Main.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath());
try {
FileUtils.delete(dexOptDir);
} catch (Throwable throwable) {
}
} catch (Throwable throwable) {
DexLog.e("error when init dex path", throwable);
}
}
setupDexCachePath();
}
dexMaker.start(hookMethod, additionalHookInfo,
hookMethod.getDeclaringClass().getClassLoader(), dexDir == null ? null : dexDir.getAbsolutePath());
hookMethod.getDeclaringClass().getClassLoader(), getDexDirPath());
hookedInfo.put(hookMethod, dexMaker.getCallBackupMethod());
} catch (Exception e) {
DexLog.e("error occur when generating dex. dexDir=" + dexDir, e);
}
}
private static String getDexDirPath() {
if (dexDir == null) {
return null;
}
return dexDir.getAbsolutePath();
}
private static void setupDexCachePath() {
// using file based DexClassLoader
if (!dexPathInited.compareAndSet(false, true)) {
return;
}
// delete previous compiled dex to prevent potential crashing
// TODO find a way to reuse them in consideration of performance
try {
// 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
String fixedAppDataDir = getDataPathPrefix() + getPackageName(Main.appDataDir) + "/";
dexDir = new File(fixedAppDataDir, "/cache/edhookers/"
+ getCurrentProcessName().replace(":", "_") + "/");
dexOptDir = new File(dexDir, "oat");
dexDir.mkdirs();
DexLog.d(Main.appProcessName + " deleting dir: " + dexOptDir.getAbsolutePath());
try {
FileUtils.delete(dexOptDir);
} catch (Throwable throwable) {
}
} catch (Throwable throwable) {
DexLog.e("error when init dex path", throwable);
}
}
private static boolean checkMember(Member member) {
if (member instanceof Method) {

View File

@ -30,7 +30,6 @@ import static com.elderdrivers.riru.xposed.dexmaker.DexMakerUtils.autoBoxIfNeces
import static com.elderdrivers.riru.xposed.dexmaker.DexMakerUtils.autoUnboxIfNecessary;
import static com.elderdrivers.riru.xposed.dexmaker.DexMakerUtils.createResultLocals;
import static com.elderdrivers.riru.xposed.dexmaker.DexMakerUtils.getObjTypeIdIfPrimitive;
import static com.elderdrivers.riru.xposed.dexmaker.DexMakerUtils.shouldUseInMemoryHook;
public class HookerDexMaker {
@ -193,14 +192,11 @@ public class HookerDexMaker {
generateCallBackupMethod();
ClassLoader loader;
if (shouldUseInMemoryHook()) {
if (TextUtils.isEmpty(mDexDirPath)) {
// in memory dex classloader
byte[] dexBytes = mDexMaker.generate();
loader = new InMemoryDexClassLoader(ByteBuffer.wrap(dexBytes), mAppClassLoader);
} else {
if (TextUtils.isEmpty(mDexDirPath)) {
throw new IllegalArgumentException("dexDirPath should not be empty!!!");
}
// Create the dex file and load it.
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath));
}

View File

@ -1,6 +1,8 @@
package com.elderdrivers.riru.xposed.util;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Process;
import android.text.TextUtils;
import java.io.BufferedReader;
@ -10,6 +12,8 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import static com.elderdrivers.riru.xposed.util.ProcessUtils.PER_USER_RANGE;
public class FileUtils {
public static final boolean IS_USING_PROTECTED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
@ -71,7 +75,11 @@ public class FileUtils {
return dataDir.substring(lastIndex + 1);
}
// FIXME: Although multi-users is considered here, but compat mode doesn't support other users' apps on Oreo and later yet.
@SuppressLint("SdCardPath")
public static String getDataPathPrefix() {
return IS_USING_PROTECTED_STORAGE ? "/data/user_de/0/" : "/data/data/";
int userId = Process.myUid() / PER_USER_RANGE;
String format = IS_USING_PROTECTED_STORAGE ? "/data/user_de/%d/" : "/data/user/%d/";
return String.format(format, userId);
}
}

View File

@ -12,6 +12,10 @@ import java.io.InputStreamReader;
public class ProcessUtils {
// Copied from UserHandle, indicates range of uids allocated for a user.
public static final int PER_USER_RANGE = 100000;
public static final int USER_SYSTEM = 0;
public static String getCurrentProcessName() {
String prettyName = Main.appProcessName;
if (!TextUtils.isEmpty(prettyName)) {