Unverified Commit df191cd2 authored by 南宫雪珊's avatar 南宫雪珊 Committed by GitHub

Use AGP to compile resources

parent baa19f0c
...@@ -39,11 +39,6 @@ android { ...@@ -39,11 +39,6 @@ android {
dataBinding = true dataBinding = true
} }
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
packagingOptions { packagingOptions {
resources { resources {
excludes += "/META-INF/*" excludes += "/META-INF/*"
......
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.InputStream
import java.io.PrintStream import java.io.PrintStream
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
import java.util.zip.GZIPOutputStream
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
...@@ -54,7 +52,7 @@ private fun <T> chain(vararg iters: Iterable<T>) = sequence { ...@@ -54,7 +52,7 @@ private fun <T> chain(vararg iters: Iterable<T>) = sequence {
private fun PrintStream.byteField(name: String, bytes: ByteArray) { private fun PrintStream.byteField(name: String, bytes: ByteArray) {
println("public static byte[] $name() {") println("public static byte[] $name() {")
print("byte[] buf = {") print("byte[] buf = {")
print(bytes.joinToString(",") { "(byte)(${it.toInt() and 0xff})" }) print(bytes.joinToString(",") { it.toString() })
println("};") println("};")
println("return buf;") println("return buf;")
println("}") println("}")
...@@ -192,10 +190,8 @@ fun genStubManifest(srcDir: File, outDir: File): String { ...@@ -192,10 +190,8 @@ fun genStubManifest(srcDir: File, outDir: File): String {
names.addAll(c3.subList(0, 10)) names.addAll(c3.subList(0, 10))
names.shuffle(RANDOM) names.shuffle(RANDOM)
// Decapitalize as older Android versions do not allow capitalized package names
// Distinct by lower case to support case insensitive file systems // Distinct by lower case to support case insensitive file systems
val pkgNames = names.map { it.decapitalize(Locale.ROOT) } val pkgNames = names.distinctBy { it.toLowerCase(Locale.ROOT) }
.distinctBy { it.toLowerCase(Locale.ROOT) }
var idx = 0 var idx = 0
fun genCmpName() = "${pkgNames[idx++]}.${names.random(kRANDOM)}" fun genCmpName() = "${pkgNames[idx++]}.${names.random(kRANDOM)}"
...@@ -247,15 +243,8 @@ fun genStubManifest(srcDir: File, outDir: File): String { ...@@ -247,15 +243,8 @@ fun genStubManifest(srcDir: File, outDir: File): String {
return genXml return genXml
} }
fun genEncryptedResources(res: File, outDir: File) { fun genEncryptedResources(res: InputStream, outDir: File) {
val mainPkgDir = File(outDir, "com/topjohnwu/magisk") val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
// Rename R.java
val r = File(mainPkgDir, "R.java").let {
val txt = it.readText()
it.delete()
txt
}
File(mainPkgDir, "R2.java").writeText(r.replace("class R", "class R2"))
// Generate iv and key // Generate iv and key
val iv = ByteArray(16) val iv = ByteArray(16)
...@@ -267,9 +256,8 @@ fun genEncryptedResources(res: File, outDir: File) { ...@@ -267,9 +256,8 @@ fun genEncryptedResources(res: File, outDir: File) {
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
val bos = ByteArrayOutputStream() val bos = ByteArrayOutputStream()
FileInputStream(res).use { res.use {
// First compress, then encrypt CipherOutputStream(bos, cipher).use { os ->
GZIPOutputStream(CipherOutputStream(bos, cipher)).use { os ->
it.transferTo(os) it.transferTo(os)
} }
} }
......
import com.android.build.gradle.BaseExtension import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
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.tasks.StopExecutionException import org.gradle.api.tasks.StopExecutionException
import org.gradle.api.tasks.Sync import org.gradle.api.tasks.Sync
import org.gradle.internal.os.OperatingSystem
import org.gradle.kotlin.dsl.filter import org.gradle.kotlin.dsl.filter
import org.gradle.kotlin.dsl.named import java.io.*
import java.io.File
import java.io.OutputStream
import java.io.PrintStream
import java.nio.file.Paths
import java.util.* import java.util.*
import java.util.zip.Deflater
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)
private fun Project.android(configure: Action<BaseExtension>) = private fun Project.android(configure: Action<BaseAppModuleExtension>) =
extensions.configure("android", configure) extensions.configure("android", configure)
private val Project.android: BaseAppModuleExtension get() = private val Project.android: BaseAppModuleExtension
extensions.getByName("android") as BaseAppModuleExtension get() = extensions.getByName("android") as BaseAppModuleExtension
fun Project.setupCommon() { fun Project.setupCommon() {
android { androidBase {
compileSdkVersion(31) compileSdkVersion(31)
buildToolsVersion = "31.0.0" buildToolsVersion = "31.0.0"
ndkPath = "${System.getenv("ANDROID_SDK_ROOT")}/ndk/magisk" ndkPath = "${System.getenv("ANDROID_SDK_ROOT")}/ndk/magisk"
...@@ -69,9 +69,13 @@ private fun Project.setupAppCommon() { ...@@ -69,9 +69,13 @@ private fun Project.setupAppCommon() {
} }
} }
lintOptions { lint {
disable += "MissingTranslation" disable += "MissingTranslation"
} }
dependenciesInfo {
includeInApk = false
}
} }
} }
...@@ -147,11 +151,9 @@ fun Project.setupApp() { ...@@ -147,11 +151,9 @@ fun Project.setupApp() {
} }
} }
tasks.named<DefaultTask>("preBuild") {
dependsOn(syncResources)
}
android.applicationVariants.all { android.applicationVariants.all {
preBuildProvider.get().dependsOn(syncResources)
val keysDir = rootProject.file("tools/keys") val keysDir = rootProject.file("tools/keys")
val outSrcDir = File(buildDir, "generated/source/keydata/$name") val outSrcDir = File(buildDir, "generated/source/keydata/$name")
val outSrc = File(outSrcDir, "com/topjohnwu/magisk/signing/KeyData.java") val outSrc = File(outSrcDir, "com/topjohnwu/magisk/signing/KeyData.java")
...@@ -170,91 +172,67 @@ fun Project.setupApp() { ...@@ -170,91 +172,67 @@ fun Project.setupApp() {
fun Project.setupStub() { fun Project.setupStub() {
setupAppCommon() setupAppCommon()
// Make sure we have a working manifest while building
val ensureManifest = tasks.register("ensureManifest") {
val manifest = file("src/main/AndroidManifest.xml")
if (!manifest.exists()) {
PrintStream(manifest).use {
it.println("<manifest package=\"com.topjohnwu.magisk\"/>")
}
}
}
tasks.named<DefaultTask>("preBuild") {
dependsOn(ensureManifest)
}
android.applicationVariants.all { android.applicationVariants.all {
val manifest = file("src/main/AndroidManifest.xml") val variantCapped = name.capitalize(Locale.ROOT)
val outSrcDir = File(buildDir, "generated/source/obfuscate/$name") val variantLowered = name.toLowerCase(Locale.ROOT)
val manifest = file("src/${variantLowered}/AndroidManifest.xml")
val outSrcDir = File(buildDir, "generated/source/obfuscate/${variantLowered}")
val templateDir = file("template") val templateDir = file("template")
val resDir = file("res") val aapt = File(android.sdkDirectory, "build-tools/${android.buildToolsVersion}/aapt2")
val apk = File(buildDir, "intermediates/processed_res/" +
"${variantLowered}/out/resources-${variantLowered}.ap_")
val apkTmp = File("${apk}.tmp")
val androidJar = Paths.get(android.sdkDirectory.path, "platforms", val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedManifest") {
android.compileSdkVersion, "android.jar")
val aaptCommand = if (OperatingSystem.current().isWindows) "aapt2.exe" else "aapt2"
val aapt = Paths.get(android.sdkDirectory.path,
"build-tools", android.buildToolsVersion, aaptCommand)
val dummy = object : OutputStream() {
override fun write(b: Int) {}
override fun write(bytes: ByteArray, off: Int, len: Int) {}
}
val genSrcTask = tasks.register("generate${name.capitalize(Locale.ROOT)}ObfuscatedSources") {
doLast { doLast {
val xml = genStubManifest(templateDir, outSrcDir) val xml = genStubManifest(templateDir, outSrcDir)
manifest.parentFile.mkdirs()
PrintStream(manifest).use { PrintStream(manifest).use {
it.print(xml) it.print(xml)
} }
}
}
mergeResourcesProvider.get().dependsOn(genManifestTask)
val compileTmp = File.createTempFile("tmp", ".zip") val genSrcTask = tasks.register("generate${variantCapped}ObfuscatedSources") {
val linkTmp = File.createTempFile("tmp", ".zip") dependsOn(":stub:process${variantCapped}Resources")
val optTmp = File.createTempFile("tmp", ".zip") doLast {
val stubXml = File.createTempFile("tmp", ".xml") exec {
try { commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
PrintStream(stubXml).use { }
it.println("<manifest package=\"com.topjohnwu.magisk\"/>")
}
exec {
commandLine(aapt, "compile",
"-o", compileTmp,
"--dir", resDir)
standardOutput = dummy
errorOutput = dummy
}
exec { val buffer = ByteArrayOutputStream(apk.length().toInt())
commandLine(aapt, "link", val newApk = ZipOutputStream(FileOutputStream(apk))
"-o", linkTmp, ZipFile(apkTmp).use {
"-I", androidJar, newApk.use { new ->
"--min-sdk-version", android.defaultConfig.minSdk, new.setLevel(Deflater.BEST_COMPRESSION)
"--target-sdk-version", android.defaultConfig.targetSdk, new.putNextEntry(ZipEntry("AndroidManifest.xml"))
"--manifest", stubXml, it.getInputStream(it.getEntry("AndroidManifest.xml")).transferTo(new)
"--java", outSrcDir, compileTmp) new.closeEntry()
standardOutput = dummy new.finish()
errorOutput = dummy
} }
ZipOutputStream(buffer).use { arsc ->
exec { arsc.setLevel(Deflater.BEST_COMPRESSION)
commandLine(aapt, "optimize", arsc.putNextEntry(ZipEntry("resources.arsc"))
"-o", optTmp, it.getInputStream(it.getEntry("resources.arsc")).transferTo(arsc)
"--collapse-resource-names", linkTmp) arsc.closeEntry()
standardOutput = dummy arsc.finish()
errorOutput = dummy
} }
genEncryptedResources(optTmp, outSrcDir)
} finally {
compileTmp.delete()
linkTmp.delete()
optTmp.delete()
stubXml.delete()
} }
apkTmp.delete()
genEncryptedResources(ByteArrayInputStream(buffer.toByteArray()), outSrcDir)
} }
} }
registerJavaGeneratingTask(genSrcTask, outSrcDir) registerJavaGeneratingTask(genSrcTask, outSrcDir)
} }
// Override optimizeReleaseResources task
tasks.whenTaskAdded {
val apk = File(buildDir, "intermediates/processed_res/" +
"release/out/resources-release.ap_")
val optRes = File(buildDir, "intermediates/optimized_processed_res/" +
"release/resources-release-optimize.ap_")
if (name == "optimizeReleaseResources") {
doLast { apk.copyTo(optRes, true) }
}
}
} }
/build /build
/src/main/AndroidManifest.xml /src/release/AndroidManifest.xml
/src/debug/AndroidManifest.xml
...@@ -29,11 +29,6 @@ android { ...@@ -29,11 +29,6 @@ android {
proguardFiles("proguard-rules.pro") proguardFiles("proguard-rules.pro")
} }
} }
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
} }
setupStub() setupStub()
......
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.topjohnwu.magisk" />
...@@ -3,10 +3,10 @@ package com.topjohnwu.magisk; ...@@ -3,10 +3,10 @@ package com.topjohnwu.magisk;
import static android.R.string.no; import static android.R.string.no;
import static android.R.string.ok; import static android.R.string.ok;
import static android.R.string.yes; import static android.R.string.yes;
import static com.topjohnwu.magisk.R2.string.dling; import static com.topjohnwu.magisk.R.string.dling;
import static com.topjohnwu.magisk.R2.string.no_internet_msg; import static com.topjohnwu.magisk.R.string.no_internet_msg;
import static com.topjohnwu.magisk.R2.string.relaunch_app; import static com.topjohnwu.magisk.R.string.relaunch_app;
import static com.topjohnwu.magisk.R2.string.upgrade_msg; import static com.topjohnwu.magisk.R.string.upgrade_msg;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
...@@ -28,9 +28,6 @@ import org.json.JSONException; ...@@ -28,9 +28,6 @@ import org.json.JSONException;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.CipherInputStream; import javax.crypto.CipherInputStream;
...@@ -140,10 +137,13 @@ public class DownloadActivity extends Activity { ...@@ -140,10 +137,13 @@ public class DownloadActivity extends Activity {
SecretKey key = new SecretKeySpec(Bytes.key(), "AES"); SecretKey key = new SecretKeySpec(Bytes.key(), "AES");
IvParameterSpec iv = new IvParameterSpec(Bytes.iv()); IvParameterSpec iv = new IvParameterSpec(Bytes.iv());
cipher.init(Cipher.DECRYPT_MODE, key, iv); cipher.init(Cipher.DECRYPT_MODE, key, iv);
InputStream is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher); var is = new CipherInputStream(new ByteArrayInputStream(Bytes.res()), cipher);
try (InputStream gzip = new GZIPInputStream(is); var out = new FileOutputStream(apk);
OutputStream out = new FileOutputStream(apk)) { try (is; out) {
APKInstall.transfer(gzip, out); byte[] buf = new byte[4096];
for (int read; (read = is.read(buf)) >= 0;) {
out.write(buf, 0, read);
}
} }
DynAPK.addAssetPath(getResources().getAssets(), apk.getPath()); DynAPK.addAssetPath(getResources().getAssets(), apk.getPath());
} catch (Exception ignored) { } catch (Exception ignored) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment