Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opt: dexkit cache #1037

Merged
merged 5 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ public void init(LoadPackageParam lpparam) {
}

mLoadPackageParam = lpparam;
DexKit dexKit = new DexKit(lpparam, TAG);
initZygote();
handleLoadPackage();

if (dexKit.isInit) {
dexKit.close();
DexKit.ready(lpparam, TAG);
try {
initZygote();
handleLoadPackage();
} catch (Throwable e) {
DexKit.close();
throw new RuntimeException(e);
}
DexKit.close();
}

/*public void initHook(BaseHook baseHook) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.sevtinge.hyperceiler.R;
import com.tencent.mmkv.MMKV;

Expand All @@ -45,44 +47,61 @@

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
* 使用 MMKV 工具缓存 Dexkit 结果的工具类。
* 缓存路径为 /data/data/<App Package Name>/files/mmkv
*
* @author 焕晨HChen
* @co-author Ling Qiqi
*/
public class DexKit {
private static final int mVersion = 7;
public boolean isInit = false;
private static final String MMKV_PATH = "/files/mmkv";
private static String TAG = "DexKit";
private static boolean isInit = false;
private static final int mVersion = 1; // 新时节,新气象
private static final String MMKV_PATH = "/files/hyperceiler/mmkv";
private static XC_LoadPackage.LoadPackageParam mParam;
private static final String TYPE_METHOD = "METHOD";
private static final String TYPE_CLASS = "CLASS";
private static final String TYPE_FIELD = "FIELD";
private static MMKV mMMKV = null;
private static DexKit mThis;
private static Gson mGson = null;
private static DexKitBridge mDexKitBridge;
private final String TAG;

public DexKit(XC_LoadPackage.LoadPackageParam param, String tag) {
public static void ready(XC_LoadPackage.LoadPackageParam param, String tag) {
mParam = param;
TAG = tag;
mThis = this;
isInit = false;
}

@NotNull
public static DexKitBridge initDexkitBridge() {
if (mDexKitBridge == null) {
if (mThis == null) {
throw new RuntimeException("InitDexKit is null!!");
} else {
mThis.init();
}
if (mDexKitBridge != null)
return mDexKitBridge;
if (isInit)
throw new RuntimeException(TAG + ": mDexKitBridge is null!");
if (mParam == null)
throw new RuntimeException(TAG + ": lpparam is null!");

String hostDir = mParam.appInfo.sourceDir,
mmkvPath = mParam.appInfo.dataDir + MMKV_PATH;
mGson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create();

// 启动 MMKV
MMKV.initialize(mmkvPath, System::loadLibrary);
mMMKV = MMKV.defaultMMKV();
int version = mMMKV.getInt("version", 0);
if (version != 0 && version != mVersion) {
mMMKV.clearAll();
}
mMMKV.putInt("version", mVersion);

// 启动 DexKit
System.loadLibrary("dexkit");
mDexKitBridge = DexKitBridge.create(hostDir);
isInit = true;

return mDexKitBridge;
}

Expand All @@ -95,40 +114,36 @@ public static <T> T findMember(@NonNull String key, IDexKit iDexKit) {

public static <T> T findMember(@NonNull String key, ClassLoader classLoader, IDexKit iDexKit) {
DexKitBridge dexKitBridge = initDexkitBridge();
String json = mMMKV.getString(key, "");
if (json.isEmpty()) {
String descriptor = mMMKV.getString(key, "");
if (descriptor.isEmpty()) {
try {
BaseData baseData = iDexKit.dexkit(dexKitBridge);
if (baseData instanceof FieldData fieldData) {
saveToMMKV(key, TYPE_FIELD, List.of(fieldData.toDexField().serialize()));
mMMKV.putString(key, mGson.toJson(new MemberData(TYPE_FIELD, fieldData.toDexField().serialize())));
return (T) fieldData.getFieldInstance(classLoader);
} else if (baseData instanceof MethodData methodData) {
saveToMMKV(key, TYPE_METHOD, List.of(methodData.toDexMethod().serialize()));
mMMKV.putString(key, mGson.toJson(new MemberData(TYPE_METHOD, methodData.toDexMethod().serialize())));
return (T) methodData.getMethodInstance(classLoader);
} else if (baseData instanceof ClassData classData) {
saveToMMKV(key, TYPE_CLASS, List.of(classData.toDexType().serialize()));
mMMKV.putString(key, mGson.toJson(new MemberData(TYPE_CLASS, classData.toDexType().serialize())));
return (T) classData.getInstance(classLoader);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
} else {
MemberData data = mGson.fromJson(descriptor, new TypeToken<MemberData>() {
}.getType());
try {
JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
for (String jsonKey : jsonObject.keySet()) {
JsonObject item = jsonObject.getAsJsonObject(jsonKey);
String type = item.get("type").getAsString();
String serializedItem = item.get("name").getAsString();
switch (type) {
case TYPE_METHOD -> {
return (T) new DexMethod(serializedItem).getMethodInstance(classLoader);
}
case TYPE_FIELD -> {
return (T) new DexField(serializedItem).getFieldInstance(classLoader);
}
case TYPE_CLASS -> {
return (T) new DexClass(serializedItem).getInstance(classLoader);
}
switch (data.type) {
case TYPE_METHOD -> {
return (T) new DexMethod(data.serialize).getMethodInstance(classLoader);
}
case TYPE_FIELD -> {
return (T) new DexField(data.serialize).getFieldInstance(classLoader);
}
case TYPE_CLASS -> {
return (T) new DexClass(data.serialize).getInstance(classLoader);
}
}
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) {
Expand All @@ -144,8 +159,8 @@ public static <T> List<T> findMemberList(@NonNull String key, IDexKitList iDexKi

public static <T> List<T> findMemberList(@NonNull String key, ClassLoader classLoader, IDexKitList iDexKitList) {
DexKitBridge dexKitBridge = initDexkitBridge();
String json = mMMKV.getString(key, "");
if (json.isEmpty()) {
String descriptor = mMMKV.getString(key, "");
if (descriptor.isEmpty()) {
try {
BaseDataList<?> baseDataList = iDexKitList.dexkit(dexKitBridge);
ArrayList<String> serializeList = new ArrayList<>();
Expand All @@ -155,90 +170,75 @@ public static <T> List<T> findMemberList(@NonNull String key, ClassLoader classL
serializeList.add(f.toDexField().serialize());
instanceList.add((T) f.getFieldInstance(classLoader));
}
saveToMMKV(key, TYPE_FIELD, serializeList);
mMMKV.putString(key, mGson.toJson(new MemberData(TYPE_FIELD, serializeList)));
return instanceList;
} else if (baseDataList instanceof MethodDataList methodDataList) {
for (MethodData m : methodDataList) {
serializeList.add(m.toDexMethod().serialize());
instanceList.add((T) m.getMethodInstance(classLoader));
}
saveToMMKV(key, TYPE_METHOD, serializeList);
mMMKV.putString(key, mGson.toJson(new MemberData(TYPE_METHOD, serializeList)));
return instanceList;
} else if (baseDataList instanceof ClassDataList classDataList) {
for (ClassData c : classDataList) {
serializeList.add(c.toDexType().serialize());
instanceList.add((T) c.getInstance(classLoader));
}
saveToMMKV(key, TYPE_CLASS, serializeList);
mMMKV.putString(key, mGson.toJson(new MemberData(TYPE_CLASS, serializeList)));
return instanceList;
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
} else {
MemberData data = mGson.fromJson(descriptor, new TypeToken<MemberData>() {
}.getType());
ArrayList<T> instanceList = new ArrayList<>();
try {
JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
for (String jsonKey : jsonObject.keySet()) {
JsonObject item = jsonObject.getAsJsonObject(jsonKey);
String type = item.get("type").getAsString();
String serializedItem = item.get("name").getAsString();
switch (type) {
case TYPE_METHOD -> instanceList.add((T) new DexMethod(serializedItem).getMethodInstance(classLoader));
case TYPE_FIELD -> instanceList.add((T) new DexField(serializedItem).getFieldInstance(classLoader));
case TYPE_CLASS -> instanceList.add((T) new DexClass(serializedItem).getInstance(classLoader));
switch (data.type) {
case TYPE_METHOD -> {
for (String s : data.serializeList) {
instanceList.add((T) new DexMethod(s).getMethodInstance(classLoader));
}
return instanceList;
}
case TYPE_FIELD -> {
for (String s : data.serializeList) {
instanceList.add((T) new DexField(s).getFieldInstance(classLoader));
}
return instanceList;
}
case TYPE_CLASS -> {
for (String s : data.serializeList) {
instanceList.add((T) new DexClass(s).getInstance(classLoader));
}
return instanceList;
}
}
return instanceList;
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
return new ArrayList<>();
}

private static void saveToMMKV(String key, String type, List<String> serializeList) {
JsonObject jsonObject = new JsonObject();
for (int i = 0; i < serializeList.size(); i++) {
JsonObject item = new JsonObject();
item.addProperty("type", type);
item.addProperty("name", serializeList.get(i));
jsonObject.add(String.valueOf(i), item);
}
mMMKV.putString(key, jsonObject + "\n\n");
}

private void init() {
if (mDexKitBridge != null) {
return;
}

if (mParam == null) {
throw new RuntimeException(TAG != null ? TAG : "InitDexKit" + ": lpparam is null");
}

String hostDir = mParam.appInfo.sourceDir;
String mmkvPath = mParam.appInfo.dataDir + MMKV_PATH;

// 启动 MMKV
MMKV.initialize(mmkvPath, System::loadLibrary);
mMMKV = MMKV.defaultMMKV();
int version = mMMKV.getInt("version", 0);
if (version != 0 && version != mVersion) {
mMMKV.clearAll();
}
mMMKV.putInt("version", mVersion);

// 启动 DexKit
System.loadLibrary("dexkit");
mDexKitBridge = DexKitBridge.create(hostDir);
isInit = true;
public static void deleteAllCache(Context context) {
String[] folderNames = context.getResources().getStringArray(R.array.xposed_scope);
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
for (String folderName : folderNames) {
String folderPath = "/data/data/" + folderName + MMKV_PATH;
safeExecCommandWithRoot("rm -rf " + folderPath);
}
});
}

/**
* 请勿手动调用。
*/
public void close() {
public static void close() {
if (!isInit) return;

if (mDexKitBridge != null) {
mDexKitBridge.close();
mDexKitBridge = null;
Expand All @@ -248,17 +248,38 @@ public void close() {
mMMKV = null;
}
mParam = null;
mThis = null;
mGson = null;
isInit = false;
}

public static void deleteAllCache(Context context) {
String[] folderNames = context.getResources().getStringArray(R.array.xposed_scope);
for (String folderName : folderNames) {
String folderPath = "/data/data/" + folderName + MMKV_PATH;
if (safeExecCommandWithRoot("ls " + folderPath).contains("mmkv")) {
safeExecCommandWithRoot("rm -rf " + folderPath);
}
private static final class MemberData {
public String type = "";
public String serialize = "";

public ArrayList<String> serializeList = new ArrayList<>();

public MemberData(String type, String serialize) {
this.type = type;
this.serialize = serialize;
}

public MemberData(String type, ArrayList<String> serializeList) {
this.type = type;
this.serializeList = serializeList;
}

@NonNull
@Override
public String toString() {
return "Type: " + type + ", Serialize: " + serialize + ", SerializeList: " + serializeList;
}

@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof MemberData memberData
&& memberData.type.equals(type)
&& memberData.serialize.equals(serialize)
&& memberData.serializeList.equals(serializeList);
}
}
}
Loading
Loading