Fix Android 9 compatibility
This commit is contained in:
parent
dbc9d3aa61
commit
de06c1cf7c
|
|
@ -6,8 +6,8 @@
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="org.lsposed.lspatch.appstub.LSPApplicationStub"
|
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:appComponentFactory="org.lsposed.lspatch.appstub.LSPAppComponentFactoryStub"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/sample_app_title"
|
android:label="@string/sample_app_title"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ package org.lsposed.lspatch.loader;
|
||||||
|
|
||||||
import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
|
import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
|
||||||
import static org.lsposed.lspatch.share.Constants.ORIGINAL_APK_ASSET_PATH;
|
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 static org.lsposed.lspd.service.ConfigFileManager.loadModule;
|
import static org.lsposed.lspd.service.ConfigFileManager.loadModule;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
|
import android.app.AppComponentFactory;
|
||||||
import android.app.LoadedApk;
|
import android.app.LoadedApk;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
|
|
@ -43,6 +46,7 @@ import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import dalvik.system.DelegateLastClassLoader;
|
||||||
import de.robv.android.xposed.XC_MethodHook;
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import de.robv.android.xposed.XposedInit;
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
|
@ -61,6 +65,7 @@ public class LSPApplication extends ApplicationServiceClient {
|
||||||
private static String originalSignature = null;
|
private static String originalSignature = null;
|
||||||
private static ManagerResolver managerResolver = null;
|
private static ManagerResolver managerResolver = null;
|
||||||
private static Object activityThread;
|
private static Object activityThread;
|
||||||
|
private static LoadedApk loadedApkObj;
|
||||||
|
|
||||||
final static public int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
|
final static public int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
|
||||||
final static public int PER_USER_RANGE = 100000;
|
final static public int PER_USER_RANGE = 100000;
|
||||||
|
|
@ -86,6 +91,8 @@ public class LSPApplication extends ApplicationServiceClient {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initAppComponentFactory(context);
|
||||||
|
|
||||||
useManager = Boolean.parseBoolean(Objects.requireNonNull(FileUtils.readTextFromAssets(context, USE_MANAGER_CONTROL_PATH)));
|
useManager = Boolean.parseBoolean(Objects.requireNonNull(FileUtils.readTextFromAssets(context, USE_MANAGER_CONTROL_PATH)));
|
||||||
originalSignature = FileUtils.readTextFromAssets(context, ORIGINAL_SIGNATURE_ASSET_PATH);
|
originalSignature = FileUtils.readTextFromAssets(context, ORIGINAL_SIGNATURE_ASSET_PATH);
|
||||||
|
|
||||||
|
|
@ -114,6 +121,49 @@ public class LSPApplication extends ApplicationServiceClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DiscouragedPrivateApi")
|
||||||
|
private static void initAppComponentFactory(Context context) {
|
||||||
|
try {
|
||||||
|
ApplicationInfo aInfo = context.getApplicationInfo();
|
||||||
|
ClassLoader baseClassLoader = context.getClassLoader();
|
||||||
|
Class<?> 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) {
|
public static void disableProfile(Context context) {
|
||||||
final ArrayList<String> codePaths = new ArrayList<>();
|
final ArrayList<String> codePaths = new ArrayList<>();
|
||||||
var appInfo = context.getApplicationInfo();
|
var appInfo = context.getApplicationInfo();
|
||||||
|
|
@ -182,7 +232,7 @@ public class LSPApplication extends ApplicationServiceClient {
|
||||||
|
|
||||||
if (!Files.exists(Paths.get(cacheApkPath))) {
|
if (!Files.exists(Paths.get(cacheApkPath))) {
|
||||||
Log.i(TAG, "extract module apk: " + packageName);
|
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));
|
Files.createDirectories(Paths.get(modulePath));
|
||||||
try (var is = context.getAssets().open("modules/" + name)) {
|
try (var is = context.getAssets().open("modules/" + name)) {
|
||||||
Files.copy(is, Paths.get(cacheApkPath));
|
Files.copy(is, Paths.get(cacheApkPath));
|
||||||
|
|
@ -331,7 +381,7 @@ public class LSPApplication extends ApplicationServiceClient {
|
||||||
}
|
}
|
||||||
Field infoField = mBoundApplication.getClass().getDeclaredField("info"); // info
|
Field infoField = mBoundApplication.getClass().getDeclaredField("info"); // info
|
||||||
infoField.setAccessible(true);
|
infoField.setAccessible(true);
|
||||||
LoadedApk loadedApkObj = (LoadedApk) infoField.get(mBoundApplication); // LoadedApk
|
loadedApkObj = (LoadedApk) infoField.get(mBoundApplication); // LoadedApk
|
||||||
if (loadedApkObj == null) {
|
if (loadedApkObj == null) {
|
||||||
Log.e(TAG, "loadedApkObj null");
|
Log.e(TAG, "loadedApkObj null");
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,60 @@ package org.lsposed.lspatch.loader.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
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 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) {
|
public static String readTextFromAssets(Context context, String assetsFileName) {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
throw new IllegalStateException("context null");
|
throw new IllegalStateException("context null");
|
||||||
}
|
}
|
||||||
try (InputStream is = context.getAssets().open(assetsFileName)) {
|
try (InputStream is = context.getAssets().open(assetsFileName)) {
|
||||||
return org.lsposed.lspatch.share.FileUtils.readTextFromInputStream(is);
|
return readTextFromInputStream(is);
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':share')
|
|
||||||
implementation 'de.upb.cs.swt:axml:2.1.1'
|
implementation 'de.upb.cs.swt:axml:2.1.1'
|
||||||
compileOnly project(":hiddenapi-stubs")
|
compileOnly project(":hiddenapi-stubs")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
package org.lsposed.lspatch.appstub;
|
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.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AppComponentFactory;
|
import android.app.AppComponentFactory;
|
||||||
|
|
@ -11,78 +8,54 @@ import android.app.Service;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.lsposed.lspatch.share.FileUtils;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import java.io.InputStream;
|
@SuppressLint({"UnsafeDynamicallyLoadedCode", "DiscouragedPrivateApi"})
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
import dalvik.system.DelegateLastClassLoader;
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
public class LSPAppComponentFactoryStub extends AppComponentFactory {
|
public class LSPAppComponentFactoryStub extends AppComponentFactory {
|
||||||
private static final String TAG = "LSPatch";
|
private static final String TAG = "LSPatch";
|
||||||
private static final String PROXY_APPLICATION = "org.lsposed.lspatch.appstub.LSPApplicationStub";
|
|
||||||
|
|
||||||
private ClassLoader appClassLoader = null;
|
public static byte[] dex = null;
|
||||||
private ClassLoader baseClassLoader = null;
|
public static ClassLoader appClassLoader = null;
|
||||||
private AppComponentFactory originalAppComponentFactory = null;
|
public static ClassLoader baseClassLoader = null;
|
||||||
|
public static AppComponentFactory originalAppComponentFactory = null;
|
||||||
|
|
||||||
/**
|
public LSPAppComponentFactoryStub() {
|
||||||
* Instantiate original AppComponentFactory<br/>
|
baseClassLoader = getClass().getClassLoader();
|
||||||
* This method will be called at <b>instantiateClassLoader</b> by <b>createOrUpdateClassLoaderLocked</b>
|
try (var is = baseClassLoader.getResourceAsStream("assets/lsp");
|
||||||
**/
|
var os = new ByteArrayOutputStream()) {
|
||||||
private void initOriginalAppComponentFactory(ApplicationInfo aInfo) {
|
byte[] buffer = new byte[8192];
|
||||||
final String originPath = aInfo.dataDir + "/cache/lspatch/origin/";
|
int n;
|
||||||
final String originalAppComponentFactoryClass = FileUtils.readTextFromInputStream(baseClassLoader.getResourceAsStream(ORIGINAL_APP_COMPONENT_FACTORY_ASSET_PATH));
|
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 {
|
try {
|
||||||
String cacheApkPath;
|
Class<?> VMRuntime = Class.forName("dalvik.system.VMRuntime");
|
||||||
try (ZipFile sourceFile = new ZipFile(aInfo.sourceDir)) {
|
Method getRuntime = VMRuntime.getDeclaredMethod("getRuntime");
|
||||||
cacheApkPath = originPath + sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc();
|
getRuntime.setAccessible(true);
|
||||||
}
|
Method vmInstructionSet = VMRuntime.getDeclaredMethod("vmInstructionSet");
|
||||||
|
vmInstructionSet.setAccessible(true);
|
||||||
|
|
||||||
if (!Files.exists(Paths.get(cacheApkPath))) {
|
String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null));
|
||||||
Log.i(TAG, "extract original apk");
|
String path = baseClassLoader.getResource("assets/lib/lspd/" + arch + "/liblspd.so").getPath().substring(5);
|
||||||
FileUtils.deleteFolderIfExists(Paths.get(originPath));
|
System.load(path);
|
||||||
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);
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.e(TAG, "initOriginalAppComponentFactory", e);
|
Log.e("LSPatch", "load lspd error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassLoader instantiateClassLoader(ClassLoader cl, ApplicationInfo aInfo) {
|
public Application instantiateApplication(ClassLoader cl, String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
|
||||||
baseClassLoader = cl;
|
|
||||||
initOriginalAppComponentFactory(aInfo);
|
|
||||||
Log.d(TAG, "baseClassLoader is " + baseClassLoader);
|
Log.d(TAG, "baseClassLoader is " + baseClassLoader);
|
||||||
Log.d(TAG, "appClassLoader is " + appClassLoader);
|
Log.d(TAG, "appClassLoader is " + appClassLoader);
|
||||||
return originalAppComponentFactory.instantiateClassLoader(appClassLoader, aInfo);
|
Log.d(TAG, "originalAppComponentFactory is " + originalAppComponentFactory);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Application instantiateApplication(ClassLoader cl, String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
|
|
||||||
baseClassLoader.loadClass(PROXY_APPLICATION).newInstance();
|
|
||||||
Log.i(TAG, "lspd initialized, instantiate original application");
|
Log.i(TAG, "lspd initialized, instantiate original application");
|
||||||
return originalAppComponentFactory.instantiateApplication(cl, className);
|
return originalAppComponentFactory.instantiateApplication(cl, className);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -16,7 +16,7 @@ ext {
|
||||||
androidCompileSdkVersion = 31
|
androidCompileSdkVersion = 31
|
||||||
androidCompileNdkVersion = "23.0.7599858"
|
androidCompileNdkVersion = "23.0.7599858"
|
||||||
androidBuildToolsVersion = "31.0.0"
|
androidBuildToolsVersion = "31.0.0"
|
||||||
androidMinSdkVersion = 27
|
androidMinSdkVersion = 28
|
||||||
androidTargetSdkVersion = 30
|
androidTargetSdkVersion = 30
|
||||||
verCode = 1
|
verCode = 1
|
||||||
verName = "lspatch"
|
verName = "lspatch"
|
||||||
|
|
|
||||||
2
core
2
core
|
|
@ -1 +1 @@
|
||||||
Subproject commit 89eedddbd94af1240c14e6172e98c19bd4c7cafa
|
Subproject commit a6be0018e02bbd0d581c8a5f7ee9310bb8a6a711
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue