/* * 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 . * * Copyright (C) 2021 - 2022 LSPosed Contributors */ import org.apache.commons.codec.binary.Hex import org.apache.tools.ant.filters.FixCrLfFilter import org.apache.tools.ant.filters.ReplaceTokens import java.io.ByteArrayOutputStream import java.security.MessageDigest plugins { alias(libs.plugins.agp.app) alias(libs.plugins.lsplugin.resopt) } val moduleName = "LSPosed" val moduleBaseId = "lsposed" val authors = "JingMatrix & LSPosed Developers" val injectedPackageName: String by rootProject.extra val injectedPackageUid: Int by rootProject.extra val defaultManagerPackageName: String by rootProject.extra val verCode: Int by rootProject.extra val verName: String by rootProject.extra android { flavorDimensions += "api" buildFeatures { prefab = true buildConfig = true } defaultConfig { applicationId = "org.lsposed.lspd" multiDexEnabled = false buildConfigField( "String", "DEFAULT_MANAGER_PACKAGE_NAME", """"$defaultManagerPackageName"""" ) buildConfigField("String", "MANAGER_INJECTED_PKG_NAME", """"$injectedPackageName"""") buildConfigField("int", "MANAGER_INJECTED_UID", """$injectedPackageUid""") } buildTypes { release { isMinifyEnabled = true proguardFiles("proguard-rules.pro") } } externalNativeBuild { cmake { path("src/main/jni/CMakeLists.txt") } } productFlavors { all { externalNativeBuild { cmake { arguments += "-DMODULE_NAME=${name.lowercase()}_$moduleBaseId" arguments += "-DAPI=${name.lowercase()}" } } } create("Zygisk") { dimension = "api" externalNativeBuild { cmake { arguments += "-DAPI_VERSION=1" } } } } namespace = "org.lsposed.lspd" } abstract class Injected @Inject constructor(val magiskDir: String) { @get:Inject abstract val factory: ObjectFactory } dependencies { implementation(projects.core) implementation(projects.hiddenapi.bridge) implementation(projects.services.managerService) implementation(projects.services.daemonService) compileOnly(libs.androidx.annotation) compileOnly(projects.hiddenapi.stubs) } val zipAll = tasks.register("zipAll") { group = "LSPosed" } fun afterEval() = android.applicationVariants.forEach { variant -> val variantCapped = variant.name.replaceFirstChar { it.uppercase() } val variantLowered = variant.name.lowercase() val buildTypeCapped = variant.buildType.name.replaceFirstChar { it.uppercase() } val buildTypeLowered = variant.buildType.name.lowercase() val flavorCapped = variant.flavorName!!.replaceFirstChar { it.uppercase() } val flavorLowered = variant.flavorName!!.lowercase() val magiskDir = layout.buildDirectory.dir("magisk/$variantLowered") val moduleId = "${flavorLowered}_$moduleBaseId" val zipFileName = "$moduleName-v$verName-$verCode-${flavorLowered}-$buildTypeLowered.zip" val prepareMagiskFilesTask = tasks.register("prepareMagiskFiles$variantCapped") { group = "LSPosed" dependsOn( "assemble$variantCapped", ":app:package$buildTypeCapped", ":daemon:package$buildTypeCapped", ":dex2oat:externalNativeBuild${buildTypeCapped}" ) into(magiskDir) from("${rootProject.projectDir}/README.md") from("$projectDir/magisk_module") { exclude("module.prop", "customize.sh", "daemon") } from("$projectDir/magisk_module") { include("module.prop") expand( "moduleId" to moduleId, "versionName" to "v$verName", "versionCode" to verCode, "authorList" to authors, "updateJson" to "https://raw.githubusercontent.com/JingMatrix/LSPosed/master/magisk-loader/update/${flavorLowered}.json", "requirement" to when (flavorLowered) { "zygisk" -> "Requires Magisk 26.0+ and Zygisk enabled" else -> "No further requirements" }, "api" to flavorCapped, ) filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) } from("$projectDir/magisk_module") { include("customize.sh", "daemon") val tokens = mapOf( "FLAVOR" to flavorLowered, "DEBUG" to if (buildTypeLowered == "debug") "true" else "false" ) filter("tokens" to tokens) filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) } from(project(":app").tasks.getByName("package$buildTypeCapped").outputs) { include("*.apk") rename(".*\\.apk", "manager.apk") } from(project(":daemon").tasks.getByName("package$buildTypeCapped").outputs) { include("*.apk") rename(".*\\.apk", "daemon.apk") } into("lib") { val libDir = variantCapped + "/strip${variantCapped}DebugSymbols" from(layout.buildDirectory.dir("intermediates/stripped_native_libs/$libDir/out/lib")) { include("**/liblspd.so") } } into("bin") { from(project(":dex2oat").layout.buildDirectory.dir("intermediates/cmake/$buildTypeLowered/obj")) { include("**/dex2oat") include("**/liboat_hook.so") } } val dexOutPath = if (buildTypeLowered == "release") layout.buildDirectory.dir("intermediates/dex/$variantCapped/minify${variantCapped}WithR8") else layout.buildDirectory.dir("intermediates/dex/$variantCapped/mergeDex$variantCapped") into("framework") { from(dexOutPath) rename("classes.dex", "lspd.dex") } val injected = objects.newInstance(magiskDir.get().asFile.path) doLast { injected.factory.fileTree().from(injected.magiskDir).visit { if (isDirectory) return@visit val md = MessageDigest.getInstance("SHA-256") file.forEachBlock(4096) { bytes, size -> md.update(bytes, 0, size) } File(file.path + ".sha256").writeText(Hex.encodeHexString(md.digest())) } } } val zipTask = tasks.register("zip${variantCapped}") { group = "LSPosed" dependsOn(prepareMagiskFilesTask) archiveFileName = zipFileName destinationDirectory = file("$projectDir/release") from(magiskDir) } zipAll.configure { dependsOn(zipTask) } val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath val pushTask = tasks.register("push${variantCapped}") { group = "LSPosed" dependsOn(zipTask) workingDir("${projectDir}/release") commandLine(adb, "push", zipFileName, "/data/local/tmp/") } val installMagiskTask = tasks.register("installMagisk${variantCapped}") { group = "LSPosed" dependsOn(pushTask) commandLine( adb, "shell", "su", "-c", "magisk --install-module /data/local/tmp/${zipFileName}" ) } tasks.register("installMagiskAndReboot${variantCapped}") { group = "LSPosed" dependsOn(installMagiskTask) commandLine(adb, "shell", "su", "-c", "/system/bin/svc", "power", "reboot") } val installKsuTask = tasks.register("installKsu${variantCapped}") { group = "LSPosed" dependsOn(pushTask) commandLine( adb, "shell", "su", "-c", "ksud module install /data/local/tmp/${zipFileName}" ) } tasks.register("installKsuAndReboot${variantCapped}") { group = "LSPosed" dependsOn(installKsuTask) commandLine(adb, "shell", "su", "-c", "/system/bin/svc", "power", "reboot") } val installAPatchTask = tasks.register("installAPatch${variantCapped}") { group = "LSPosed" dependsOn(pushTask) commandLine( adb, "shell", "su", "-c", "apd module install /data/local/tmp/${zipFileName}" ) } tasks.register("installAPatchAndReboot${variantCapped}") { group = "LSPosed" dependsOn(installAPatchTask) commandLine(adb, "shell", "su", "-c", "/system/bin/svc", "power", "reboot") } } afterEvaluate { afterEval() } evaluationDependsOn(":app") evaluationDependsOn(":daemon")