mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-07-28 15:53:43 +00:00
Reorganize buildSrc
This commit is contained in:
parent
b36066bbcd
commit
c4590fe2ba
77
app/buildSrc/src/main/java/AddCommentTask.kt
Normal file
77
app/buildSrc/src/main/java/AddCommentTask.kt
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import com.android.build.api.artifact.ArtifactTransformationRequest
|
||||||
|
import com.android.build.api.dsl.ApkSigningConfig
|
||||||
|
import com.android.builder.internal.packaging.IncrementalPackager
|
||||||
|
import com.android.tools.build.apkzlib.sign.SigningExtension
|
||||||
|
import com.android.tools.build.apkzlib.sign.SigningOptions
|
||||||
|
import com.android.tools.build.apkzlib.zfile.ZFiles
|
||||||
|
import com.android.tools.build.apkzlib.zip.ZFileOptions
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.InputFiles
|
||||||
|
import org.gradle.api.tasks.Internal
|
||||||
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import java.io.File
|
||||||
|
import java.security.KeyStore
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
|
||||||
|
abstract class AddCommentTask: DefaultTask() {
|
||||||
|
@get:Input
|
||||||
|
abstract val comment: Property<String>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val signingConfig: Property<ApkSigningConfig>
|
||||||
|
|
||||||
|
@get:InputFiles
|
||||||
|
abstract val apkFolder: DirectoryProperty
|
||||||
|
|
||||||
|
@get:OutputDirectory
|
||||||
|
abstract val outFolder: DirectoryProperty
|
||||||
|
|
||||||
|
@get:Internal
|
||||||
|
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
|
||||||
|
val inFile = File(artifact.outputFile)
|
||||||
|
val outFile = outFolder.file(inFile.name).get().asFile
|
||||||
|
|
||||||
|
val privateKey = signingConfig.get().getPrivateKey()
|
||||||
|
val signingOptions = SigningOptions.builder()
|
||||||
|
.setMinSdkVersion(0)
|
||||||
|
.setV1SigningEnabled(true)
|
||||||
|
.setV2SigningEnabled(true)
|
||||||
|
.setKey(privateKey.privateKey)
|
||||||
|
.setCertificates(privateKey.certificate as X509Certificate)
|
||||||
|
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
|
||||||
|
.build()
|
||||||
|
val options = ZFileOptions().apply {
|
||||||
|
noTimestamps = true
|
||||||
|
autoSortFiles = true
|
||||||
|
}
|
||||||
|
outFile.parentFile?.mkdirs()
|
||||||
|
inFile.copyTo(outFile, overwrite = true)
|
||||||
|
ZFiles.apk(outFile, options).use {
|
||||||
|
SigningExtension(signingOptions).register(it)
|
||||||
|
it.eocdComment = comment.get().toByteArray()
|
||||||
|
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
|
||||||
|
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
|
||||||
|
it.get(JarFile.MANIFEST_NAME)?.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
|
||||||
|
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
|
||||||
|
storeFile!!.inputStream().use {
|
||||||
|
keyStore.load(it, storePassword!!.toCharArray())
|
||||||
|
}
|
||||||
|
val keyPwdArray = keyPassword!!.toCharArray()
|
||||||
|
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
|
||||||
|
return entry as KeyStore.PrivateKeyEntry
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,12 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.kotlin.dsl.provideDelegate
|
import org.gradle.kotlin.dsl.provideDelegate
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
import java.util.Random
|
||||||
|
|
||||||
|
// Set non-zero value here to fix the random seed for reproducible builds
|
||||||
|
// CI builds are always reproducible
|
||||||
|
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
|
||||||
|
lateinit var RANDOM: Random
|
||||||
|
|
||||||
private val props = Properties()
|
private val props = Properties()
|
||||||
private var commitHash = ""
|
private var commitHash = ""
|
||||||
|
@ -1,33 +1,18 @@
|
|||||||
import com.android.build.api.artifact.ArtifactTransformationRequest
|
|
||||||
import com.android.build.api.artifact.SingleArtifact
|
import com.android.build.api.artifact.SingleArtifact
|
||||||
import com.android.build.api.dsl.ApkSigningConfig
|
|
||||||
import com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
|
import com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
|
||||||
import com.android.build.api.instrumentation.InstrumentationScope
|
import com.android.build.api.instrumentation.InstrumentationScope
|
||||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||||
import com.android.build.gradle.BaseExtension
|
import com.android.build.gradle.BaseExtension
|
||||||
import com.android.build.gradle.LibraryExtension
|
import com.android.build.gradle.LibraryExtension
|
||||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||||
import com.android.builder.internal.packaging.IncrementalPackager
|
|
||||||
import com.android.tools.build.apkzlib.sign.SigningExtension
|
|
||||||
import com.android.tools.build.apkzlib.sign.SigningOptions
|
|
||||||
import com.android.tools.build.apkzlib.zfile.ZFiles
|
|
||||||
import com.android.tools.build.apkzlib.zip.ZFileOptions
|
|
||||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||||
import org.gradle.api.Action
|
import org.gradle.api.Action
|
||||||
import org.gradle.api.DefaultTask
|
|
||||||
import org.gradle.api.JavaVersion
|
import org.gradle.api.JavaVersion
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.file.DirectoryProperty
|
|
||||||
import org.gradle.api.provider.Property
|
|
||||||
import org.gradle.api.tasks.Copy
|
import org.gradle.api.tasks.Copy
|
||||||
import org.gradle.api.tasks.Delete
|
import org.gradle.api.tasks.Delete
|
||||||
import org.gradle.api.tasks.Input
|
|
||||||
import org.gradle.api.tasks.InputFiles
|
|
||||||
import org.gradle.api.tasks.Internal
|
|
||||||
import org.gradle.api.tasks.OutputDirectory
|
|
||||||
import org.gradle.api.tasks.StopExecutionException
|
import org.gradle.api.tasks.StopExecutionException
|
||||||
import org.gradle.api.tasks.Sync
|
import org.gradle.api.tasks.Sync
|
||||||
import org.gradle.api.tasks.TaskAction
|
|
||||||
import org.gradle.kotlin.dsl.assign
|
import org.gradle.kotlin.dsl.assign
|
||||||
import org.gradle.kotlin.dsl.exclude
|
import org.gradle.kotlin.dsl.exclude
|
||||||
import org.gradle.kotlin.dsl.filter
|
import org.gradle.kotlin.dsl.filter
|
||||||
@ -36,18 +21,14 @@ import org.gradle.kotlin.dsl.getValue
|
|||||||
import org.gradle.kotlin.dsl.named
|
import org.gradle.kotlin.dsl.named
|
||||||
import org.gradle.kotlin.dsl.provideDelegate
|
import org.gradle.kotlin.dsl.provideDelegate
|
||||||
import org.gradle.kotlin.dsl.register
|
import org.gradle.kotlin.dsl.register
|
||||||
import org.gradle.kotlin.dsl.registering
|
|
||||||
import org.gradle.kotlin.dsl.withType
|
import org.gradle.kotlin.dsl.withType
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.security.KeyStore
|
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.util.HexFormat
|
import java.util.HexFormat
|
||||||
import java.util.jar.JarFile
|
|
||||||
import java.util.zip.Deflater
|
import java.util.zip.Deflater
|
||||||
import java.util.zip.DeflaterOutputStream
|
import java.util.zip.DeflaterOutputStream
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
@ -60,13 +41,13 @@ private fun Project.androidBase(configure: Action<BaseExtension>) =
|
|||||||
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
|
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
|
||||||
extensions.configure("android", configure)
|
extensions.configure("android", configure)
|
||||||
|
|
||||||
private val Project.androidApp: BaseAppModuleExtension
|
internal val Project.androidApp: BaseAppModuleExtension
|
||||||
get() = extensions["android"] as BaseAppModuleExtension
|
get() = extensions["android"] as BaseAppModuleExtension
|
||||||
|
|
||||||
private val Project.androidLib: LibraryExtension
|
private val Project.androidLib: LibraryExtension
|
||||||
get() = extensions["android"] as LibraryExtension
|
get() = extensions["android"] as LibraryExtension
|
||||||
|
|
||||||
private val Project.androidComponents
|
internal val Project.androidComponents
|
||||||
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
|
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
|
||||||
|
|
||||||
fun Project.setupCommon() {
|
fun Project.setupCommon() {
|
||||||
@ -145,46 +126,42 @@ const val BUSYBOX_ZIP_CHECKSUM =
|
|||||||
fun Project.setupCoreLib() {
|
fun Project.setupCoreLib() {
|
||||||
setupCommon()
|
setupCommon()
|
||||||
|
|
||||||
val abiList = Config.abiList
|
androidLib.libraryVariants.all {
|
||||||
|
val variant = name
|
||||||
|
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||||
|
val abiList = Config.abiList
|
||||||
|
|
||||||
val syncLibs by tasks.registering(Sync::class) {
|
val syncLibs = tasks.register("sync${variantCapped}JniLibs", Sync::class) {
|
||||||
into("src/main/jniLibs")
|
into("src/$variant/jniLibs")
|
||||||
for (abi in abiList) {
|
for (abi in abiList) {
|
||||||
into(abi) {
|
into(abi) {
|
||||||
from(rootFile("native/out/$abi")) {
|
from(rootFile("native/out/$abi")) {
|
||||||
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
|
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
|
||||||
rename { if (it.endsWith(".so")) it else "lib$it.so" }
|
rename { if (it.endsWith(".so")) it else "lib$it.so" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
|
||||||
|
include(abiList.map { "$it/libbusybox.so" })
|
||||||
|
onlyIf {
|
||||||
|
if (inputs.sourceFiles.files.size != abiList.size * 6)
|
||||||
|
throw StopExecutionException("Please build binaries first! (./build.py binary)")
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onlyIf {
|
|
||||||
if (inputs.sourceFiles.files.size != abiList.size * 5)
|
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(syncLibs)
|
||||||
throw StopExecutionException("Please build binaries first! (./build.py binary)")
|
|
||||||
true
|
val syncResources = tasks.register("sync${variantCapped}Resources", Sync::class) {
|
||||||
|
into("src/$variant/resources/META-INF/com/google/android")
|
||||||
|
from(rootFile("scripts/update_binary.sh")) {
|
||||||
|
rename { "update-binary" }
|
||||||
|
}
|
||||||
|
from(rootFile("scripts/flash_script.sh")) {
|
||||||
|
rename { "updater-script" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val downloadBusybox by tasks.registering(Copy::class) {
|
|
||||||
dependsOn(syncLibs)
|
|
||||||
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
|
|
||||||
include(abiList.map { "$it/libbusybox.so" })
|
|
||||||
into("src/main/jniLibs")
|
|
||||||
}
|
|
||||||
|
|
||||||
val syncResources by tasks.registering(Sync::class) {
|
|
||||||
into("src/main/resources/META-INF/com/google/android")
|
|
||||||
from(rootFile("scripts/update_binary.sh")) {
|
|
||||||
rename { "update-binary" }
|
|
||||||
}
|
|
||||||
from(rootFile("scripts/flash_script.sh")) {
|
|
||||||
rename { "updater-script" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
androidLib.libraryVariants.all {
|
|
||||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
|
||||||
|
|
||||||
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(downloadBusybox)
|
|
||||||
processJavaResourcesProvider.configure { dependsOn(syncResources) }
|
processJavaResourcesProvider.configure { dependsOn(syncResources) }
|
||||||
|
|
||||||
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
||||||
@ -196,7 +173,7 @@ fun Project.setupCoreLib() {
|
|||||||
dependsOn(stubTask)
|
dependsOn(stubTask)
|
||||||
inputs.property("version", Config.version)
|
inputs.property("version", Config.version)
|
||||||
inputs.property("versionCode", Config.versionCode)
|
inputs.property("versionCode", Config.versionCode)
|
||||||
into("src/${this@all.name}/assets")
|
into("src/$variant/assets")
|
||||||
from(rootFile("scripts")) {
|
from(rootFile("scripts")) {
|
||||||
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
|
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
|
||||||
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
|
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
|
||||||
@ -229,64 +206,6 @@ fun Project.setupCoreLib() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
|
|
||||||
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
|
|
||||||
storeFile!!.inputStream().use {
|
|
||||||
keyStore.load(it, storePassword!!.toCharArray())
|
|
||||||
}
|
|
||||||
val keyPwdArray = keyPassword!!.toCharArray()
|
|
||||||
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
|
|
||||||
return entry as KeyStore.PrivateKeyEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AddCommentTask: DefaultTask() {
|
|
||||||
@get:Input
|
|
||||||
abstract val comment: Property<String>
|
|
||||||
|
|
||||||
@get:Input
|
|
||||||
abstract val signingConfig: Property<ApkSigningConfig>
|
|
||||||
|
|
||||||
@get:InputFiles
|
|
||||||
abstract val apkFolder: DirectoryProperty
|
|
||||||
|
|
||||||
@get:OutputDirectory
|
|
||||||
abstract val outFolder: DirectoryProperty
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
|
|
||||||
val inFile = File(artifact.outputFile)
|
|
||||||
val outFile = outFolder.file(inFile.name).get().asFile
|
|
||||||
|
|
||||||
val privateKey = signingConfig.get().getPrivateKey()
|
|
||||||
val signingOptions = SigningOptions.builder()
|
|
||||||
.setMinSdkVersion(0)
|
|
||||||
.setV1SigningEnabled(true)
|
|
||||||
.setV2SigningEnabled(true)
|
|
||||||
.setKey(privateKey.privateKey)
|
|
||||||
.setCertificates(privateKey.certificate as X509Certificate)
|
|
||||||
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
|
|
||||||
.build()
|
|
||||||
val options = ZFileOptions().apply {
|
|
||||||
noTimestamps = true
|
|
||||||
autoSortFiles = true
|
|
||||||
}
|
|
||||||
outFile.parentFile?.mkdirs()
|
|
||||||
inFile.copyTo(outFile, overwrite = true)
|
|
||||||
ZFiles.apk(outFile, options).use {
|
|
||||||
SigningExtension(signingOptions).register(it)
|
|
||||||
it.eocdComment = comment.get().toByteArray()
|
|
||||||
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
|
|
||||||
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
|
|
||||||
it.get(JarFile.MANIFEST_NAME)?.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
outFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Project.setupAppCommon() {
|
fun Project.setupAppCommon() {
|
||||||
setupCommon()
|
setupCommon()
|
||||||
|
|
||||||
@ -382,89 +301,6 @@ fun Project.setupMainApk() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Project.setupStubApk() {
|
|
||||||
setupAppCommon()
|
|
||||||
|
|
||||||
androidComponents.onVariants { variant ->
|
|
||||||
val variantName = variant.name
|
|
||||||
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
|
||||||
val manifestUpdater =
|
|
||||||
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
|
|
||||||
dependsOn("generate${variantCapped}ObfuscatedClass")
|
|
||||||
applicationId = variant.applicationId
|
|
||||||
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
|
|
||||||
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
|
|
||||||
}
|
|
||||||
variant.artifacts.use(manifestUpdater)
|
|
||||||
.wiredWithFiles(
|
|
||||||
ManifestUpdater::mergedManifest,
|
|
||||||
ManifestUpdater::outputManifest)
|
|
||||||
.toTransform(SingleArtifact.MERGED_MANIFEST)
|
|
||||||
}
|
|
||||||
|
|
||||||
androidApp.applicationVariants.all {
|
|
||||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
|
||||||
val variantLowered = name.lowercase()
|
|
||||||
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
|
|
||||||
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
|
|
||||||
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
|
|
||||||
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
|
|
||||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
|
||||||
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
|
|
||||||
|
|
||||||
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
|
|
||||||
inputs.property("seed", RAND_SEED)
|
|
||||||
outputs.dirs(outFactoryClassDir, outAppClassDir)
|
|
||||||
doLast {
|
|
||||||
outFactoryClassDir.mkdirs()
|
|
||||||
outAppClassDir.mkdirs()
|
|
||||||
genStubClasses(outFactoryClassDir, outAppClassDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
|
|
||||||
|
|
||||||
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
|
|
||||||
outputs.dir(outResDir)
|
|
||||||
doLast {
|
|
||||||
val apkTmp = File("${apk}.tmp")
|
|
||||||
exec {
|
|
||||||
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
|
||||||
}
|
|
||||||
|
|
||||||
val bos = ByteArrayOutputStream()
|
|
||||||
ZipFile(apkTmp).use { src ->
|
|
||||||
ZipOutputStream(apk.outputStream()).use {
|
|
||||||
it.setLevel(Deflater.BEST_COMPRESSION)
|
|
||||||
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
|
||||||
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
|
||||||
it.closeEntry()
|
|
||||||
}
|
|
||||||
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
|
||||||
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apkTmp.delete()
|
|
||||||
genEncryptedResources(bos.toByteArray(), outResDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerJavaGeneratingTask(processResourcesTask, outResDir)
|
|
||||||
}
|
|
||||||
// Override optimizeReleaseResources task
|
|
||||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
|
||||||
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
|
|
||||||
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
|
|
||||||
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
|
|
||||||
afterEvaluate {
|
|
||||||
tasks.named("optimizeReleaseResources") {
|
|
||||||
doLast { apk.copyTo(optRes, true) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks.named<Delete>("clean") {
|
|
||||||
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const val LSPOSED_DOWNLOAD_URL =
|
const val LSPOSED_DOWNLOAD_URL =
|
||||||
"https://github.com/LSPosed/LSPosed/releases/download/v1.9.2/LSPosed-v1.9.2-7024-zygisk-release.zip"
|
"https://github.com/LSPosed/LSPosed/releases/download/v1.9.2/LSPosed-v1.9.2-7024-zygisk-release.zip"
|
||||||
const val LSPOSED_CHECKSUM =
|
const val LSPOSED_CHECKSUM =
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import com.android.build.api.artifact.SingleArtifact
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.file.DirectoryProperty
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.file.RegularFileProperty
|
import org.gradle.api.file.RegularFileProperty
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.tasks.CacheableTask
|
import org.gradle.api.tasks.CacheableTask
|
||||||
|
import org.gradle.api.tasks.Delete
|
||||||
import org.gradle.api.tasks.Input
|
import org.gradle.api.tasks.Input
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
import org.gradle.api.tasks.InputFiles
|
import org.gradle.api.tasks.InputFiles
|
||||||
@ -10,22 +13,25 @@ import org.gradle.api.tasks.OutputFile
|
|||||||
import org.gradle.api.tasks.PathSensitive
|
import org.gradle.api.tasks.PathSensitive
|
||||||
import org.gradle.api.tasks.PathSensitivity
|
import org.gradle.api.tasks.PathSensitivity
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.gradle.kotlin.dsl.assign
|
||||||
|
import org.gradle.kotlin.dsl.named
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Random
|
import java.util.Random
|
||||||
|
import java.util.zip.Deflater
|
||||||
|
import java.util.zip.DeflaterOutputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.CipherOutputStream
|
import javax.crypto.CipherOutputStream
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.random.asKotlinRandom
|
import kotlin.random.asKotlinRandom
|
||||||
|
|
||||||
// Set non-zero value here to fix the random seed for reproducible builds
|
|
||||||
// CI builds are always reproducible
|
|
||||||
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
|
|
||||||
private lateinit var RANDOM: Random
|
|
||||||
private val kRANDOM get() = RANDOM.asKotlinRandom()
|
private val kRANDOM get() = RANDOM.asKotlinRandom()
|
||||||
|
|
||||||
private val c1 = mutableListOf<String>()
|
private val c1 = mutableListOf<String>()
|
||||||
@ -72,7 +78,7 @@ private fun PrintStream.byteField(name: String, bytes: ByteArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CacheableTask
|
@CacheableTask
|
||||||
abstract class ManifestUpdater: DefaultTask() {
|
private abstract class ManifestUpdater: DefaultTask() {
|
||||||
@get:Input
|
@get:Input
|
||||||
abstract val applicationId: Property<String>
|
abstract val applicationId: Property<String>
|
||||||
|
|
||||||
@ -182,9 +188,7 @@ abstract class ManifestUpdater: DefaultTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||||
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
|
|
||||||
|
|
||||||
val classNameGenerator = sequence {
|
val classNameGenerator = sequence {
|
||||||
fun notJavaKeyword(name: String) = when (name) {
|
fun notJavaKeyword(name: String) = when (name) {
|
||||||
"do", "if", "for", "int", "new", "try" -> false
|
"do", "if", "for", "int", "new", "try" -> false
|
||||||
@ -228,7 +232,7 @@ fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
|||||||
genClass("StubApplication", appOutDir)
|
genClass("StubApplication", appOutDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genEncryptedResources(res: ByteArray, outDir: File) {
|
private fun genEncryptedResources(res: ByteArray, outDir: File) {
|
||||||
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
||||||
mainPkgDir.mkdirs()
|
mainPkgDir.mkdirs()
|
||||||
|
|
||||||
@ -259,3 +263,86 @@ fun genEncryptedResources(res: ByteArray, outDir: File) {
|
|||||||
it.println("}")
|
it.println("}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Project.setupStubApk() {
|
||||||
|
setupAppCommon()
|
||||||
|
|
||||||
|
androidComponents.onVariants { variant ->
|
||||||
|
val variantName = variant.name
|
||||||
|
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
||||||
|
val manifestUpdater =
|
||||||
|
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
|
||||||
|
dependsOn("generate${variantCapped}ObfuscatedClass")
|
||||||
|
applicationId = variant.applicationId
|
||||||
|
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
|
||||||
|
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
|
||||||
|
}
|
||||||
|
variant.artifacts.use(manifestUpdater)
|
||||||
|
.wiredWithFiles(
|
||||||
|
ManifestUpdater::mergedManifest,
|
||||||
|
ManifestUpdater::outputManifest)
|
||||||
|
.toTransform(SingleArtifact.MERGED_MANIFEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
androidApp.applicationVariants.all {
|
||||||
|
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||||
|
val variantLowered = name.lowercase()
|
||||||
|
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
|
||||||
|
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
|
||||||
|
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
|
||||||
|
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
|
||||||
|
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||||
|
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
|
||||||
|
|
||||||
|
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
|
||||||
|
inputs.property("seed", RAND_SEED)
|
||||||
|
outputs.dirs(outFactoryClassDir, outAppClassDir)
|
||||||
|
doLast {
|
||||||
|
outFactoryClassDir.mkdirs()
|
||||||
|
outAppClassDir.mkdirs()
|
||||||
|
genStubClasses(outFactoryClassDir, outAppClassDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
|
||||||
|
|
||||||
|
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
|
||||||
|
outputs.dir(outResDir)
|
||||||
|
doLast {
|
||||||
|
val apkTmp = File("${apk}.tmp")
|
||||||
|
exec {
|
||||||
|
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
ZipFile(apkTmp).use { src ->
|
||||||
|
ZipOutputStream(apk.outputStream()).use {
|
||||||
|
it.setLevel(Deflater.BEST_COMPRESSION)
|
||||||
|
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||||
|
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
||||||
|
it.closeEntry()
|
||||||
|
}
|
||||||
|
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
||||||
|
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apkTmp.delete()
|
||||||
|
genEncryptedResources(bos.toByteArray(), outResDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerJavaGeneratingTask(processResourcesTask, outResDir)
|
||||||
|
}
|
||||||
|
// Override optimizeReleaseResources task
|
||||||
|
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||||
|
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
|
||||||
|
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
|
||||||
|
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
|
||||||
|
afterEvaluate {
|
||||||
|
tasks.named("optimizeReleaseResources") {
|
||||||
|
doLast { apk.copyTo(optRes, true) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.named<Delete>("clean") {
|
||||||
|
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
|
||||||
|
}
|
||||||
|
}
|
5
app/core/.gitignore
vendored
5
app/core/.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
src/*/assets
|
src/debug
|
||||||
src/*/jniLibs
|
src/release
|
||||||
src/*/resources
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user