Switch into mainline core branch

This commit is contained in:
Nullptr 2022-03-17 19:52:05 +08:00
parent d7b508e731
commit b89b425a53
41 changed files with 737 additions and 350 deletions

2
app/.gitignore vendored
View File

@ -1,2 +0,0 @@
/build
/target

View File

@ -1,100 +0,0 @@
val androidCompileSdkVersion: Int by rootProject.extra
val androidMinSdkVersion: Int by rootProject.extra
val androidTargetSdkVersion: Int by rootProject.extra
val androidSourceCompatibility: JavaVersion by rootProject.extra
val androidTargetCompatibility: JavaVersion by rootProject.extra
plugins {
id("com.android.application")
}
android {
flavorDimensions += "api"
productFlavors {
create("Riru") {
dimension = "api"
}
}
compileSdk = androidCompileSdkVersion
defaultConfig {
minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
multiDexEnabled = false
signingConfigs.create("config") {
val androidStoreFile = project.findProperty("androidStoreFile") as String?
if (!androidStoreFile.isNullOrEmpty()) {
storeFile = file(androidStoreFile)
storePassword = project.property("androidStorePassword") as String
keyAlias = project.property("androidKeyAlias") as String
keyPassword = project.property("androidKeyPassword") as String
}
}
compileOptions {
sourceCompatibility = androidSourceCompatibility
targetCompatibility = androidTargetCompatibility
}
}
buildTypes {
debug {
isDebuggable = true
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
signingConfig = if (signingConfigs["config"].storeFile != null) signingConfigs["config"] else signingConfigs["debug"]
}
release {
isDebuggable = false
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
signingConfig = if (signingConfigs["config"].storeFile != null) signingConfigs["config"] else signingConfigs["debug"]
}
}
lint {
abortOnError = false
}
}
androidComponents.onVariants { variant ->
val variantCapped = variant.name.capitalize()
val variantLowered = variant.name.toLowerCase()
task<Copy>("copyDex$variantCapped") {
dependsOn("assemble$variantCapped")
from("$buildDir/intermediates/dex/$variantCapped/mergeDex$variantCapped/classes.dex")
rename("classes.dex", "lsp.dex")
into("${rootProject.projectDir}/out/assets/dex")
}
task<Copy>("copySo$variantCapped") {
dependsOn("assemble$variantCapped")
from("$buildDir/intermediates/merged_native_libs/$variantCapped/out/lib")
into("${rootProject.projectDir}/out/assets/so")
}
task("copy$variantCapped") {
dependsOn("copySo$variantCapped")
dependsOn("copyDex$variantCapped")
doLast {
println("Dex and so files has been copied to ${rootProject.projectDir}${File.separator}out")
}
}
}
dependencies {
compileOnly(projects.hiddenapiStubs)
implementation(projects.daemonService)
implementation(projects.lspcore)
implementation(projects.hiddenapiBridge)
implementation(projects.share)
implementation(projects.imanager)
implementation("com.google.code.gson:gson:2.9.0")
}

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.lsposed.lspatch">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:appComponentFactory="org.lsposed.lspatch.appstub.LSPAppComponentFactoryStub"
android:icon="@mipmap/ic_launcher"
android:label="@string/sample_app_title"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
<activity android:name="org.lsposed.lspatch.tester.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="xposedmodule" android:value="true"/>
<meta-data android:name="xposedminversion" android:value="53"/>
</application>
</manifest>

View File

@ -1 +0,0 @@
palceholder

View File

@ -1 +0,0 @@
org.lsposed.lspatch.tester.Hook

View File

@ -1,18 +0,0 @@
package org.lsposed.lspatch.tester;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class Hook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
XposedHelpers.findAndHookMethod("org.lsposed.lspatch.tester.MainActivity", lpparam.classLoader, "checkXposed2", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(true);
}
});
}
}

View File

@ -1,49 +0,0 @@
package org.lsposed.lspatch.tester;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import org.lsposed.lspatch.R;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
public class MainActivity extends Activity {
@SuppressLint("SetTextI18n")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
XposedHelpers.findAndHookMethod(this.getClass(), "checkXposed", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(true);
}
});
TextView textView = findViewById(R.id.msg);
if (checkXposed() && checkXposed2()) {
textView.setText("ok");
}
else {
textView.setText("fail");
}
}
public void onClick(View view) {
}
public boolean checkXposed() {
return false;
}
public static boolean checkXposed2() {
return false;
}
}

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.lsposed.lspatch.tester.MainActivity">
<TextView
android:id="@+id/msg"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sample_app_title">Xposed Module Loader</string>
</resources>

View File

