Build script adjustments

This commit is contained in:
topjohnwu 2023-03-02 20:32:13 -08:00 committed by John Wu
parent 307cf87215
commit 8adf27859d
3 changed files with 138 additions and 124 deletions

View File

@ -1,10 +1,11 @@
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.InputStream
import java.io.PrintStream
import java.security.SecureRandom
import java.util.*
@ -78,6 +79,9 @@ fun genKeyData(keysDir: File, outSrc: File) {
@CacheableTask
abstract class ManifestUpdater: DefaultTask() {
@get:Input
abstract val applicationId: Property<String>
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val mergedManifest: RegularFileProperty
@ -99,74 +103,68 @@ abstract class ManifestUpdater: DefaultTask() {
val cmpList = mutableListOf<String>()
cmpList.add(
"""
|<provider
| android:name="x.COMPONENT_PLACEHOLDER_0"
| android:authorities="${'$'}{applicationId}.provider"
| android:directBootAware="true"
| android:exported="false"
| android:grantUriPermissions="true" />""".ind(2)
cmpList.add("""
|<provider
| android:name="x.COMPONENT_PLACEHOLDER_0"
| android:authorities="${'$'}{applicationId}.provider"
| android:directBootAware="true"
| android:exported="false"
| android:grantUriPermissions="true" />""".ind(2)
)
cmpList.add(
"""
|<receiver
| android:name="x.COMPONENT_PLACEHOLDER_1"
| android:exported="false">
| <intent-filter>
| <action android:name="android.intent.action.LOCALE_CHANGED" />
| <action android:name="android.intent.action.UID_REMOVED" />
| <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
| </intent-filter>
| <intent-filter>
| <action android:name="android.intent.action.PACKAGE_REPLACED" />
| <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
| <data android:scheme="package" />
| </intent-filter>
|</receiver>""".ind(2)
cmpList.add("""
|<receiver
| android:name="x.COMPONENT_PLACEHOLDER_1"
| android:exported="false">
| <intent-filter>
| <action android:name="android.intent.action.LOCALE_CHANGED" />
| <action android:name="android.intent.action.UID_REMOVED" />
| <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
| </intent-filter>
| <intent-filter>
| <action android:name="android.intent.action.PACKAGE_REPLACED" />
| <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
| <data android:scheme="package" />
| </intent-filter>
|</receiver>""".ind(2)
)
cmpList.add(
"""
|<activity
| android:name="x.COMPONENT_PLACEHOLDER_2"
| android:exported="true">
| <intent-filter>
| <action android:name="android.intent.action.MAIN" />
| <category android:name="android.intent.category.LAUNCHER" />
| </intent-filter>
|</activity>""".ind(2)
cmpList.add("""
|<activity
| android:name="x.COMPONENT_PLACEHOLDER_2"
| android:exported="true">
| <intent-filter>
| <action android:name="android.intent.action.MAIN" />
| <category android:name="android.intent.category.LAUNCHER" />
| </intent-filter>
|</activity>""".ind(2)
)
cmpList.add(
"""
|<activity
| android:name="x.COMPONENT_PLACEHOLDER_3"
| android:directBootAware="true"
| android:exported="false"
| android:taskAffinity="">
| <intent-filter>
| <action android:name="android.intent.action.VIEW"/>
| <category android:name="android.intent.category.DEFAULT"/>
| </intent-filter>
|</activity>""".ind(2)
cmpList.add("""
|<activity
| android:name="x.COMPONENT_PLACEHOLDER_3"
| android:directBootAware="true"
| android:exported="false"
| android:taskAffinity="">
| <intent-filter>
| <action android:name="android.intent.action.VIEW"/>
| <category android:name="android.intent.category.DEFAULT"/>
| </intent-filter>
|</activity>""".ind(2)
)
cmpList.add(
"""
|<service
| android:name="x.COMPONENT_PLACEHOLDER_4"
| android:exported="false" />""".ind(2)
cmpList.add("""
|<service
| android:name="x.COMPONENT_PLACEHOLDER_4"
| android:exported="false" />""".ind(2)
)
cmpList.add(
"""
|<service
| android:name="x.COMPONENT_PLACEHOLDER_5"
| android:exported="false"
| android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2)
cmpList.add("""
|<service
| android:name="x.COMPONENT_PLACEHOLDER_5"
| android:exported="false"
| android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2)
)
// Shuffle the order of the components
@ -177,15 +175,19 @@ abstract class ManifestUpdater: DefaultTask() {
val (appPkg, appClass) = appClassDir.asFileTree.first().let {
it.parentFile.name to it.name.removeSuffix(".java")
}
var manifest = mergedManifest.asFile.get().readText()
manifest = manifest.replace("<application", """<application android:appComponentFactory="$factoryPkg.$factoryClass" android:name="$appPkg.$appClass"""")
manifest = manifest.replace("</application", "${cmpList.joinToString("\n\n")}\n</application")
val components = cmpList.joinToString("\n\n")
.replace("\${applicationId}", applicationId.get())
val manifest = mergedManifest.asFile.get().readText().replace(Regex(".*\\<application"), """
|<application
| android:appComponentFactory="$factoryPkg.$factoryClass"
| android:name="$appPkg.$appClass"""".ind(1)
).replace(Regex(".*\\<\\/application"), components + "\n </application")
outputManifest.get().asFile.writeText(manifest)
}
}
fun genStubClass(factoryOutDir: File, appOutDir: File) {
fun genStubClasses(factoryOutDir: File, appOutDir: File) {
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
val classNameGenerator = sequence {
@ -231,7 +233,7 @@ fun genStubClass(factoryOutDir: File, appOutDir: File) {
genClass("DelegateApplication", appOutDir)
}
fun genEncryptedResources(res: InputStream, outDir: File) {
fun genEncryptedResources(res: ByteArray, outDir: File) {
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
mainPkgDir.mkdirs()
@ -245,7 +247,7 @@ fun genEncryptedResources(res: InputStream, outDir: File) {
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
val bos = ByteArrayOutputStream()
res.use {
ByteArrayInputStream(res).use {
CipherOutputStream(bos, cipher).use { os ->
it.transferTo(os)
}

View File

@ -17,18 +17,33 @@ import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.*
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.Sync
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.filter
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.registering
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.util.*
import java.util.jar.JarFile
import java.util.zip.*
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
private fun Project.androidBase(configure: Action<BaseExtension>) =
extensions.configure("android", configure)
@ -88,30 +103,6 @@ private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
return entry as KeyStore.PrivateKeyEntry
}
private fun addComment(inFile: File, outFile: File, signConfig: ApkSigningConfig, minSdk: Int, eocdComment: String) {
val privateKey = signConfig.getPrivateKey()
val signingOptions = SigningOptions.builder()
.setMinSdkVersion(minSdk)
.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 = eocdComment.toByteArray()
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
it.get(JarFile.MANIFEST_NAME)?.delete()
}
}
abstract class AddCommentTask: DefaultTask() {
@get:Input
abstract val comment: Property<String>
@ -132,7 +123,29 @@ abstract class AddCommentTask: DefaultTask() {
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
val inFile = File(artifact.outputFile)
val outFile = outFolder.file(inFile.name).get().asFile
addComment(inFile, outFile, signingConfig.get(), 0, comment.get())
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(JarFile.MANIFEST_NAME)?.delete()
}
outFile
}
}
@ -305,16 +318,15 @@ fun Project.setupApp() {
fun Project.setupStub() {
setupAppCommon()
androidComponents.onVariants { variant ->
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
val manifestUpdater =
project.tasks.register(
"${variant.name}ManifestProducer",
ManifestUpdater::class.java
) {
dependsOn("generate${variant.name.replaceFirstChar { it.uppercase() }}ObfuscatedClass")
appClassDir.set(File(buildDir, "generated/source/app/${variant.name}"))
factoryClassDir.set(File(buildDir, "generated/source/factory/${variant.name}"))
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
dependsOn("generate${variantCapped}ObfuscatedClass")
applicationId.set(variant.applicationId)
appClassDir.set(File(buildDir, "generated/source/app/$variantName"))
factoryClassDir.set(File(buildDir, "generated/source/factory/$variantName"))
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
@ -340,33 +352,34 @@ fun Project.setupStub() {
doLast {
outFactoryClassDir.mkdirs()
outAppClassDir.mkdirs()
genStubClass(outFactoryClassDir, outAppClassDir)
genStubClasses(outFactoryClassDir, outAppClassDir)
}
}
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
val processResourcesTask = tasks.getByPath(":stub:process${variantCapped}Resources")
processResourcesTask.doLast {
exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
doLast {
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)
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)
}
apkTmp.delete()
genEncryptedResources(ByteArrayInputStream(bos.toByteArray()), outResDir)
}
@Suppress("DEPRECATION")
registerJavaGeneratingTask(processResourcesTask, outResDir)
}
// Override optimizeReleaseResources task

View File

@ -7,8 +7,7 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<application
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon,UnusedAttribute">
<application tools:ignore="MissingApplicationIcon">
</application>
</manifest>