forked from firka/firka
android: transform aabs
This commit is contained in:
@@ -4,8 +4,8 @@ import java.security.MessageDigest
|
||||
import java.util.Properties
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import java.util.zip.ZipOutputStream.STORED
|
||||
import java.util.zip.ZipOutputStream.DEFLATED
|
||||
import java.util.zip.ZipOutputStream.STORED
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
@@ -105,7 +105,7 @@ flutter {
|
||||
|
||||
tasks.register("transformAndResignDebugApk") {
|
||||
group = "build"
|
||||
description = "Transform and resign debug APK with debug key"
|
||||
description = "Transform and resign APK with debug key"
|
||||
|
||||
dependsOn("assembleDebug")
|
||||
|
||||
@@ -116,7 +116,7 @@ tasks.register("transformAndResignDebugApk") {
|
||||
|
||||
tasks.register("transformAndResignReleaseApk") {
|
||||
group = "build"
|
||||
description = "Transform and resign debug APK with debug key"
|
||||
description = "Transform and resign APK with release key"
|
||||
|
||||
dependsOn("assembleRelease")
|
||||
|
||||
@@ -125,26 +125,34 @@ tasks.register("transformAndResignReleaseApk") {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("transformAndResignReleaseBundle") {
|
||||
group = "build"
|
||||
description = "Transform and resign bundle with release key"
|
||||
|
||||
dependsOn("bundleRelease")
|
||||
|
||||
doLast {
|
||||
transformAppBundle()
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
tasks.findByName("assembleDebug")?.finalizedBy("transformAndResignDebugApk")
|
||||
tasks.findByName("assembleRelease")?.finalizedBy("transformAndResignReleaseApk")
|
||||
tasks.findByName("bundleRelease")?.finalizedBy("transformAndResignReleaseBundle")
|
||||
}
|
||||
|
||||
fun transformApks(debug: Boolean) {
|
||||
val buildDir = project.buildDir
|
||||
val apkDir = File(buildDir, "outputs/flutter-apk")
|
||||
val apks = apkDir.listFiles()!!
|
||||
val flavor = if (debug) { "debug" } else { "release" }
|
||||
|
||||
println("Starting APK transformation process...")
|
||||
|
||||
val buildDir = project.buildDir
|
||||
val apkDir = File(buildDir, "outputs/flutter-apk")
|
||||
val apks = getApks(debug)
|
||||
var c = 0;
|
||||
apks
|
||||
.filter { apk -> apk.name.startsWith("app-") && apk.name.endsWith("-$flavor.apk") }
|
||||
.forEach { c++; transformAndSignApk(apkDir, it.nameWithoutExtension, debug) }
|
||||
|
||||
println("Transformed: $c apks")
|
||||
|
||||
}
|
||||
|
||||
fun transformAndSignApk(apkDir: File, name: String, debug: Boolean) {
|
||||
@@ -201,8 +209,6 @@ fun transformApk(input: File, output: File, compressionLevel: String = "Z") {
|
||||
into(tempDir)
|
||||
}
|
||||
|
||||
val assetsDir = File(tempDir, "assets")
|
||||
|
||||
val metaInf = File(tempDir, "META-INF")
|
||||
val metaInfFiles = metaInf.listFiles();
|
||||
for (file in metaInfFiles!!) {
|
||||
@@ -216,16 +222,12 @@ fun transformApk(input: File, output: File, compressionLevel: String = "Z") {
|
||||
val compressedLibs = mutableMapOf<String, String>()
|
||||
for (arch in arches!!) {
|
||||
val libFlutter = File(arch, "libflutter.so")
|
||||
val libApp = File(arch, "libapp.so")
|
||||
|
||||
if (!libFlutter.exists()) continue
|
||||
|
||||
val compressedDir = File(assetsDir, "flutter-br-${arch.name}")
|
||||
val compressedFlutter = File(compressedDir, "libflutter.so.br")
|
||||
val compressedFlutter = File(arch, "libflutter-br.so")
|
||||
|
||||
if (!compressedDir.exists()) compressedDir.mkdirs()
|
||||
|
||||
compressedLibs["${arch.name}/libflutter.so"] = libFlutter.sha256()
|
||||
compressedLibs["libflutter.so"] = libFlutter.sha256()
|
||||
|
||||
println("Compressing ${arch.name}/libflutter.so with brotli")
|
||||
exec {
|
||||
@@ -237,10 +239,10 @@ fun transformApk(input: File, output: File, compressionLevel: String = "Z") {
|
||||
)
|
||||
}
|
||||
libFlutter.delete()
|
||||
}
|
||||
|
||||
val json = groovy.json.JsonBuilder(compressedLibs)
|
||||
File(assetsDir, "flutter-br.json").writeText(json.toString())
|
||||
val json = groovy.json.JsonBuilder(compressedLibs)
|
||||
File(arch, "index.so").writeText(json.toString())
|
||||
}
|
||||
|
||||
val topDirL = tempDir.absolutePath.length + 1
|
||||
val zos = ZipOutputStream(output.outputStream());
|
||||
@@ -275,12 +277,49 @@ fun transformApk(input: File, output: File, compressionLevel: String = "Z") {
|
||||
println("APK transformed successfully")
|
||||
}
|
||||
|
||||
fun transformAppBundle() {
|
||||
val buildDir = project.buildDir
|
||||
val bundle = File(buildDir, "outputs/bundle/release/app-release.aab")
|
||||
|
||||
val apks = getApks(false)
|
||||
val apkCount = apks.count { it.name.startsWith("app-") && it.name.endsWith("-release.apk") }
|
||||
|
||||
if (!bundle.exists()) {
|
||||
throw Exception("Bundle not found at: $bundle")
|
||||
}
|
||||
|
||||
if (apkCount < 3) {
|
||||
throw Exception("Excepected 3 apks per abi but only found $apkCount")
|
||||
}
|
||||
|
||||
val aabTempDir = File(project.buildDir, "tmp/aab-transform")
|
||||
aabTempDir.deleteRecursively()
|
||||
aabTempDir.mkdirs()
|
||||
|
||||
copy {
|
||||
from(zipTree(bundle))
|
||||
into(aabTempDir)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun File.sha256(): String {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
val digest = md.digest(this.readBytes())
|
||||
return digest.fold("") { str, it -> str + "%02x".format(it) }
|
||||
}
|
||||
|
||||
fun getApks(debug: Boolean): List<File> {
|
||||
val buildDir = project.buildDir
|
||||
val apkDir = File(buildDir, "outputs/flutter-apk")
|
||||
val apks = apkDir.listFiles()!!
|
||||
val flavor = if (debug) { "debug" } else { "release" }
|
||||
|
||||
return apks
|
||||
.filter { apk -> apk.name.startsWith("app-") && apk.name.endsWith("-$flavor.apk") }
|
||||
.toList()
|
||||
}
|
||||
|
||||
fun getDebugKeystorePath(): String {
|
||||
val userHome = System.getProperty("user.home")
|
||||
val debugKeystore = File(userHome, ".android/debug.keystore")
|
||||
|
||||
@@ -5,11 +5,11 @@ import android.app.Application
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import org.brotli.dec.BrotliInputStream
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.security.MessageDigest
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class AppMain : Application() {
|
||||
|
||||
@@ -25,29 +25,54 @@ class AppMain : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
val am = assets
|
||||
val abi = Build.SUPPORTED_ABIS[0]
|
||||
val assets = am.list("")
|
||||
|
||||
if (!assets?.contains("flutter-br-$abi")!!) {
|
||||
throw Exception("Unsupported abi: $abi, try downloading an apk with a different abi")
|
||||
val apks = File(applicationInfo.nativeLibraryDir, "../..").absoluteFile
|
||||
.listFiles()!!
|
||||
.filter { file -> file.name.endsWith(".apk") }
|
||||
.toList()
|
||||
|
||||
var nativesApkN: ZipFile? = null
|
||||
for (apk in apks) {
|
||||
if (nativesApkN != null) break
|
||||
|
||||
val zip = ZipFile(apk)
|
||||
val entries = zip.entries()
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
val entry = entries.nextElement()
|
||||
|
||||
entry.name.endsWith("$abi/index.so")
|
||||
zip.close()
|
||||
nativesApkN = ZipFile(apk)
|
||||
break
|
||||
}
|
||||
|
||||
zip.close()
|
||||
}
|
||||
|
||||
val compressedLibsIndex = am.open("flutter-br.json")
|
||||
if (nativesApkN == null) {
|
||||
throw Exception("Can't find native libraries")
|
||||
}
|
||||
val nativesApk: ZipFile = nativesApkN
|
||||
|
||||
val compressedLibsIndex = nativesApk.getInputStream(
|
||||
nativesApk.getEntry("lib/$abi/index.so")
|
||||
)
|
||||
val compressedLibs = JSONObject(compressedLibsIndex.readBytes().toString(Charsets.UTF_8))
|
||||
|
||||
val natives = am.list("flutter-br-$abi")!!
|
||||
for (lib in natives) {
|
||||
val so = lib.substring(0, lib.length-".br".length)
|
||||
for (so in compressedLibs.keys()) {
|
||||
val soFile = File(cacheDir, so)
|
||||
|
||||
if (soFile.sha256() == compressedLibs.getString("${abi}/$so")) {
|
||||
if (soFile.sha256() == compressedLibs.getString(so)) {
|
||||
System.load(soFile.absolutePath)
|
||||
return
|
||||
}
|
||||
|
||||
Log.d("AppMain", "Decompressing: $so")
|
||||
val brInput = am.open("flutter-br-$abi/$lib")
|
||||
val brInput = nativesApk.getInputStream(
|
||||
nativesApk.getEntry("lib/$abi/${so.replace(".so", "-br.so")}")
|
||||
)
|
||||
val soOutput = FileOutputStream(soFile)
|
||||
|
||||
val brIn = BrotliInputStream(brInput)
|
||||
|
||||
Reference in New Issue
Block a user