diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 81d756b..069c784 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,8 +6,8 @@
stubClass = Class.forName("org.lsposed.lspatch.appstub.LSPAppComponentFactoryStub", false, baseClassLoader);
+
+ String originPath = aInfo.dataDir + "/cache/lspatch/origin/";
+ String originalAppComponentFactoryClass = FileUtils.readTextFromInputStream(baseClassLoader.getResourceAsStream(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH));
+ String cacheApkPath;
+ try (ZipFile sourceFile = new ZipFile(aInfo.sourceDir)) {
+ cacheApkPath = originPath + sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc();
+ }
+ if (!Files.exists(Paths.get(cacheApkPath))) {
+ Log.i(TAG, "extract original apk");
+ FileUtils.deleteFolderIfExists(Paths.get(originPath));
+ Files.createDirectories(Paths.get(originPath));
+ try (InputStream is = baseClassLoader.getResourceAsStream(ORIGINAL_APK_ASSET_PATH)) {
+ Files.copy(is, Paths.get(cacheApkPath));
+ }
+ }
+ var appClassLoader = new DelegateLastClassLoader(cacheApkPath, aInfo.nativeLibraryDir, baseClassLoader.getParent());
+ AppComponentFactory originalAppComponentFactory;
+ try {
+ originalAppComponentFactory = (AppComponentFactory) appClassLoader.loadClass(originalAppComponentFactoryClass).newInstance();
+ } catch (ClassNotFoundException | NullPointerException ignored) {
+ if (originalAppComponentFactoryClass != null && !originalAppComponentFactoryClass.isEmpty())
+ Log.w(TAG, "original AppComponentFactory not found");
+ originalAppComponentFactory = new AppComponentFactory();
+ }
+ Field mClassLoaderField = LoadedApk.class.getDeclaredField("mClassLoader");
+ mClassLoaderField.setAccessible(true);
+ mClassLoaderField.set(loadedApkObj, appClassLoader);
+
+ stubClass.getDeclaredField("appClassLoader").set(null, appClassLoader);
+ stubClass.getDeclaredField("originalAppComponentFactory").set(null, originalAppComponentFactory);
+
+ Log.d(TAG, "set up original AppComponentFactory");
+ } catch (Throwable e) {
+ Log.e(TAG, "initAppComponentFactory", e);
+ }
+ }
+
public static void disableProfile(Context context) {
final ArrayList codePaths = new ArrayList<>();
var appInfo = context.getApplicationInfo();
@@ -182,7 +232,7 @@ public class LSPApplication extends ApplicationServiceClient {
if (!Files.exists(Paths.get(cacheApkPath))) {
Log.i(TAG, "extract module apk: " + packageName);
- org.lsposed.lspatch.share.FileUtils.deleteFolderIfExists(Paths.get(modulePath));
+ FileUtils.deleteFolderIfExists(Paths.get(modulePath));
Files.createDirectories(Paths.get(modulePath));
try (var is = context.getAssets().open("modules/" + name)) {
Files.copy(is, Paths.get(cacheApkPath));
@@ -331,7 +381,7 @@ public class LSPApplication extends ApplicationServiceClient {
}
Field infoField = mBoundApplication.getClass().getDeclaredField("info"); // info
infoField.setAccessible(true);
- LoadedApk loadedApkObj = (LoadedApk) infoField.get(mBoundApplication); // LoadedApk
+ loadedApkObj = (LoadedApk) infoField.get(mBoundApplication); // LoadedApk
if (loadedApkObj == null) {
Log.e(TAG, "loadedApkObj null");
return null;
diff --git a/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java b/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java
index 1b40415..d374a6c 100644
--- a/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java
+++ b/app/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java
@@ -2,16 +2,60 @@ package org.lsposed.lspatch.loader.util;
import android.content.Context;
+import java.io.BufferedReader;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
public class FileUtils {
+ public static String readTextFromInputStream(InputStream is) {
+ try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(reader)) {
+ StringBuilder builder = new StringBuilder();
+ String str;
+ while ((str = bufferedReader.readLine()) != null) {
+ builder.append(str);
+ }
+ return builder.toString();
+ } catch (Throwable ignored) {
+ }
+ return null;
+ }
+
+ public static void deleteFolderIfExists(Path target) throws IOException {
+ if (Files.notExists(target)) return;
+ Files.walkFileTree(target, new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+ throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir, IOException e)
+ throws IOException {
+ if (e == null) {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ } else {
+ throw e;
+ }
+ }
+ });
+ }
public static String readTextFromAssets(Context context, String assetsFileName) {
if (context == null) {
throw new IllegalStateException("context null");
}
try (InputStream is = context.getAssets().open(assetsFileName)) {
- return org.lsposed.lspatch.share.FileUtils.readTextFromInputStream(is);
+ return readTextFromInputStream(is);
} catch (Throwable ignored) {
}
return null;
diff --git a/appstub/build.gradle b/appstub/build.gradle
index db21a89..b216341 100644
--- a/appstub/build.gradle
+++ b/appstub/build.gradle
@@ -50,7 +50,6 @@ android {
}
dependencies {
- implementation project(':share')
implementation 'de.upb.cs.swt:axml:2.1.1'
compileOnly project(":hiddenapi-stubs")
}
diff --git a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java
index 7b4a8e1..df41c30 100644
--- a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java
+++ b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub.java
@@ -1,8 +1,5 @@
package org.lsposed.lspatch.appstub;
-import static org.lsposed.lspatch.share.Constants.ORIGINAL_APK_ASSET_PATH;
-import static org.lsposed.lspatch.share.Constants.ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH;
-
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AppComponentFactory;
@@ -11,78 +8,54 @@ import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.util.Log;
-import org.lsposed.lspatch.share.FileUtils;
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Method;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.zip.ZipFile;
-
-import dalvik.system.DelegateLastClassLoader;
-
-@SuppressLint("NewApi")
+@SuppressLint({"UnsafeDynamicallyLoadedCode", "DiscouragedPrivateApi"})
public class LSPAppComponentFactoryStub extends AppComponentFactory {
private static final String TAG = "LSPatch";
- private static final String PROXY_APPLICATION = "org.lsposed.lspatch.appstub.LSPApplicationStub";
- private ClassLoader appClassLoader = null;
- private ClassLoader baseClassLoader = null;
- private AppComponentFactory originalAppComponentFactory = null;
+ public static byte[] dex = null;
+ public static ClassLoader appClassLoader = null;
+ public static ClassLoader baseClassLoader = null;
+ public static AppComponentFactory originalAppComponentFactory = null;
- /**
- * Instantiate original AppComponentFactory
- * This method will be called at instantiateClassLoader by createOrUpdateClassLoaderLocked
- **/
- private void initOriginalAppComponentFactory(ApplicationInfo aInfo) {
- final String originPath = aInfo.dataDir + "/cache/lspatch/origin/";
- final String originalAppComponentFactoryClass = FileUtils.readTextFromInputStream(baseClassLoader.getResourceAsStream(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH));
+ public LSPAppComponentFactoryStub() {
+ baseClassLoader = getClass().getClassLoader();
+ try (var is = baseClassLoader.getResourceAsStream("assets/lsp");
+ var os = new ByteArrayOutputStream()) {
+ byte[] buffer = new byte[8192];
+ int n;
+ while (-1 != (n = is.read(buffer))) {
+ os.write(buffer, 0, n);
+ }
+ dex = os.toByteArray();
+ } catch (Throwable e) {
+ Log.e("LSPatch", "load dex error", e);
+ }
try {
- String cacheApkPath;
- try (ZipFile sourceFile = new ZipFile(aInfo.sourceDir)) {
- cacheApkPath = originPath + sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc();
- }
+ Class> VMRuntime = Class.forName("dalvik.system.VMRuntime");
+ Method getRuntime = VMRuntime.getDeclaredMethod("getRuntime");
+ getRuntime.setAccessible(true);
+ Method vmInstructionSet = VMRuntime.getDeclaredMethod("vmInstructionSet");
+ vmInstructionSet.setAccessible(true);
- if (!Files.exists(Paths.get(cacheApkPath))) {
- Log.i(TAG, "extract original apk");
- FileUtils.deleteFolderIfExists(Paths.get(originPath));
- Files.createDirectories(Paths.get(originPath));
- try (InputStream is = baseClassLoader.getResourceAsStream(ORIGINAL_APK_ASSET_PATH)) {
- Files.copy(is, Paths.get(cacheApkPath));
- }
- }
-
- appClassLoader = new DelegateLastClassLoader(cacheApkPath, aInfo.nativeLibraryDir, baseClassLoader);
-
- try {
- originalAppComponentFactory = (AppComponentFactory) appClassLoader.loadClass(originalAppComponentFactoryClass).newInstance();
- } catch (ClassNotFoundException | NullPointerException ignored) {
- if (originalAppComponentFactoryClass != null && !originalAppComponentFactoryClass.isEmpty())
- Log.w(TAG, "original AppComponentFactory not found");
- originalAppComponentFactory = new AppComponentFactory();
- }
-
- Log.d(TAG, "instantiate original AppComponentFactory: " + originalAppComponentFactory);
+ String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null));
+ String path = baseClassLoader.getResource("assets/lib/lspd/" + arch + "/liblspd.so").getPath().substring(5);
+ System.load(path);
} catch (Throwable e) {
- Log.e(TAG, "initOriginalAppComponentFactory", e);
+ Log.e("LSPatch", "load lspd error", e);
}
}
@Override
- public ClassLoader instantiateClassLoader(ClassLoader cl, ApplicationInfo aInfo) {
- baseClassLoader = cl;
- initOriginalAppComponentFactory(aInfo);
+ public Application instantiateApplication(ClassLoader cl, String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
Log.d(TAG, "baseClassLoader is " + baseClassLoader);
Log.d(TAG, "appClassLoader is " + appClassLoader);
- return originalAppComponentFactory.instantiateClassLoader(appClassLoader, aInfo);
- }
-
- @Override
- public Application instantiateApplication(ClassLoader cl, String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
- baseClassLoader.loadClass(PROXY_APPLICATION).newInstance();
+ Log.d(TAG, "originalAppComponentFactory is " + originalAppComponentFactory);
Log.i(TAG, "lspd initialized, instantiate original application");
return originalAppComponentFactory.instantiateApplication(cl, className);
}
diff --git a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPApplicationStub.java b/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPApplicationStub.java
deleted file mode 100644
index 84f580e..0000000
--- a/appstub/src/main/java/org/lsposed/lspatch/appstub/LSPApplicationStub.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.lsposed.lspatch.appstub;
-
-import android.annotation.SuppressLint;
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-import java.lang.reflect.Method;
-
-@SuppressLint("UnsafeDynamicallyLoadedCode")
-public class LSPApplicationStub {
-
- private static byte[] dex = null;
-
- static {
- try (var is = LSPApplicationStub.class.getClassLoader().getResourceAsStream("assets/lsp");
- var os = new ByteArrayOutputStream()) {
- byte[] buffer = new byte[8192];
- int n;
- while (-1 != (n = is.read(buffer))) {
- os.write(buffer, 0, n);
- }
- dex = os.toByteArray();
- } catch (Throwable e) {
- Log.e("LSPatch", "load dex error", e);
- }
-
- try {
- Class> VMRuntime = Class.forName("dalvik.system.VMRuntime");
- Method getRuntime = VMRuntime.getDeclaredMethod("getRuntime");
- getRuntime.setAccessible(true);
- Method vmInstructionSet = VMRuntime.getDeclaredMethod("vmInstructionSet");
- vmInstructionSet.setAccessible(true);
-
- String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null));
- String path = LSPApplicationStub.class.getClassLoader().getResource("assets/lib/lspd/" + arch + "/liblspd.so").getPath().substring(5);
- System.load(path);
- } catch (Throwable e) {
- Log.e("LSPatch", "load lspd error", e);
- }
- }
-}
diff --git a/build.gradle b/build.gradle
index 061482f..4229e98 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ ext {
androidCompileSdkVersion = 31
androidCompileNdkVersion = "23.0.7599858"
androidBuildToolsVersion = "31.0.0"
- androidMinSdkVersion = 27
+ androidMinSdkVersion = 28
androidTargetSdkVersion = 30
verCode = 1
verName = "lspatch"
diff --git a/core b/core
index 89eeddd..a6be001 160000
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit 89eedddbd94af1240c14e6172e98c19bd4c7cafa
+Subproject commit a6be0018e02bbd0d581c8a5f7ee9310bb8a6a711
diff --git a/share/src/main/java/org/lsposed/lspatch/share/FileUtils.java b/share/src/main/java/org/lsposed/lspatch/share/FileUtils.java
deleted file mode 100644
index 5d14ba4..0000000
--- a/share/src/main/java/org/lsposed/lspatch/share/FileUtils.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.lsposed.lspatch.share;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-
-public class FileUtils {
- public static String readTextFromInputStream(InputStream is) {
- try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(reader)) {
- StringBuilder builder = new StringBuilder();
- String str;
- while ((str = bufferedReader.readLine()) != null) {
- builder.append(str);
- }
- return builder.toString();
- } catch (Throwable ignored) {
- }
- return null;
- }
-
- public static void deleteFolderIfExists(Path target) throws IOException {
- if (Files.notExists(target)) return;
- Files.walkFileTree(target, new SimpleFileVisitor<>() {
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
- throws IOException {
- Files.delete(file);
- return FileVisitResult.CONTINUE;
- }
-
- @Override
- public FileVisitResult postVisitDirectory(Path dir, IOException e)
- throws IOException {
- if (e == null) {
- Files.delete(dir);
- return FileVisitResult.CONTINUE;
- } else {
- throw e;
- }
- }
- });
- }
-}
\ No newline at end of file