Support bootstrapping loader dynamically for local mode (#118)
This commit is contained in:
parent
91b038ac16
commit
f8888b81e8
|
|
@ -1,4 +0,0 @@
|
||||||
-keep class org.lsposed.lspatch.appstub.LSPAppComponentFactoryStub {
|
|
||||||
public static byte[] dex;
|
|
||||||
<init>();
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="org.lsposed.lspatch.appstub">
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
package org.lsposed.lspatch.appstub;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.AppComponentFactory;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
|
||||||
public class LSPAppComponentFactoryStub extends AppComponentFactory {
|
|
||||||
public static byte[] dex = null;
|
|
||||||
|
|
||||||
static {
|
|
||||||
var cl = Objects.requireNonNull(LSPAppComponentFactoryStub.class.getClassLoader());
|
|
||||||
try (var is = cl.getResourceAsStream("assets/lspatch/lsp.dex");
|
|
||||||
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 = cl.getResource("assets/lspatch/so/" + arch + "/liblspatch.so").getPath().substring(5);
|
|
||||||
System.load(path);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new ExceptionInInitializerError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -58,7 +58,7 @@ tasks.register<Delete>("clean") {
|
||||||
listOf("Debug", "Release").forEach { variant ->
|
listOf("Debug", "Release").forEach { variant ->
|
||||||
tasks.register("build$variant") {
|
tasks.register("build$variant") {
|
||||||
description = "Build LSPatch with $variant"
|
description = "Build LSPatch with $variant"
|
||||||
dependsOn(projects.patchJar.dependencyProject.tasks["build$variant"])
|
dependsOn(projects.jar.dependencyProject.tasks["build$variant"])
|
||||||
dependsOn(projects.manager.dependencyProject.tasks["build$variant"])
|
dependsOn(projects.manager.dependencyProject.tasks["build$variant"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,13 @@ fun Jar.configure(variant: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register<Jar>("buildDebug") {
|
tasks.register<Jar>("buildDebug") {
|
||||||
dependsOn(":appstub:copyDebug")
|
dependsOn(":meta-loader:copyDebug")
|
||||||
dependsOn(":patch-loader:copyDebug")
|
dependsOn(":patch-loader:copyDebug")
|
||||||
configure("debug")
|
configure("debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register<Jar>("buildRelease") {
|
tasks.register<Jar>("buildRelease") {
|
||||||
dependsOn(":appstub:copyRelease")
|
dependsOn(":meta-loader:copyRelease")
|
||||||
dependsOn(":patch-loader:copyRelease")
|
dependsOn(":patch-loader:copyRelease")
|
||||||
configure("release")
|
configure("release")
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +18,10 @@ android {
|
||||||
applicationId = defaultManagerPackageName
|
applicationId = defaultManagerPackageName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidResources {
|
||||||
|
noCompress.add(".so")
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
|
|
@ -58,7 +62,7 @@ afterEvaluate {
|
||||||
val variantCapped = variant.name.capitalize()
|
val variantCapped = variant.name.capitalize()
|
||||||
|
|
||||||
task<Copy>("copy${variantCapped}Assets") {
|
task<Copy>("copy${variantCapped}Assets") {
|
||||||
dependsOn(":appstub:copy$variantCapped")
|
dependsOn(":meta-loader:copy$variantCapped")
|
||||||
dependsOn(":patch-loader:copy$variantCapped")
|
dependsOn(":patch-loader:copy$variantCapped")
|
||||||
tasks["merge${variantCapped}Assets"].dependsOn(this)
|
tasks["merge${variantCapped}Assets"].dependsOn(this)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package org.lsposed.lspatch
|
|
||||||
|
|
||||||
object Constants {
|
|
||||||
|
|
||||||
const val PATCH_FILE_SUFFIX = "-lspatched.apk"
|
|
||||||
}
|
|
||||||
|
|
@ -4,9 +4,9 @@ import androidx.core.net.toUri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX
|
|
||||||
import org.lsposed.lspatch.config.Configs
|
import org.lsposed.lspatch.config.Configs
|
||||||
import org.lsposed.lspatch.config.MyKeyStore
|
import org.lsposed.lspatch.config.MyKeyStore
|
||||||
|
import org.lsposed.lspatch.share.Constants
|
||||||
import org.lsposed.lspatch.share.PatchConfig
|
import org.lsposed.lspatch.share.PatchConfig
|
||||||
import org.lsposed.patch.LSPatch
|
import org.lsposed.patch.LSPatch
|
||||||
import org.lsposed.patch.util.Logger
|
import org.lsposed.patch.util.Logger
|
||||||
|
|
@ -47,10 +47,10 @@ object Patcher {
|
||||||
val root = DocumentFile.fromTreeUri(lspApp, uri)
|
val root = DocumentFile.fromTreeUri(lspApp, uri)
|
||||||
?: throw IOException("DocumentFile is null")
|
?: throw IOException("DocumentFile is null")
|
||||||
root.listFiles().forEach {
|
root.listFiles().forEach {
|
||||||
if (it.name?.endsWith(PATCH_FILE_SUFFIX) == true) it.delete()
|
if (it.name?.endsWith(Constants.PATCH_FILE_SUFFIX) == true) it.delete()
|
||||||
}
|
}
|
||||||
lspApp.tmpApkDir.walk()
|
lspApp.tmpApkDir.walk()
|
||||||
.filter { it.name.endsWith(PATCH_FILE_SUFFIX) }
|
.filter { it.name.endsWith(Constants.PATCH_FILE_SUFFIX) }
|
||||||
.forEach { apk ->
|
.forEach { apk ->
|
||||||
val file = root.createFile("application/vnd.android.package-archive", apk.name)
|
val file = root.createFile("application/vnd.android.package-archive", apk.name)
|
||||||
?: throw IOException("Failed to create output file")
|
?: throw IOException("Failed to create output file")
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,14 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardCapslock
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
|
@ -41,6 +43,7 @@ import org.lsposed.lspatch.config.ConfigManager
|
||||||
import org.lsposed.lspatch.config.Configs
|
import org.lsposed.lspatch.config.Configs
|
||||||
import org.lsposed.lspatch.database.entity.Module
|
import org.lsposed.lspatch.database.entity.Module
|
||||||
import org.lsposed.lspatch.lspApp
|
import org.lsposed.lspatch.lspApp
|
||||||
|
import org.lsposed.lspatch.share.Constants
|
||||||
import org.lsposed.lspatch.share.LSPConfig
|
import org.lsposed.lspatch.share.LSPConfig
|
||||||
import org.lsposed.lspatch.ui.component.AnywhereDropdown
|
import org.lsposed.lspatch.ui.component.AnywhereDropdown
|
||||||
import org.lsposed.lspatch.ui.component.AppItem
|
import org.lsposed.lspatch.ui.component.AppItem
|
||||||
|
|
@ -136,6 +139,8 @@ fun AppManageBody(
|
||||||
items = viewModel.appList,
|
items = viewModel.appList,
|
||||||
key = { it.first.app.packageName }
|
key = { it.first.app.packageName }
|
||||||
) {
|
) {
|
||||||
|
val isRolling = it.second.useManager && it.second.lspConfig.VERSION_CODE >= Constants.MIN_ROLLING_VERSION_CODE
|
||||||
|
val canUpdateLoader = !isRolling && it.second.lspConfig.VERSION_CODE < LSPConfig.instance.VERSION_CODE
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
AnywhereDropdown(
|
AnywhereDropdown(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
|
|
@ -148,6 +153,7 @@ fun AppManageBody(
|
||||||
label = it.first.label,
|
label = it.first.label,
|
||||||
packageName = it.first.app.packageName,
|
packageName = it.first.app.packageName,
|
||||||
additionalContent = {
|
additionalContent = {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = buildAnnotatedString {
|
text = buildAnnotatedString {
|
||||||
val (text, color) =
|
val (text, color) =
|
||||||
|
|
@ -155,12 +161,20 @@ fun AppManageBody(
|
||||||
else stringResource(R.string.patch_portable) to MaterialTheme.colorScheme.tertiary
|
else stringResource(R.string.patch_portable) to MaterialTheme.colorScheme.tertiary
|
||||||
append(AnnotatedString(text, SpanStyle(color = color)))
|
append(AnnotatedString(text, SpanStyle(color = color)))
|
||||||
append(" ")
|
append(" ")
|
||||||
append(it.second.lspConfig.VERSION_CODE.toString())
|
if (isRolling) append(stringResource(R.string.manage_rolling))
|
||||||
|
else append(it.second.lspConfig.VERSION_CODE.toString())
|
||||||
},
|
},
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
fontFamily = FontFamily.Serif,
|
fontFamily = FontFamily.Serif,
|
||||||
style = MaterialTheme.typography.bodySmall
|
style = MaterialTheme.typography.bodySmall
|
||||||
)
|
)
|
||||||
|
if (canUpdateLoader) {
|
||||||
|
with(LocalDensity.current) {
|
||||||
|
val size = MaterialTheme.typography.bodySmall.fontSize * 1.2
|
||||||
|
Icon(Icons.Filled.KeyboardCapslock, null, Modifier.size(size.toDp()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +184,7 @@ fun AppManageBody(
|
||||||
onClick = {}, enabled = false
|
onClick = {}, enabled = false
|
||||||
)
|
)
|
||||||
val shizukuUnavailable = stringResource(R.string.shizuku_unavailable)
|
val shizukuUnavailable = stringResource(R.string.shizuku_unavailable)
|
||||||
if (it.second.lspConfig.VERSION_CODE < LSPConfig.instance.VERSION_CODE || BuildConfig.DEBUG) {
|
if (canUpdateLoader || BuildConfig.DEBUG) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.manage_update_loader)) },
|
text = { Text(stringResource(R.string.manage_update_loader)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import me.zhanghai.android.appiconloader.AppIconLoader
|
import me.zhanghai.android.appiconloader.AppIconLoader
|
||||||
import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX
|
|
||||||
import org.lsposed.lspatch.config.ConfigManager
|
import org.lsposed.lspatch.config.ConfigManager
|
||||||
import org.lsposed.lspatch.config.Configs
|
import org.lsposed.lspatch.config.Configs
|
||||||
import org.lsposed.lspatch.lspApp
|
import org.lsposed.lspatch.lspApp
|
||||||
|
import org.lsposed.lspatch.share.Constants
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
|
|
@ -91,7 +91,7 @@ object LSPPackageManager {
|
||||||
val uri = Configs.storageDirectory?.toUri() ?: throw IOException("Uri is null")
|
val uri = Configs.storageDirectory?.toUri() ?: throw IOException("Uri is null")
|
||||||
val root = DocumentFile.fromTreeUri(lspApp, uri) ?: throw IOException("DocumentFile is null")
|
val root = DocumentFile.fromTreeUri(lspApp, uri) ?: throw IOException("DocumentFile is null")
|
||||||
root.listFiles().forEach { file ->
|
root.listFiles().forEach { file ->
|
||||||
if (file.name?.endsWith(PATCH_FILE_SUFFIX) != true) return@forEach
|
if (file.name?.endsWith(Constants.PATCH_FILE_SUFFIX) != true) return@forEach
|
||||||
Log.d(TAG, "Add ${file.name}")
|
Log.d(TAG, "Add ${file.name}")
|
||||||
val input = lspApp.contentResolver.openInputStream(file.uri)
|
val input = lspApp.contentResolver.openInputStream(file.uri)
|
||||||
?: throw IOException("Cannot open input stream")
|
?: throw IOException("Cannot open input stream")
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
<string name="screen_manage">Manage</string>
|
<string name="screen_manage">Manage</string>
|
||||||
<string name="manage_loading">Loading</string>
|
<string name="manage_loading">Loading</string>
|
||||||
<string name="manage_no_apps">No patched apps yet</string>
|
<string name="manage_no_apps">No patched apps yet</string>
|
||||||
|
<string name="manage_rolling">Rolling</string>
|
||||||
<string name="manage_update_loader">Update loader</string>
|
<string name="manage_update_loader">Update loader</string>
|
||||||
<string name="manage_update_loader_successfully">Update successfully</string>
|
<string name="manage_update_loader_successfully">Update successfully</string>
|
||||||
<string name="manage_update_loader_failed">Update failed</string>
|
<string name="manage_update_loader_failed">Update failed</string>
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ androidComponents.onVariants { variant ->
|
||||||
"$buildDir/intermediates/dex/$variantLowered/minify${variantCapped}WithR8" else
|
"$buildDir/intermediates/dex/$variantLowered/minify${variantCapped}WithR8" else
|
||||||
"$buildDir/intermediates/dex/$variantLowered/mergeDex$variantCapped"
|
"$buildDir/intermediates/dex/$variantLowered/mergeDex$variantCapped"
|
||||||
from(dexOutPath)
|
from(dexOutPath)
|
||||||
rename("classes.dex", "loader.dex")
|
rename("classes.dex", "metaloader.dex")
|
||||||
into("${rootProject.projectDir}/out/assets/dex")
|
into("${rootProject.projectDir}/out/assets/lspatch")
|
||||||
}
|
}
|
||||||
|
|
||||||
task("copy$variantCapped") {
|
task("copy$variantCapped") {
|
||||||
|
|
@ -41,4 +41,5 @@ androidComponents.onVariants { variant ->
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(projects.hiddenapi.stubs)
|
compileOnly(projects.hiddenapi.stubs)
|
||||||
|
implementation(projects.share.java)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
-keep class org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub {
|
||||||
|
public static byte[] dex;
|
||||||
|
<init>();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="org.lsposed.lspatch.metaloader" />
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
package org.lsposed.lspatch.metaloader;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.AppComponentFactory;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.IPackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.ServiceManager;
|
||||||
|
import android.util.JsonReader;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.lsposed.lspatch.share.Constants;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||||
|
public class LSPAppComponentFactoryStub extends AppComponentFactory {
|
||||||
|
|
||||||
|
private static final String TAG = "LSPatch-MetaLoader";
|
||||||
|
private static final Map<String, String> archToLib = Map.of(
|
||||||
|
"arm", "armeabi-v7a",
|
||||||
|
"arm64", "arm64-v8a",
|
||||||
|
"x86", "x86",
|
||||||
|
"x86_64", "x86_64"
|
||||||
|
);
|
||||||
|
|
||||||
|
public static byte[] dex;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
var cl = Objects.requireNonNull(LSPAppComponentFactoryStub.class.getClassLoader());
|
||||||
|
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 libName = archToLib.get(arch);
|
||||||
|
|
||||||
|
boolean useManager = false;
|
||||||
|
String soPath;
|
||||||
|
|
||||||
|
try (var is = cl.getResourceAsStream(Constants.CONFIG_ASSET_PATH);
|
||||||
|
var reader = new JsonReader(new InputStreamReader(is))) {
|
||||||
|
reader.beginObject();
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
var name = reader.nextName();
|
||||||
|
if (name.equals("useManager")) {
|
||||||
|
useManager = reader.nextBoolean();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
reader.skipValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useManager) {
|
||||||
|
Log.i(TAG, "Bootstrap loader from manager");
|
||||||
|
var ipm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
|
||||||
|
ApplicationInfo manager;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
manager = ipm.getApplicationInfo(Constants.MANAGER_PACKAGE_NAME, 0L, 0);
|
||||||
|
} else {
|
||||||
|
manager = ipm.getApplicationInfo(Constants.MANAGER_PACKAGE_NAME, 0, 0);
|
||||||
|
}
|
||||||
|
try (var zip = new ZipFile(new File(manager.sourceDir));
|
||||||
|
var is = zip.getInputStream(zip.getEntry(Constants.LOADER_DEX_ASSET_PATH));
|
||||||
|
var os = new ByteArrayOutputStream()) {
|
||||||
|
transfer(is, os);
|
||||||
|
dex = os.toByteArray();
|
||||||
|
}
|
||||||
|
soPath = manager.sourceDir + "!/assets/lspatch/so/" + libName + "/liblspatch.so";
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "Bootstrap loader from embedment");
|
||||||
|
try (var is = cl.getResourceAsStream(Constants.LOADER_DEX_ASSET_PATH);
|
||||||
|
var os = new ByteArrayOutputStream()) {
|
||||||
|
transfer(is, os);
|
||||||
|
dex = os.toByteArray();
|
||||||
|
}
|
||||||
|
soPath = cl.getResource("assets/lspatch/so/" + libName + "/liblspatch.so").getPath().substring(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.load(soPath);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new ExceptionInInitializerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void transfer(InputStream is, OutputStream os) throws IOException {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int n;
|
||||||
|
while (-1 != (n = is.read(buffer))) {
|
||||||
|
os.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,8 +27,8 @@ androidComponents.onVariants { variant ->
|
||||||
task<Copy>("copyDex$variantCapped") {
|
task<Copy>("copyDex$variantCapped") {
|
||||||
dependsOn("assemble$variantCapped")
|
dependsOn("assemble$variantCapped")
|
||||||
from("$buildDir/intermediates/dex/${variant.name}/mergeDex$variantCapped/classes.dex")
|
from("$buildDir/intermediates/dex/${variant.name}/mergeDex$variantCapped/classes.dex")
|
||||||
rename("classes.dex", "lsp.dex")
|
rename("classes.dex", "loader.dex")
|
||||||
into("${rootProject.projectDir}/out/assets/dex")
|
into("${rootProject.projectDir}/out/assets/lspatch")
|
||||||
}
|
}
|
||||||
|
|
||||||
task<Copy>("copySo$variantCapped") {
|
task<Copy>("copySo$variantCapped") {
|
||||||
|
|
@ -39,7 +39,7 @@ androidComponents.onVariants { variant ->
|
||||||
"include" to listOf("**/liblspatch.so")
|
"include" to listOf("**/liblspatch.so")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
into("${rootProject.projectDir}/out/assets/so")
|
into("${rootProject.projectDir}/out/assets/lspatch/so")
|
||||||
}
|
}
|
||||||
|
|
||||||
task("copy$variantCapped") {
|
task("copy$variantCapped") {
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ namespace lspd {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto stub = JNI_FindClass(env, "org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub");
|
auto stub = JNI_FindClass(env, "org/lsposed/lspatch/metaloader/LSPAppComponentFactoryStub");
|
||||||
auto dex_field = JNI_GetStaticFieldID(env, stub, "dex", "[B");
|
auto dex_field = JNI_GetStaticFieldID(env, stub, "dex", "[B");
|
||||||
|
|
||||||
ScopedLocalRef<jbyteArray> array = JNI_GetStaticObjectField(env, stub, dex_field);
|
ScopedLocalRef<jbyteArray> array = JNI_GetStaticObjectField(env, stub, dex_field);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package org.lsposed.patch;
|
package org.lsposed.patch;
|
||||||
|
|
||||||
import static org.lsposed.lspatch.share.Constants.CONFIG_ASSET_PATH;
|
import static org.lsposed.lspatch.share.Constants.CONFIG_ASSET_PATH;
|
||||||
import static org.lsposed.lspatch.share.Constants.DEX_ASSET_PATH;
|
import static org.lsposed.lspatch.share.Constants.LOADER_DEX_ASSET_PATH;
|
||||||
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.PROXY_APP_COMPONENT_FACTORY;
|
import static org.lsposed.lspatch.share.Constants.PROXY_APP_COMPONENT_FACTORY;
|
||||||
|
|
||||||
|
|
@ -21,6 +21,7 @@ import com.wind.meditor.property.ModificationProperty;
|
||||||
import com.wind.meditor.utils.NodeValue;
|
import com.wind.meditor.utils.NodeValue;
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.lsposed.lspatch.share.Constants;
|
||||||
import org.lsposed.lspatch.share.LSPConfig;
|
import org.lsposed.lspatch.share.LSPConfig;
|
||||||
import org.lsposed.lspatch.share.PatchConfig;
|
import org.lsposed.lspatch.share.PatchConfig;
|
||||||
import org.lsposed.patch.util.ApkSignatureHelper;
|
import org.lsposed.patch.util.ApkSignatureHelper;
|
||||||
|
|
@ -98,12 +99,6 @@ public class LSPatch {
|
||||||
"x86",
|
"x86",
|
||||||
"x86_64"
|
"x86_64"
|
||||||
));
|
));
|
||||||
private static final HashSet<String> APK_LIB_PATH_ARRAY = new HashSet<>(Arrays.asList(
|
|
||||||
"arm",
|
|
||||||
"arm64",
|
|
||||||
"x86",
|
|
||||||
"x86_64"
|
|
||||||
));
|
|
||||||
|
|
||||||
private static final ZFileOptions Z_FILE_OPTIONS = new ZFileOptions().setAlignmentRule(AlignmentRules.compose(
|
private static final ZFileOptions Z_FILE_OPTIONS = new ZFileOptions().setAlignmentRule(AlignmentRules.compose(
|
||||||
AlignmentRules.constantForSuffix(".so", 4096),
|
AlignmentRules.constantForSuffix(".so", 4096),
|
||||||
|
|
@ -244,10 +239,10 @@ public class LSPatch {
|
||||||
logger.d("Adding native lib..");
|
logger.d("Adding native lib..");
|
||||||
|
|
||||||
// copy so and dex files into the unzipped apk
|
// copy so and dex files into the unzipped apk
|
||||||
// do not put liblspd.so into apk!lib because x86 native bridge causes crash
|
// do not put liblspatch.so into apk!lib because x86 native bridge causes crash
|
||||||
for (String arch : APK_LIB_PATH_ARRAY) {
|
for (String arch : ARCHES) {
|
||||||
String entryName = "assets/lspatch/so/" + arch + "/liblspatch.so";
|
String entryName = "assets/lspatch/so/" + arch + "/liblspatch.so";
|
||||||
try (var is = getClass().getClassLoader().getResourceAsStream("assets/so/" + (arch.equals("arm") ? "armeabi-v7a" : (arch.equals("arm64") ? "arm64-v8a" : arch)) + "/liblspatch.so")) {
|
try (var is = getClass().getClassLoader().getResourceAsStream(entryName)) {
|
||||||
dstZFile.add(entryName, is, false); // no compress for so
|
dstZFile.add(entryName, is, false); // no compress for so
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// More exception info
|
// More exception info
|
||||||
|
|
@ -258,14 +253,14 @@ public class LSPatch {
|
||||||
|
|
||||||
logger.d("Adding dex..");
|
logger.d("Adding dex..");
|
||||||
|
|
||||||
try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/loader.dex")) {
|
try (var is = getClass().getClassLoader().getResourceAsStream(Constants.META_LOADER_DEX_ASSET_PATH)) {
|
||||||
dstZFile.add("classes.dex", is);
|
dstZFile.add("classes.dex", is);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new PatchError("Error when adding dex", e);
|
throw new PatchError("Error when adding dex", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/lsp.dex")) {
|
try (var is = getClass().getClassLoader().getResourceAsStream(LOADER_DEX_ASSET_PATH)) {
|
||||||
dstZFile.add(DEX_ASSET_PATH, is);
|
dstZFile.add(LOADER_DEX_ASSET_PATH, is);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new PatchError("Error when adding assets", e);
|
throw new PatchError("Error when adding assets", e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,14 @@ dependencyResolutionManagement {
|
||||||
rootProject.name = "LSPatch"
|
rootProject.name = "LSPatch"
|
||||||
include(
|
include(
|
||||||
":apkzlib",
|
":apkzlib",
|
||||||
":appstub",
|
|
||||||
":axmlprinter",
|
":axmlprinter",
|
||||||
":core",
|
":core",
|
||||||
":hiddenapi:bridge",
|
":hiddenapi:bridge",
|
||||||
":hiddenapi:stubs",
|
":hiddenapi:stubs",
|
||||||
|
":jar",
|
||||||
":manager",
|
":manager",
|
||||||
|
":meta-loader",
|
||||||
":patch",
|
":patch",
|
||||||
":patch-jar",
|
|
||||||
":patch-loader",
|
":patch-loader",
|
||||||
":services:daemon-service",
|
":services:daemon-service",
|
||||||
":services:manager-service",
|
":services:manager-service",
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,16 @@ package org.lsposed.lspatch.share;
|
||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
|
|
||||||
final static public String DEX_ASSET_PATH = "assets/lspatch/lsp.dex";
|
|
||||||
final static public String CONFIG_ASSET_PATH = "assets/lspatch/config.json";
|
final static public String CONFIG_ASSET_PATH = "assets/lspatch/config.json";
|
||||||
|
final static public String LOADER_DEX_ASSET_PATH = "assets/lspatch/loader.dex";
|
||||||
|
final static public String META_LOADER_DEX_ASSET_PATH = "assets/lspatch/metaloader.dex";
|
||||||
final static public String ORIGINAL_APK_ASSET_PATH = "assets/lspatch/origin.apk";
|
final static public String ORIGINAL_APK_ASSET_PATH = "assets/lspatch/origin.apk";
|
||||||
|
|
||||||
final static public String PROXY_APP_COMPONENT_FACTORY = "org.lsposed.lspatch.appstub.LSPAppComponentFactoryStub";
|
final static public String PATCH_FILE_SUFFIX = "-lspatched.apk";
|
||||||
|
final static public String PROXY_APP_COMPONENT_FACTORY = "org.lsposed.lspatch.metaloader.LSPAppComponentFactoryStub";
|
||||||
final static public String MANAGER_PACKAGE_NAME = "org.lsposed.lspatch";
|
final static public String MANAGER_PACKAGE_NAME = "org.lsposed.lspatch";
|
||||||
|
final static public int MIN_ROLLING_VERSION_CODE = 341;
|
||||||
|
|
||||||
final static public int SIGBYPASS_LV_DISABLE = 0;
|
final static public int SIGBYPASS_LV_DISABLE = 0;
|
||||||
final static public int SIGBYPASS_LV_PM = 1;
|
final static public int SIGBYPASS_LV_PM = 1;
|
||||||
final static public int SIGBYPASS_LV_PM_OPENAT = 2;
|
final static public int SIGBYPASS_LV_PM_OPENAT = 2;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue