mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-27 12:05:30 +00:00
Use AGP to compile resources
This commit is contained in:
parent
baa19f0ccf
commit
df191cd2b5
@ -39,11 +39,6 @@ android {
|
|||||||
dataBinding = true
|
dataBinding = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dependenciesInfo {
|
|
||||||
includeInApk = false
|
|
||||||
includeInBundle = false
|
|
||||||
}
|
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
resources {
|
resources {
|
||||||
excludes += "/META-INF/*"
|
excludes += "/META-INF/*"
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
|
|
||||||
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.android(configure: Action<BaseExtension>) =
|
private fun Project.androidBase(configure: Action<BaseExtension>) =
|
||||||
extensions.configure("android", configure)
|
extensions.configure("android", configure)
|
||||||
|
|
||||||
private val Project.android: BaseAppModuleExtension get() =
|
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
|
||||||
extensions.getByName("android") as BaseAppModuleExtension
|
extensions.configure("android", configure)
|
||||||
|
|
||||||
|
private val Project.android: 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() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lintOptions {
|
lint {
|
||||||
disable += "MissingTranslation"
|
disable += "MissingTranslation"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependenciesInfo {
|
||||||
|
includeInApk = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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() {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
val compileTmp = File.createTempFile("tmp", ".zip")
|
|
||||||
val linkTmp = File.createTempFile("tmp", ".zip")
|
|
||||||
val optTmp = File.createTempFile("tmp", ".zip")
|
|
||||||
val stubXml = File.createTempFile("tmp", ".xml")
|
|
||||||
try {
|
|
||||||
PrintStream(stubXml).use {
|
|
||||||
it.println("<manifest package=\"com.topjohnwu.magisk\"/>")
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
mergeResourcesProvider.get().dependsOn(genManifestTask)
|
||||||
|
|
||||||
|
val genSrcTask = tasks.register("generate${variantCapped}ObfuscatedSources") {
|
||||||
|
dependsOn(":stub:process${variantCapped}Resources")
|
||||||
|
doLast {
|
||||||
exec {
|
exec {
|
||||||
commandLine(aapt, "compile",
|
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||||
"-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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
3
stub/.gitignore
vendored
3
stub/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
/src/main/AndroidManifest.xml
|
/src/release/AndroidManifest.xml
|
||||||
|
/src/debug/AndroidManifest.xml
|
||||||
|
@ -29,11 +29,6 @@ android {
|
|||||||
proguardFiles("proguard-rules.pro")
|
proguardFiles("proguard-rules.pro")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependenciesInfo {
|
|
||||||
includeInApk = false
|
|
||||||
includeInBundle = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupStub()
|
setupStub()
|
||||||
|
2
stub/src/main/AndroidManifest.xml
Normal file
2
stub/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest 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;
|
|||||||
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 {
|
|||||||
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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user