From 40a31fd655b2f8932ee3d44e83e0d30a1d2ca5d1 Mon Sep 17 00:00:00 2001 From: cfig Date: Sun, 9 Feb 2020 23:27:11 +0800 Subject: [PATCH] generalize command recognization/invocation Now we can directly call "java -jar bbootimg.jar". Also add hidden command '@footer' in BootImgParser --- README.md | 6 +- .../src/main/kotlin/packable/BootImgParser.kt | 24 +++- .../src/main/kotlin/packable/DtboParser.kt | 1 + .../src/main/kotlin/packable/IPackable.kt | 7 ++ .../main/kotlin/packable/PackableLauncher.kt | 112 +++++++++++------- .../src/main/kotlin/packable/VBMetaParser.kt | 7 ++ .../{SparseImg.kt => SparseImgParser.kt} | 5 +- integrationTest.py | 2 + 8 files changed, 114 insertions(+), 50 deletions(-) rename bbootimg/src/main/kotlin/sparse_util/{SparseImg.kt => SparseImgParser.kt} (93%) diff --git a/README.md b/README.md index ff36f9b..f04d34a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Supported images: - recovery.img (also recovery-two-step.img) - vbmeta.img (also vbmeta\_system.img, vbmeta\_vendor.img etc.) - dtbo.img (only 'unpack' is supported) + - sparse images (system.img, vendor.img ...) (2) These utilities are known to work for Nexus/Pixel boot.img for the following Android releases: @@ -85,7 +86,7 @@ We now support both VB 1.0 and AVB 2.0 layouts. | Pixel 3 (blueline) | Google | Y | Q preview (qpp2.190228.023,
2019)| [more ...](doc/additional_tricks.md#pixel-3-blueline) | | Pixel XL (marlin) | HTC | Y | 9.0.0 (PPR2.180905.006,
Sep 2018)| [more ...](doc/additional_tricks.md#pixel-xl-marlin) | | K3 (CPH1955) | OPPO | Y for recovery.img
N for boot.img | Pie | [more](doc/additional_tricks.md#k3-cph1955) | -| Z18(NX606J) | ZTE | Y | 8.1.0 | [more...](doc/additional_tricks.md#nx606j) | +| Z18 (NX606J) | ZTE | Y | 8.1.0 | [more...](doc/additional_tricks.md#nx606j) | | Nexus 9 (volantis/flounder) | HTC | Y(with some tricks) | 7.1.1 (N9F27M, Oct 2017) | [tricks](doc/additional_tricks.md#tricks-for-nexus-9volantis)| | Nexus 5x (bullhead) | LG | Y | 6.0.0_r12 (MDA89E) | | | Moto X (2013) T-Mobile | Motorola | N | | | @@ -116,3 +117,6 @@ https://android.googlesource.com/platform/system/libufdt/ libsparse https://android.googlesource.com/platform/system/core/+/refs/heads/master/libsparse/ + +Android Nexus/Pixle factory images +https://developers.google.cn/android/images diff --git a/bbootimg/src/main/kotlin/packable/BootImgParser.kt b/bbootimg/src/main/kotlin/packable/BootImgParser.kt index 5f7fd53..e590713 100644 --- a/bbootimg/src/main/kotlin/packable/BootImgParser.kt +++ b/bbootimg/src/main/kotlin/packable/BootImgParser.kt @@ -1,12 +1,14 @@ package cfig.packable import avb.AVBInfo +import avb.blob.Footer import cfig.* import cfig.bootimg.BootImgInfo import com.fasterxml.jackson.databind.ObjectMapper import de.vandermeer.asciitable.AsciiTable import org.slf4j.LoggerFactory import java.io.File +import java.io.FileInputStream import java.lang.IllegalArgumentException @ExperimentalUnsignedTypes @@ -20,18 +22,17 @@ class BootImgParser() : IPackable { } private fun unpackVBMeta(): Boolean { - if (File("vbmeta.img").exists()) { + return if (File("vbmeta.img").exists()) { log.warn("Found vbmeta.img, parsing ...") VBMetaParser().unpack("vbmeta.img") - return true + true } else { - return false + false } } override fun unpack(fileName: String) { - if (File(UnifiedConfig.workDir).exists()) File(UnifiedConfig.workDir).deleteRecursively() - File(UnifiedConfig.workDir).mkdirs() + cleanUp() try { val info = Parser().parseBootImgHeader(fileName, avbtool = "aosp/avb/avbtool") InfoTable.instance.addRule() @@ -98,4 +99,17 @@ class BootImgParser() : IPackable { val stem = fileName.substring(0, fileName.indexOf(".")) super.flash("$fileName.signed", stem) } + + // invoked solely by reflection + fun `@footer`(image_file: String) { + FileInputStream(image_file).use { fis -> + fis.skip(File(image_file).length() - Footer.SIZE) + try { + val footer = Footer(fis) + log.info("\n" + ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(footer)) + } catch (e: IllegalArgumentException) { + log.info("image $image_file has no AVB Footer") + } + } + } } diff --git a/bbootimg/src/main/kotlin/packable/DtboParser.kt b/bbootimg/src/main/kotlin/packable/DtboParser.kt index c078aab..ed20113 100644 --- a/bbootimg/src/main/kotlin/packable/DtboParser.kt +++ b/bbootimg/src/main/kotlin/packable/DtboParser.kt @@ -25,6 +25,7 @@ class DtboParser(val workDir: File) : IPackable { } override fun unpack(fileName: String) { + cleanUp() val outputDir = UnifiedConfig.workDir val dtbPath = File("$outputDir/dtb").path!! val headerPath = File("$outputDir/dtbo.header").path!! diff --git a/bbootimg/src/main/kotlin/packable/IPackable.kt b/bbootimg/src/main/kotlin/packable/IPackable.kt index 2bfde8e..49986ca 100644 --- a/bbootimg/src/main/kotlin/packable/IPackable.kt +++ b/bbootimg/src/main/kotlin/packable/IPackable.kt @@ -2,8 +2,10 @@ package cfig.packable import cfig.Helper.Companion.check_call import cfig.Helper.Companion.check_output +import cfig.UnifiedConfig import org.slf4j.Logger import org.slf4j.LoggerFactory +import java.io.File @ExperimentalUnsignedTypes interface IPackable { @@ -29,6 +31,11 @@ interface IPackable { "adb shell rm /cache/file.to.burn".check_call() } + fun cleanUp() { + if (File(UnifiedConfig.workDir).exists()) File(UnifiedConfig.workDir).deleteRecursively() + File(UnifiedConfig.workDir).mkdirs() + } + companion object { val log: Logger = LoggerFactory.getLogger(IPackable::class.java) } diff --git a/bbootimg/src/main/kotlin/packable/PackableLauncher.kt b/bbootimg/src/main/kotlin/packable/PackableLauncher.kt index 0bbf7c8..5889cdc 100644 --- a/bbootimg/src/main/kotlin/packable/PackableLauncher.kt +++ b/bbootimg/src/main/kotlin/packable/PackableLauncher.kt @@ -1,12 +1,13 @@ package cfig.packable -import cfig.UnifiedConfig -import cfig.sparse_util.SparseImg +import cfig.sparse_util.SparseImgParser import org.slf4j.LoggerFactory import java.io.File import java.util.regex.Pattern import kotlin.reflect.KClass import kotlin.reflect.full.createInstance +import kotlin.reflect.full.declaredFunctions +import kotlin.system.exitProcess class PackableLauncher @@ -14,7 +15,7 @@ class PackableLauncher fun main(args: Array) { val log = LoggerFactory.getLogger(PackableLauncher::class.java) val packablePool = mutableMapOf, KClass>() - listOf(DtboParser(), VBMetaParser(), BootImgParser(), SparseImg()).forEach { + listOf(DtboParser(), VBMetaParser(), BootImgParser(), SparseImgParser()).forEach { @Suppress("UNCHECKED_CAST") packablePool.put(it.capabilities(), it::class as KClass) } @@ -24,56 +25,83 @@ fun main(args: Array) { var targetFile: String? = null var targetHandler: KClass? = null run found@{ - File(".").listFiles().forEach { file -> - packablePool - .filter { it.value.createInstance().loopNo == 0 } - .forEach { p -> - for (item in p.key) { - if (Pattern.compile(item).matcher(file.name).matches()) { - log.debug("Found: " + file.name + ", " + item) - targetFile = file.name - targetHandler = p.value - return@found + for (currentLoopNo in 0..1) { //currently we have only 2 loops + File(".").listFiles()!!.forEach { file -> + packablePool + .filter { it.value.createInstance().loopNo == currentLoopNo } + .forEach { p -> + for (item in p.key) { + if (Pattern.compile(item).matcher(file.name).matches()) { + log.debug("Found: " + file.name + ", " + item) + targetFile = file.name + targetHandler = p.value + return@found + } } } - } - } + }//end-of-file-traversing + }//end-of-range-loop + }//end-of-found@ - File(".").listFiles().forEach { file -> - packablePool - .filter { it.value.createInstance().loopNo != 0 } - .forEach { p -> - for (item in p.key) { - if (Pattern.compile(item).matcher(file.name).matches()) { - log.debug("Found: " + file.name + ", " + item) - targetFile = file.name - targetHandler = p.value - return@found - } - } - } + // /* 1 */ no-args & no-handler: help for IPackable + // /* 2 */ no-args & handler : help for Handler + // /* 3 */ args & no-handler: do nothing + // /* 4 */ args & handler : work + when (listOf(args.isEmpty(), targetHandler == null)) { + listOf(true, true) -> { /* 1 */ + log.info("help:") + log.info("available IPackable subcommands are:") + IPackable::class.declaredFunctions.forEach { + log.info("\t" + it.name) + } + exitProcess(1) + } + listOf(true, false) -> {/* 2 */ + log.info("available ${targetHandler!!.simpleName} subcommands are:") + targetHandler!!.declaredFunctions.forEach { + log.info("\t" + it.name) + } + exitProcess(1) + } + listOf(false, true) -> {/* 3 */ + log.warn("No handler is activated, DO NOTHING!") + exitProcess(2) + } + listOf(false, false) -> {/* 4 */ + log.debug("continue ...") } } - if (targetHandler != null) { - log.warn("Active image target: $targetFile") - when (args[0]) { - "unpack" -> { - if (File(UnifiedConfig.workDir).exists()) File(UnifiedConfig.workDir).deleteRecursively() - File(UnifiedConfig.workDir).mkdirs() - targetHandler!!.createInstance().unpack(targetFile!!) + targetHandler?.let { + log.warn("[$targetFile] will be handled by [${it.simpleName}]") + val functions = it.declaredFunctions.filter { funcItem -> funcItem.name == args[0] } + if (functions.size != 1) { + log.error("command '${args[0]}' can not be recognized") + log.info("available ${it.simpleName} subcommands are:") + it.declaredFunctions.forEach { + log.info("\t" + it.name) } - "pack" -> { - targetHandler!!.createInstance().pack(targetFile!!) + exitProcess(3) + } + log.warn("'${args[0]}' sequence initialized") + val reflectRet = when (functions[0].parameters.size) { + 1 -> { + functions[0].call(it.createInstance()) } - "flash" -> { - targetHandler!!.createInstance().flash(targetFile!!) + 2 -> { + functions[0].call(it.createInstance(), targetFile!!) } else -> { - log.error("Unknown cmd: " + args[0]) + functions[0].parameters.forEach { kp -> + println("Param: " + kp.index + " " + kp.type + " " + kp.name) + } + log.error("I am confused by so many parameters") + exitProcess(4) } } - } else { - log.warn("Nothing to do") + if (functions[0].returnType.toString() != Unit.toString()) { + log.info("ret: $reflectRet") + } + log.warn("'${args[0]}' sequence completed") } } diff --git a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt index 8f366ea..cd30aed 100644 --- a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt +++ b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt @@ -1,6 +1,8 @@ package cfig.packable import cfig.Avb +import cfig.UnifiedConfig +import java.io.File @ExperimentalUnsignedTypes class VBMetaParser: IPackable { @@ -11,7 +13,12 @@ class VBMetaParser: IPackable { return listOf("^vbmeta\\.img$", "^vbmeta\\_[a-z]+.img$") } + override fun cleanUp() { + File(UnifiedConfig.workDir).mkdirs() + } + override fun unpack(fileName: String) { + cleanUp() Avb().parseVbMeta(fileName) } diff --git a/bbootimg/src/main/kotlin/sparse_util/SparseImg.kt b/bbootimg/src/main/kotlin/sparse_util/SparseImgParser.kt similarity index 93% rename from bbootimg/src/main/kotlin/sparse_util/SparseImg.kt rename to bbootimg/src/main/kotlin/sparse_util/SparseImgParser.kt index fa8067a..15b5b95 100644 --- a/bbootimg/src/main/kotlin/sparse_util/SparseImg.kt +++ b/bbootimg/src/main/kotlin/sparse_util/SparseImgParser.kt @@ -6,10 +6,10 @@ import org.slf4j.LoggerFactory import cfig.Helper.Companion.check_call @ExperimentalUnsignedTypes -class SparseImg : IPackable { +class SparseImgParser : IPackable { override val loopNo: Int get() = 0 - private val log = LoggerFactory.getLogger(SparseImg::class.java) + private val log = LoggerFactory.getLogger(SparseImgParser::class.java) private val simg2imgBin: String private val img2simgBin: String @@ -24,6 +24,7 @@ class SparseImg : IPackable { } override fun unpack(fileName: String) { + cleanUp() simg2img(fileName, "$fileName.unsparse") } diff --git a/integrationTest.py b/integrationTest.py index 81776ce..be034bf 100755 --- a/integrationTest.py +++ b/integrationTest.py @@ -34,10 +34,12 @@ def cleanUp(): deleteIfExists("boot.img.clear") deleteIfExists("boot.img.google") deleteIfExists("boot.img.signed") + deleteIfExists("boot.img.signed2") deleteIfExists("recovery.img") deleteIfExists("recovery.img.clear") deleteIfExists("recovery.img.google") deleteIfExists("recovery.img.signed") + deleteIfExists("recovery.img.signed2") deleteIfExists("vbmeta.img") deleteIfExists("vbmeta.img.signed")