@ -1,23 +1,10 @@
val androidMinSdkVersion: Int by rootProject.extra
val androidTargetSdkVersion: Int by rootProject.extra
val androidCompileSdkVersion: Int by rootProject.extra
val androidBuildToolsVersion: String by rootProject.extra
val androidSourceCompatibility: JavaVersion by rootProject.extra
val androidTargetCompatibility: JavaVersion by rootProject.extra
plugins { plugins {
id("com.android.application") id("com.android.application")
} }
android { android {
compileSdk = androidCompileSdkVersion
buildToolsVersion = androidBuildToolsVersion
defaultConfig { defaultConfig {
minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
multiDexEnabled = false multiDexEnabled = false
} }
@ -27,11 +14,6 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
} }
} }
compileOptions {
sourceCompatibility = androidSourceCompatibility
targetCompatibility = androidTargetCompatibility
}
} }
androidComponents.onVariants { variant -> androidComponents.onVariants { variant ->
@ -55,7 +37,7 @@ androidComponents.onVariants { variant ->
} }
dependencies { dependencies {
compileOnly(projects.hiddenapiStubs) compileOnly(projects.hiddenapi.stubs)
implementation("de.upb.cs.swt:axml:2.1.2") implementation("de.upb.cs.swt:axml:2.1.2")
} }

View File

@ -34,7 +34,7 @@ public class LSPAppComponentFactoryStub extends AppComponentFactory {
vmInstructionSet.setAccessible(true); vmInstructionSet.setAccessible(true);
String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null)); String arch = (String) vmInstructionSet.invoke(getRuntime.invoke(null));
String path = cl.getResource("assets/lspatch/lspd/" + arch + "/liblspd.so").getPath().substring(5); String path = cl.getResource("assets/lspatch/so/" + arch + "/liblspatch.so").getPath().substring(5);
System.load(path); System.load(path);
} catch (Throwable e) { } catch (Throwable e) {
Log.e("LSPatch", "load lspd error", e); Log.e("LSPatch", "load lspd error", e);

View File

@ -1,14 +1,20 @@
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.gradle.BaseExtension
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.internal.storage.file.FileRepository import org.eclipse.jgit.internal.storage.file.FileRepository
plugins {
id("com.android.application") apply false
id("com.android.library") apply false
}
buildscript { buildscript {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
val agpVersion by extra("7.1.2")
dependencies { dependencies {
classpath("com.android.tools.build:gradle:$agpVersion")
classpath("org.eclipse.jgit:org.eclipse.jgit:6.0.0.202111291000-r") classpath("org.eclipse.jgit:org.eclipse.jgit:6.0.0.202111291000-r")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
} }
@ -41,13 +47,6 @@ val androidBuildToolsVersion by extra("31.0.0")
val androidSourceCompatibility by extra(JavaVersion.VERSION_11) val androidSourceCompatibility by extra(JavaVersion.VERSION_11)
val androidTargetCompatibility by extra(JavaVersion.VERSION_11) val androidTargetCompatibility by extra(JavaVersion.VERSION_11)
allprojects {
repositories {
google()
mavenCentral()
}
}
tasks.register<Delete>("clean") { tasks.register<Delete>("clean") {
delete(rootProject.buildDir) delete(rootProject.buildDir)
} }
@ -59,3 +58,163 @@ listOf("Debug", "Release").forEach { variant ->
dependsOn(projects.manager.dependencyProject.tasks["build$variant"]) dependsOn(projects.manager.dependencyProject.tasks["build$variant"])
} }
} }
fun Project.configureBaseExtension() {
extensions.findByType(BaseExtension::class)?.run {
compileSdkVersion(androidCompileSdkVersion)
ndkVersion = androidCompileNdkVersion
buildToolsVersion = androidBuildToolsVersion
defaultConfig {
minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
versionCode = verCode
versionName = verName
signingConfigs.create("config") {
val androidStoreFile = project.findProperty("androidStoreFile") as String?
if (!androidStoreFile.isNullOrEmpty()) {
storeFile = file(androidStoreFile)
storePassword = project.property("androidStorePassword") as String
keyAlias = project.property("androidKeyAlias") as String
keyPassword = project.property("androidKeyPassword") as String
}
}
externalNativeBuild {
cmake {
arguments += "-DEXTERNAL_ROOT=${File(rootDir.absolutePath, "core/external")}"
arguments += "-DCORE_ROOT=${File(rootDir.absolutePath, "core/core/src/main/jni")}"
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
val flags = arrayOf(
"-Wall",
"-Qunused-arguments",
"-Wno-gnu-string-literal-operator-template",
"-fno-rtti",
"-fvisibility=hidden",
"-fvisibility-inlines-hidden",
"-fno-exceptions",
"-fno-stack-protector",
"-fomit-frame-pointer",
"-Wno-builtin-macro-redefined",
"-Wno-unused-value",
"-D__FILE__=__FILE_NAME__",
)
cppFlags("-std=c++20", *flags)
cFlags("-std=c18", *flags)
arguments(
"-DANDROID_STL=none",
"-DVERSION_CODE=$verCode",
"-DVERSION_NAME=$verName",
)
}
}
}
compileOptions {
targetCompatibility(androidTargetCompatibility)
sourceCompatibility(androidSourceCompatibility)
}
buildTypes {
all {
signingConfig = if (signingConfigs["config"].storeFile != null) signingConfigs["config"] else signingConfigs["debug"]
}
named("debug") {
externalNativeBuild {
cmake {
arguments.addAll(
arrayOf(
"-DCMAKE_CXX_FLAGS_DEBUG=-Og",
"-DCMAKE_C_FLAGS_DEBUG=-Og",
)
)
}
}
}
named("release") {
externalNativeBuild {
cmake {
val flags = arrayOf(
"-Wl,--exclude-libs,ALL",
"-ffunction-sections",
"-fdata-sections",
"-Wl,--gc-sections",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
"-flto=thin",
"-Wl,--thinlto-cache-policy,cache_size_bytes=300m",
"-Wl,--thinlto-cache-dir=${buildDir.absolutePath}/.lto-cache",
)
cppFlags.addAll(flags)
cFlags.addAll(flags)
val configFlags = arrayOf(
"-Oz",
"-DNDEBUG"
).joinToString(" ")
arguments.addAll(
arrayOf(
"-DCMAKE_CXX_FLAGS_RELEASE=$configFlags",
"-DCMAKE_CXX_FLAGS_RELWITHDEBINFO=$configFlags",
"-DCMAKE_C_FLAGS_RELEASE=$configFlags",
"-DCMAKE_C_FLAGS_RELWITHDEBINFO=$configFlags",
"-DDEBUG_SYMBOLS_PATH=${buildDir.absolutePath}/symbols",
)
)
}
}
}
}
}
extensions.findByType(ApplicationExtension::class)?.lint {
abortOnError = true
checkReleaseBuilds = false
}
extensions.findByType(ApplicationAndroidComponentsExtension::class)?.let { androidComponents ->
val optimizeReleaseRes = task("optimizeReleaseRes").doLast {
val aapt2 = File(
androidComponents.sdkComponents.sdkDirectory.get().asFile,
"build-tools/${androidBuildToolsVersion}/aapt2"
)
val zip = java.nio.file.Paths.get(
project.buildDir.path,
"intermediates",
"optimized_processed_res",
"release",
"resources-release-optimize.ap_"
)
val optimized = File("${zip}.opt")
val cmd = exec {
commandLine(
aapt2, "optimize",
"--collapse-resource-names",
"--enable-sparse-encoding",
"-o", optimized,
zip
)
isIgnoreExitValue = false
}
if (cmd.exitValue == 0) {
delete(zip)
optimized.renameTo(zip.toFile())
}
}
tasks.whenTaskAdded {
if (name == "optimizeReleaseResources") {
finalizedBy(optimizeReleaseRes)
}
}
}
}
subprojects {
plugins.withId("com.android.application") {
configureBaseExtension()
}
plugins.withId("com.android.library") {
configureBaseExtension()
}
}

2
core

@ -1 +1 @@
Subproject commit 216a6f00435b4ab00f5fadc80c1c3b1e7ac9b20c Subproject commit e0de4ca6d7c1757de85094672a8c069e6a90fb65

View File

@ -1,20 +1,8 @@
# Project-wide Gradle settings. android.experimental.enableNewResourceShrinker=true
# IDE (e.g. Android Studio) users: android.experimental.enableNewResourceShrinker.preciseShrinking=true
# Gradle settings configured through the IDE *will override* android.enableAppCompileTimeRClass=true
# any settings specified in this file. android.nonTransitiveRClass=true
# For more details on how to configure your build environment visit android.enableR8.fullMode=true
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
version_name=1.0
version_code=1
android.useAndroidX=true android.useAndroidX=true
agpVersion=7.1.2

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@ -1,11 +1,3 @@
val androidMinSdkVersion: Int by rootProject.extra
val androidTargetSdkVersion: Int by rootProject.extra
val androidCompileSdkVersion: Int by rootProject.extra
val androidBuildToolsVersion: String by rootProject.extra
val androidSourceCompatibility: JavaVersion by rootProject.extra
val androidTargetCompatibility: JavaVersion by rootProject.extra
plugins { plugins {
id("com.android.library") id("com.android.library")
} }
@ -16,25 +8,13 @@ android {
dimension = "api" dimension = "api"
} }
compileSdk = androidCompileSdkVersion
defaultConfig {
minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
}
buildTypes { buildTypes {
release { release {
isMinifyEnabled = true isMinifyEnabled = true
} }
} }
compileOptions {
sourceCompatibility = androidSourceCompatibility
targetCompatibility = androidTargetCompatibility
}
} }
dependencies { dependencies {
api(project(":daemon-service")) api(projects.services.daemonService)
} }

View File

@ -1,19 +1,8 @@
import com.android.build.gradle.BaseExtension
val androidCompileSdkVersion: Int by rootProject.extra
val androidMinSdkVersion: Int by rootProject.extra
val androidTargetSdkVersion: Int by rootProject.extra
val defaultManagerPackageName: String by rootProject.extra val defaultManagerPackageName: String by rootProject.extra
val apiCode: Int by rootProject.extra val apiCode: Int by rootProject.extra
val verCode: Int by rootProject.extra
val verName: String by rootProject.extra
val coreVerCode: Int by rootProject.extra val coreVerCode: Int by rootProject.extra
val coreVerName: String by rootProject.extra val coreVerName: String by rootProject.extra
val androidSourceCompatibility: JavaVersion by rootProject.extra
val androidTargetCompatibility: JavaVersion by rootProject.extra
plugins { plugins {
id("com.android.application") id("com.android.application")
id("kotlin-parcelize") id("kotlin-parcelize")
@ -21,14 +10,8 @@ plugins {
} }
android { android {
compileSdk = androidCompileSdkVersion
defaultConfig { defaultConfig {
applicationId = defaultManagerPackageName applicationId = defaultManagerPackageName
minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
versionCode = verCode
versionName = verName
buildConfigField("int", "API_CODE", """$apiCode""") buildConfigField("int", "API_CODE", """$apiCode""")
buildConfigField("int", "CORE_VERSION_CODE", """$coreVerCode""") buildConfigField("int", "CORE_VERSION_CODE", """$coreVerCode""")
@ -39,15 +22,9 @@ android {
release { release {
isMinifyEnabled = true isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
signingConfig = projects.app.dependencyProject.extensions.getByName<BaseExtension>("android").buildTypes["release"].signingConfig
} }
} }
compileOptions {
sourceCompatibility = androidSourceCompatibility
targetCompatibility = androidTargetCompatibility
}
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
} }
@ -70,7 +47,7 @@ afterEvaluate {
task<Copy>("copy${variantCapped}Assets") { task<Copy>("copy${variantCapped}Assets") {
dependsOn(":appstub:copy$variantCapped") dependsOn(":appstub:copy$variantCapped")
dependsOn(":app:copyRiru$variantCapped") dependsOn(":patch-loader:copy$variantCapped")
tasks["merge${variantCapped}Assets"].dependsOn(this) tasks["merge${variantCapped}Assets"].dependsOn(this)
into("$buildDir/intermediates/assets/$variantLowered/merge${variantCapped}Assets") into("$buildDir/intermediates/assets/$variantLowered/merge${variantCapped}Assets")
@ -92,11 +69,11 @@ dependencies {
implementation("androidx.core:core-ktx:1.7.0") implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.activity:activity-compose:1.5.0-alpha03") implementation("androidx.activity:activity-compose:1.5.0-alpha03")
implementation("androidx.compose.material:material-icons-extended:1.1.1") implementation("androidx.compose.material:material-icons-extended:1.1.1")
implementation("androidx.compose.material3:material3:1.0.0-alpha06") implementation("androidx.compose.material3:material3:1.0.0-alpha07")
implementation("androidx.compose.runtime:runtime-livedata:1.1.1") implementation("androidx.compose.runtime:runtime-livedata:1.1.1")
implementation("androidx.compose.ui:ui:1.1.1") implementation("androidx.compose.ui:ui:1.1.1")
implementation("androidx.compose.ui:ui-tooling:1.1.1") implementation("androidx.compose.ui:ui-tooling:1.1.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha03") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha04")
implementation("androidx.navigation:navigation-compose:2.5.0-alpha03") implementation("androidx.navigation:navigation-compose:2.5.0-alpha03")
implementation("androidx.preference:preference:1.2.0") implementation("androidx.preference:preference:1.2.0")
implementation("com.google.accompanist:accompanist-drawablepainter:0.24.2-alpha") implementation("com.google.accompanist:accompanist-drawablepainter:0.24.2-alpha")

View File

@ -35,13 +35,13 @@ val jar = tasks.jar.get()
tasks.register("buildDebug") { tasks.register("buildDebug") {
jar.dependsOn(":appstub:copyDebug") jar.dependsOn(":appstub:copyDebug")
jar.dependsOn(":app:copyRiruDebug") jar.dependsOn(":patch-loader:copyDebug")
dependsOn(tasks.build) dependsOn(tasks.build)
} }
tasks.register("buildRelease") { tasks.register("buildRelease") {
jar.dependsOn(":appstub:copyRelease") jar.dependsOn(":appstub:copyRelease")
jar.dependsOn(":app:copyRiruRelease") jar.dependsOn(":patch-loader:copyRelease")
dependsOn(tasks.build) dependsOn(tasks.build)
} }

2
patch-loader/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/build
/.cxx

View File

@ -0,0 +1,74 @@
plugins {
id("com.android.application")
}
android {
defaultConfig {
multiDexEnabled = false
}
buildTypes {
debug {
isDebuggable = true
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
release {
isDebuggable = false
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
externalNativeBuild {
cmake {
path("src/main/jni/CMakeLists.txt")
}
}
lint {
abortOnError = false
}
}
androidComponents.onVariants { variant ->
val variantCapped = variant.name.capitalize()
task<Copy>("copyDex$variantCapped") {
dependsOn("assemble$variantCapped")
from("$buildDir/intermediates/dex/$variantCapped/mergeDex$variantCapped/classes.dex")
rename("classes.dex", "lsp.dex")
into("${rootProject.projectDir}/out/assets/dex")
}
task<Copy>("copySo$variantCapped") {
dependsOn("assemble$variantCapped")
from(
fileTree(
"dir" to "$buildDir/intermediates/merged_native_libs/$variantCapped/out/lib",
"include" to listOf("**/liblspatch.so")
)
)
into("${rootProject.projectDir}/out/assets/so")
}
task("copy$variantCapped") {
dependsOn("copySo$variantCapped")
dependsOn("copyDex$variantCapped")
doLast {
println("Dex and so files has been copied to ${rootProject.projectDir}${File.separator}out")
}
}
}
dependencies {
compileOnly(projects.hiddenapi.stubs)
implementation(projects.core)
implementation(projects.hiddenapi.bridge)
implementation(projects.services.daemonService)
implementation(projects.imanager)
implementation(projects.share)
implementation("com.google.code.gson:gson:2.9.0")
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="org.lsposed.lspatch.loader" />

View File

@ -29,7 +29,7 @@ import org.lsposed.lspatch.manager.ModuleLoader;
import org.lsposed.lspatch.share.Constants; import org.lsposed.lspatch.share.Constants;
import org.lsposed.lspatch.share.PatchConfig; import org.lsposed.lspatch.share.PatchConfig;
import org.lsposed.lspd.config.ApplicationServiceClient; import org.lsposed.lspd.config.ApplicationServiceClient;
import org.lsposed.lspd.core.Main; import org.lsposed.lspd.core.Startup;
import org.lsposed.lspd.models.Module; import org.lsposed.lspd.models.Module;
import org.lsposed.lspd.nativebridge.SigBypass; import org.lsposed.lspd.nativebridge.SigBypass;
@ -53,7 +53,6 @@ import java.util.zip.ZipFile;
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 hidden.HiddenApiBridge; import hidden.HiddenApiBridge;
/** /**
@ -104,10 +103,9 @@ public class LSPApplication extends ApplicationServiceClient {
try { try {
disableProfile(context); disableProfile(context);
loadModules(context); loadModules(context);
Main.forkPostCommon(false, context.getDataDir().toString(), ActivityThread.currentProcessName()); Startup.initXposed(false);
doHook(context);
Log.i(TAG, "Start loading modules"); Log.i(TAG, "Start loading modules");
XposedInit.loadModules(); Startup.bootstrapXposed(ActivityThread.currentProcessName());
// WARN: Since it uses `XResource`, the following class should not be initialized // WARN: Since it uses `XResource`, the following class should not be initialized
// before forkPostCommon is invoke. Otherwise, you will get failure of XResources // before forkPostCommon is invoke. Otherwise, you will get failure of XResources
LSPLoader.initModules(appLoadedApk); LSPLoader.initModules(appLoadedApk);
@ -116,9 +114,11 @@ public class LSPApplication extends ApplicationServiceClient {
switchClassLoader("mBaseClassLoader"); switchClassLoader("mBaseClassLoader");
switchClassLoader("mDefaultClassLoader"); switchClassLoader("mDefaultClassLoader");
switchClassLoader("mClassLoader"); switchClassLoader("mClassLoader");
doSigBypass(context);
} catch (Throwable e) { } catch (Throwable e) {
Log.e(TAG, "Do hook", e); Log.e(TAG, "Do hook", e);
} }
Log.i(TAG, "LSPatch bootstrap completed");
} }
private static Context createLoadedApkWithContext() { private static Context createLoadedApkWithContext() {
@ -294,7 +294,7 @@ public class LSPApplication extends ApplicationServiceClient {
return field.getInt(null); return field.getInt(null);
} }
private static void byPassSignature(Context context) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException { private static void bypassSignature(Context context) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
final int TRANSACTION_getPackageInfo = getTranscationId("android.content.pm.IPackageManager$Stub", "TRANSACTION_getPackageInfo"); final int TRANSACTION_getPackageInfo = getTranscationId("android.content.pm.IPackageManager$Stub", "TRANSACTION_getPackageInfo");
XposedHelpers.findAndHookMethod("android.os.BinderProxy", null, "transact", int.class, Parcel.class, Parcel.class, int.class, new XC_MethodHook() { XposedHelpers.findAndHookMethod("android.os.BinderProxy", null, "transact", int.class, Parcel.class, Parcel.class, int.class, new XC_MethodHook() {
@Override @Override
@ -359,10 +359,10 @@ public class LSPApplication extends ApplicationServiceClient {
}); });
} }
private static void doHook(Context context) throws IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException { private static void doSigBypass(Context context) throws IllegalAccessException, ClassNotFoundException, IOException, NoSuchFieldException {
if (config.sigBypassLevel >= Constants.SIGBYPASS_LV_PM) { if (config.sigBypassLevel >= Constants.SIGBYPASS_LV_PM) {
XLog.d(TAG, "Original signature: " + config.originalSignature.substring(0, 16) + "..."); XLog.d(TAG, "Original signature: " + config.originalSignature.substring(0, 16) + "...");
byPassSignature(context); bypassSignature(context);
} }
if (config.sigBypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) { if (config.sigBypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) {
String cacheApkPath; String cacheApkPath;

View File

@ -1,7 +1,6 @@
package org.lsposed.lspatch.loader.util; package org.lsposed.lspatch.loader.util;
import org.lsposed.lspatch.loader.BuildConfig;
import org.lsposed.lspd.BuildConfig;
public class XLog { public class XLog {

View File

@ -0,0 +1,5 @@
package org.lsposed.lspd.nativebridge;
public class SigBypass {
public static native void enableOpenatHook(String origApkPath, String cacheApkPath);
}

View File

@ -0,0 +1,27 @@
project(lspatch)
cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(${CORE_ROOT} core)
aux_source_directory(src SRC_LIST)
aux_source_directory(src/jni SRC_LIST)
set(SRC_LIST ${SRC_LIST} api/patch_main.cpp)
add_library(${PROJECT_NAME} SHARED ${SRC_LIST})
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_include_directories(${PROJECT_NAME} PRIVATE src)
target_link_libraries(${PROJECT_NAME} core log)
if (DEFINED DEBUG_SYMBOLS_PATH)
set(DEBUG_SYMBOLS_PATH ${DEBUG_SYMBOLS_PATH}/${API})
message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}
COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $<TARGET_FILE:${PROJECT_NAME}>
${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>
COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
$<TARGET_FILE:${PROJECT_NAME}>)
endif()

View File

@ -0,0 +1,36 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
//
// Created by Nullptr on 2022/3/17.
//
#include <jni.h>
#include "patch_loader.h"
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
lspd::PatchLoader::Init();
lspd::PatchLoader::GetInstance()->Load(env);
return JNI_VERSION_1_6;
}

View File

@ -0,0 +1,56 @@
//
// Created by loves on 6/19/2021.
//
#ifndef LSPATCH_PROFILE_SAVER_H
#define LSPATCH_PROFILE_SAVER_H
#include "utils/hook_helper.hpp"
using namespace lsplant;
namespace art {
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art12ProfileSaver20ProcessProfilingInfoEbPt",
bool, ProcessProfilingInfo, (void * thiz, bool, uint16_t *), {
LOGD("skipped profile saving");
return true;
});
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art12ProfileSaver20ProcessProfilingInfoEbbPt",
bool, ProcessProfilingInfoWithBool, (void * thiz, bool, bool, uint16_t *), {
LOGD("skipped profile saving");
return true;
});
CREATE_HOOK_STUB_ENTRY(
"execve",
int, execve, (const char *pathname, const char *argv[], char *const envp[]), {
if (strstr(pathname, "dex2oat")) {
size_t count = 0;
while (argv[count++] != nullptr);
std::unique_ptr<const char *[]> new_args = std::make_unique<const char *[]>(
count + 1);
for (size_t i = 0; i < count - 1; ++i)
new_args[i] = argv[i];
new_args[count - 1] = "--inline-max-code-units=0";
new_args[count] = nullptr;
LOGD("dex2oat by disable inline!");
int ret = backup(pathname, new_args.get(), envp);
return ret;
}
int ret = backup(pathname, argv, envp);
return ret;
});
static void DisableInline(const HookHandler &handler) {
HookSyms(handler, ProcessProfilingInfo, ProcessProfilingInfoWithBool);
HookSymNoHandle(handler, reinterpret_cast<void*>(&::execve), execve);
}
}
#endif //LSPATCH_PROFILE_SAVER_H

View File

@ -0,0 +1,64 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2021 - 2022 LSPosed Contributors
*/
#ifndef LSPATCH_OAT_FILE_MANAGER_H
#define LSPATCH_OAT_FILE_MANAGER_H
#include "context.h"
#include "utils/hook_helper.hpp"
using namespace lsplant;
namespace art {
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art14OatFileManager25RunBackgroundVerificationERKNSt3__16vectorIPKNS_7DexFileENS1_9allocatorIS5_EEEEP8_jobjectPKc",
void, RunBackgroundVerificationWithContext,
(void * thiz, const std::vector<const void *> &dex_files,
jobject class_loader,
const char *class_loader_context), {
if (lspd::Context::GetInstance()->GetCurrentClassLoader() == nullptr) {
LOGD("Disabled background verification");
return;
}
backup(thiz, dex_files, class_loader, class_loader_context);
});
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art14OatFileManager25RunBackgroundVerificationERKNSt3__16vectorIPKNS_7DexFileENS1_9allocatorIS5_EEEEP8_jobject",
void, RunBackgroundVerification,
(void * thiz, const std::vector<const void *> &dex_files,
jobject class_loader), {
if (lspd::Context::GetInstance()->GetCurrentClassLoader() == nullptr) {
LOGD("Disabled background verification");
return;
}
backup(thiz, dex_files, class_loader);
});
static void DisableBackgroundVerification(const lsplant::HookHandler &handler) {
const int api_level = lspd::GetAndroidApiLevel();
if (api_level >= __ANDROID_API_Q__) {
HookSyms(handler, RunBackgroundVerificationWithContext, RunBackgroundVerification);
}
}
}
#endif //LSPATCH_OAT_FILE_MANAGER_H

View File

@ -0,0 +1,51 @@
//
// Created by VIP on 2021/4/25.
//
#include "bypass_sig.h"
#include "elf_util.h"
#include "logging.h"
#include "native_util.h"
#include "patch_loader.h"
#include "utils/hook_helper.hpp"
#include "utils/jni_helper.hpp"
namespace lspd {
std::string apkPath;
std::string redirectPath;
CREATE_HOOK_STUB_ENTRY(
"__openat",
int, __openat,
(int fd, const char* pathname, int flag, int mode), {
if (pathname == apkPath) {
LOGD("redirect openat");
return backup(fd, redirectPath.c_str(), flag, mode);
}
return backup(fd, pathname, flag, mode);
});
LSP_DEF_NATIVE_METHOD(void, SigBypass, enableOpenatHook, jstring origApkPath, jstring cacheApkPath) {
auto sym_openat = SandHook::ElfImg("libc.so").getSymbAddress<void *>("__openat");
auto r = HookSymNoHandle(handler, sym_openat, __openat);
if (!r) {
LOGE("Hook __openat fail");
return;
}
lsplant::JUTFString str1(env, origApkPath);
lsplant::JUTFString str2(env, cacheApkPath);
apkPath = str1.get();
redirectPath = str2.get();
LOGD("apkPath %s", apkPath.c_str());
LOGD("redirectPath %s", redirectPath.c_str());
}
static JNINativeMethod gMethods[] = {
LSP_NATIVE_METHOD(SigBypass, enableOpenatHook, "(Ljava/lang/String;Ljava/lang/String;)V")
};
void RegisterBypass(JNIEnv* env) {
REGISTER_LSP_NATIVE_METHODS(SigBypass);
}
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <jni.h>
namespace lspd {
void RegisterBypass(JNIEnv* env);
} // namespace lspd

View File

@ -0,0 +1,118 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
//
// Created by Nullptr on 2022/3/17.
//
#include "art/runtime/oat_file_manager.h"
#include "art/runtime/jit/profile_saver.h"
#include "elf_util.h"
#include "jni/bypass_sig.h"
#include "native_hook.h"
#include "native_util.h"
#include "patch_loader.h"
#include "symbol_cache.h"
#include "utils/jni_helper.hpp"
using namespace lsplant;
namespace lspd {
void PatchLoader::LoadDex(JNIEnv* env, Context::PreloadedDex&& dex) {
auto class_activity_thread = JNI_FindClass(env, "android/app/ActivityThread");
auto class_activity_thread_app_bind_data = JNI_FindClass(env, "android/app/ActivityThread$AppBindData");
auto class_loaded_apk = JNI_FindClass(env, "android/app/LoadedApk");
auto mid_current_activity_thread = JNI_GetStaticMethodID(env, class_activity_thread, "currentActivityThread",
"()Landroid/app/ActivityThread;");
auto mid_get_classloader = JNI_GetMethodID(env, class_loaded_apk, "getClassLoader", "()Ljava/lang/ClassLoader;");
auto fid_m_bound_application = JNI_GetFieldID(env, class_activity_thread, "mBoundApplication",
"Landroid/app/ActivityThread$AppBindData;");
auto fid_info = JNI_GetFieldID(env, class_activity_thread_app_bind_data, "info", "Landroid/app/LoadedApk;");
auto activity_thread = JNI_CallStaticObjectMethod(env, class_activity_thread, mid_current_activity_thread);
auto m_bound_application = JNI_GetObjectField(env, activity_thread, fid_m_bound_application);
auto info = JNI_GetObjectField(env, m_bound_application, fid_info);
auto stub_classloader = JNI_CallObjectMethod(env, info, mid_get_classloader);
if (!stub_classloader) [[unlikely]] {
LOGE("getStubClassLoader failed!!!");
return;
}
auto in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader");
auto mid_init = JNI_GetMethodID(env, in_memory_classloader, "<init>",
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
auto byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer");
auto dex_buffer = env->NewDirectByteBuffer(dex.data(), dex.size());
if (auto my_cl = JNI_NewObject(env, in_memory_classloader, mid_init, dex_buffer, stub_classloader)) {
inject_class_loader_ = JNI_NewGlobalRef(env, my_cl);
} else {
LOGE("InMemoryDexClassLoader creation failed!!!");
return;
}
env->DeleteLocalRef(dex_buffer);
}
void PatchLoader::InitHooks(JNIEnv* env, const lsplant::InitInfo& initInfo) {
handler = initInfo;
Context::InitHooks(env, handler);
art::DisableInline(handler);
art::DisableBackgroundVerification(handler);
RegisterBypass(env);
}
void PatchLoader::SetupEntryClass(JNIEnv* env) {
if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(),
"org.lsposed.lspatch.loader.LSPApplication")) {
entry_class_ = JNI_NewGlobalRef(env, entry_class);
}
}
void PatchLoader::Load(JNIEnv* env) {
InitSymbolCache(nullptr);
lsplant::InitInfo initInfo{
.inline_hooker = [](auto t, auto r) {
void* bk = nullptr;
return HookFunction(t, r, &bk) == RS_SUCCESS ? bk : nullptr;
},
.inline_unhooker = [](auto t) {
return UnhookFunction(t) == RT_SUCCESS;
},
.art_symbol_resolver = [](auto symbol) {
return GetArt()->getSymbAddress<void*>(symbol);
},
};
InstallInlineHooks(initInfo);
auto stub = JNI_FindClass(env, "org/lsposed/lspatch/appstub/LSPAppComponentFactoryStub");
auto dex_field = JNI_GetStaticFieldID(env, stub, "dex", "[B");
auto array = (jbyteArray) env->GetStaticObjectField(stub, dex_field);
auto dex = PreloadedDex{env->GetByteArrayElements(array, nullptr), static_cast<size_t>(JNI_GetArrayLength(env, array))};
LoadDex(env, std::move(dex));
InitHooks(env, initInfo);
SetupEntryClass(env);
FindAndCall(env, "onLoad", "()V");
}
} // namespace lspd

View File

@ -0,0 +1,51 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
//
// Created by Nullptr on 2022/3/17.
//
#pragma once
#include "context.h"
namespace lspd {
inline lsplant::InitInfo handler;
class PatchLoader : public Context {
public:
inline static void Init() {
instance_ = std::make_unique<PatchLoader>();
}
inline static PatchLoader* GetInstance() {
return static_cast<PatchLoader*>(instance_.get());
}
void Load(JNIEnv* env);
protected:
void InitHooks(JNIEnv* env, const lsplant::InitInfo& initInfo) override;
void LoadDex(JNIEnv* env, PreloadedDex&& dex) override;
void SetupEntryClass(JNIEnv* env) override;
};
} // namespace lspd

View File

@ -240,8 +240,8 @@ public class LSPatch {
// 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 liblspd.so into apk!lib because x86 native bridge causes crash
for (String arch : APK_LIB_PATH_ARRAY) { for (String arch : APK_LIB_PATH_ARRAY) {
String entryName = "assets/lspatch/lspd/" + arch + "/liblspd.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)) + "/liblspd.so")) { try (var is = getClass().getClassLoader().getResourceAsStream("assets/so/" + (arch.equals("arm") ? "armeabi-v7a" : (arch.equals("arm64") ? "arm64-v8a" : arch)) + "/liblspatch.so")) {
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

View File

@ -1,27 +1,50 @@
rootProject.name = "LSPatch"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
include(":daemon-service") pluginManagement {
include(":hiddenapi-bridge") val agpVersion: String by settings
include(":hiddenapi-stubs") repositories {
include(":interface") gradlePluginPortal()
include(":lspcore") google()
include(":manager-service") mavenCentral()
}
plugins {
id("com.android.library") version agpVersion
id("com.android.application") version agpVersion
}
}
project(":daemon-service").projectDir = file("core/daemon-service") dependencyResolutionManagement {
project(":hiddenapi-bridge").projectDir = file("core/hiddenapi-bridge") repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
project(":hiddenapi-stubs").projectDir = file("core/hiddenapi-stubs") repositories {
project(":interface").projectDir = file("core/service/interface") google()
project(":lspcore").projectDir = file("core/core") mavenCentral()
project(":manager-service").projectDir = file("core/manager-service") }
}
include(":apkzlib") rootProject.name = "LSPatch"
include(":app") include(
include(":appstub") ":apkzlib",
include(":axmlprinter") ":appstub",
include(":imanager") ":axmlprinter",
include(":manager") ":core",
include(":patch") ":hiddenapi:bridge",
include(":patch-jar") ":hiddenapi:stubs",
include(":share") ":imanager",
":manager",
":patch",
":patch-jar",
":patch-loader",
":services:daemon-service",
":services:manager-service",
":services:xposed-service:interface",
":share"
)
project(":core").projectDir = file("core/core")
project(":hiddenapi:bridge").projectDir = file("core/hiddenapi/bridge")
project(":hiddenapi:stubs").projectDir = file("core/hiddenapi/stubs")
project(":services:daemon-service").projectDir = file("core/services/daemon-service")
project(":services:manager-service").projectDir = file("core/services/manager-service")
project(":services:xposed-service:interface").projectDir = file("core/services/xposed-service/interface")
buildCache { local { removeUnusedEntriesAfterDays = 1 } }