diff --git a/bbootimg/src/main/kotlin/Helper.kt b/bbootimg/src/main/kotlin/Helper.kt index ffe5844..60c190e 100644 --- a/bbootimg/src/main/kotlin/Helper.kt +++ b/bbootimg/src/main/kotlin/Helper.kt @@ -23,7 +23,6 @@ import java.util.zip.GZIPOutputStream import javax.crypto.Cipher class Helper { - @ExperimentalStdlibApi @ExperimentalUnsignedTypes companion object { fun joinWithNulls(vararg source: ByteArray?): ByteArray { @@ -311,14 +310,28 @@ class Helper { ret = true } catch (e: java.lang.IllegalArgumentException) { log.error("$e: can not parse command: [$this]") + throw e } catch (e: ExecuteException) { log.error("$e: can not exec command") + throw e } catch (e: IOException) { log.error("$e: can not exec command") + throw e } return ret } + fun String.check_output(): String { + val outputStream = ByteArrayOutputStream() + log.info(this) + DefaultExecutor().let { + it.streamHandler = PumpStreamHandler(outputStream) + it.execute(CommandLine.parse(this)) + } + log.info(outputStream.toString()) + return outputStream.toString().trim() + } + private val log = LoggerFactory.getLogger("Helper") } } diff --git a/bbootimg/src/main/kotlin/cfig/io/Struct3.kt b/bbootimg/src/main/kotlin/cfig/io/Struct3.kt index 509ef00..71dd6da 100644 --- a/bbootimg/src/main/kotlin/cfig/io/Struct3.kt +++ b/bbootimg/src/main/kotlin/cfig/io/Struct3.kt @@ -10,7 +10,6 @@ import java.nio.ByteOrder import java.util.* import java.util.regex.Pattern -@ExperimentalStdlibApi @ExperimentalUnsignedTypes class Struct3 { private val log = LoggerFactory.getLogger(Struct3::class.java) @@ -411,7 +410,7 @@ class Struct3 { if (format[0] === String) { val data = ByteArray(format[1] as Int) Assert.assertEquals(format[1] as Int, iS.read(data)) - ret.add(data.decodeToString()) + ret.add(Helper.toCString(data)) continue } diff --git a/bbootimg/src/main/kotlin/packable/BootImgParser.kt b/bbootimg/src/main/kotlin/packable/BootImgParser.kt index 3373099..7e211d0 100644 --- a/bbootimg/src/main/kotlin/packable/BootImgParser.kt +++ b/bbootimg/src/main/kotlin/packable/BootImgParser.kt @@ -9,10 +9,6 @@ import java.lang.IllegalArgumentException @ExperimentalUnsignedTypes class BootImgParser : IPackable { - override fun flash(fileName: String) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - private val log = LoggerFactory.getLogger(BootImgParser::class.java) override fun capabilities(): List { @@ -53,4 +49,9 @@ class BootImgParser : IPackable { Packer().pack(mkbootfsBin = "./aosp/mkbootfs/build/install/main/release/$osSuffix/mkbootfs") Signer.sign(avbtool = "avb/avbtool", bootSigner = "aosp/boot_signer/build/libs/boot_signer.jar") } + + override fun flash(fileName: String, deviceName: String) { + val stem = fileName.substring(0, fileName.indexOf(".")) + super.flash("$fileName.signed", stem) + } } diff --git a/bbootimg/src/main/kotlin/packable/DtboParser.kt b/bbootimg/src/main/kotlin/packable/DtboParser.kt index 6b19c31..6ade4e0 100644 --- a/bbootimg/src/main/kotlin/packable/DtboParser.kt +++ b/bbootimg/src/main/kotlin/packable/DtboParser.kt @@ -12,10 +12,6 @@ import java.util.* @ExperimentalUnsignedTypes class DtboParser(val workDir: File) : IPackable { - override fun flash(fileName: String) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - constructor() : this(File(".")) private val log = LoggerFactory.getLogger(DtboParser::class.java) @@ -33,17 +29,7 @@ class DtboParser(val workDir: File) : IPackable { it.addArguments("--dtb $dtbPath") it.addArguments("--output $headerPath") } - - DefaultExecutor().let { - it.workingDirectory = this.workDir - try { - log.info(cmd.toString()) - it.execute(cmd) - } catch (e: org.apache.commons.exec.ExecuteException) { - log.error("can not parse $fileName") - return - } - } + execInDirectory(cmd, this.workDir) val props = Properties() props.load(FileInputStream(File(headerPath))) @@ -75,14 +61,17 @@ class DtboParser(val workDir: File) : IPackable { } it } + execInDirectory(cmd, this.workDir) + } + private fun execInDirectory(cmd: CommandLine, inWorkDir: File) { DefaultExecutor().let { - it.workingDirectory = this.workDir + it.workingDirectory = inWorkDir try { log.info(cmd.toString()) it.execute(cmd) } catch (e: org.apache.commons.exec.ExecuteException) { - log.error("can not parse $fileName") + log.error("can not exec command") return } } diff --git a/bbootimg/src/main/kotlin/packable/IPackable.kt b/bbootimg/src/main/kotlin/packable/IPackable.kt index acb89f3..2411b75 100644 --- a/bbootimg/src/main/kotlin/packable/IPackable.kt +++ b/bbootimg/src/main/kotlin/packable/IPackable.kt @@ -1,12 +1,33 @@ package cfig.packable +import cfig.Helper.Companion.check_call +import cfig.Helper.Companion.check_output +import org.slf4j.Logger +import org.slf4j.LoggerFactory + interface IPackable { fun capabilities(): List { return listOf("^dtbo\\.img$") } + fun unpack(fileName: String = "dtbo.img") fun pack(fileName: String = "dtbo.img") - fun flash(fileName: String = "dtbo.img") { + fun flash(fileName: String = "dtbo.img", deviceName: String = "dtbo") { + "adb root".check_call() + val abUpdateProp = "adb shell getprop ro.build.ab_update".check_output() + log.info("ro.build.ab_update=$abUpdateProp") + val slotSuffix = if (abUpdateProp == "true") { + "adb shell getprop ro.boot.slot_suffix".check_output() + } else { + "" + } + log.info("slot suffix = $slotSuffix") + "adb push $fileName /cache/file.to.burn".check_call() + "adb shell dd if=/cache/file.to.burn of=/dev/block/by-name/$deviceName$slotSuffix".check_call() + "adb shell rm /cache/file.to.burn".check_call() + } + 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 83982ad..ac6f09b 100644 --- a/bbootimg/src/main/kotlin/packable/PackableLauncher.kt +++ b/bbootimg/src/main/kotlin/packable/PackableLauncher.kt @@ -28,7 +28,7 @@ fun main(args: Array) { packablePool.forEach { p -> for (item in p.key) { if (Pattern.compile(item).matcher(file.name).matches()) { - log.debug("Found: " + file.name + ", " + item) + log.debug("Found: " + file.name + ", " + item) targetFile = file.name targetHandler = p.value return@found @@ -49,6 +49,9 @@ fun main(args: Array) { "pack" -> { targetHandler!!.createInstance().pack(targetFile!!) } + "flash" -> { + targetHandler!!.createInstance().flash(targetFile!!) + } else -> { log.error("Unknown cmd: " + args[0]) } diff --git a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt index 370805b..853349d 100644 --- a/bbootimg/src/main/kotlin/packable/VBMetaParser.kt +++ b/bbootimg/src/main/kotlin/packable/VBMetaParser.kt @@ -4,10 +4,6 @@ import cfig.Avb @ExperimentalUnsignedTypes class VBMetaParser: IPackable { - override fun flash(fileName: String) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - override fun capabilities(): List { return listOf("^vbmeta\\.img$", "^vbmeta\\_[a-z]+.img$") } @@ -19,4 +15,9 @@ class VBMetaParser: IPackable { override fun pack(fileName: String) { Avb().packVbMetaWithPadding(fileName) } + + override fun flash(fileName: String, deviceName: String) { + val stem = fileName.substring(0, fileName.indexOf(".")) + super.flash("$fileName.signed", stem) + } } diff --git a/bbootimg/src/main/kotlin/sparse_util/SparseImg.kt b/bbootimg/src/main/kotlin/sparse_util/SparseImg.kt index f443645..7517cbb 100644 --- a/bbootimg/src/main/kotlin/sparse_util/SparseImg.kt +++ b/bbootimg/src/main/kotlin/sparse_util/SparseImg.kt @@ -44,4 +44,8 @@ class SparseImg : IPackable { "file $sparseOut".check_call() log.info("transformed Android sparse image: $flatIn -> $sparseOut") } + + override fun flash(fileName: String, deviceName: String) { + TODO("not implemented") + } } diff --git a/build.gradle b/build.gradle deleted file mode 100644 index f40114e..0000000 --- a/build.gradle +++ /dev/null @@ -1,167 +0,0 @@ -apply plugin: "java" - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath "org.apache.commons:commons-exec:1.3" - } -} - -subprojects { - tasks.withType(JavaCompile) { - //options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } -} - -// ---------------------------------------------------------------------------- -// global -// ---------------------------------------------------------------------------- -import java.util.regex.Matcher -import java.util.regex.Pattern -import org.apache.commons.exec.CommandLine -import org.apache.commons.exec.DefaultExecutor -import org.apache.commons.exec.PumpStreamHandler - -if (parseGradleVersion(gradle.gradleVersion) < 5) { - logger.error("ERROR: Gradle Version MUST >= 5.0, current is {}", gradle.gradleVersion) - throw new RuntimeException("ERROR: Gradle Version") -} else { - logger.info("Gradle Version {}", gradle.gradleVersion) -} - -def GROUP_ANDROID = "Android" -project.ext.bootSigner = new File("boot_signer/build/libs/boot_signer.jar").getAbsolutePath() - -// ---------------------------------------------------------------------------- -// tasks -// ---------------------------------------------------------------------------- -task _setup(type: Copy) { - group GROUP_ANDROID - from 'src/test/resources/boot.img' - into '.' -} - -task pull() { - group GROUP_ANDROID - doFirst { - println("Pulling ...") - } - - doLast { - String avb_version = adbCmd("getprop ro.boot.avb_version2") - Boolean isAvbEnabled = false - if (avb_version.isEmpty()) { - isAvbEnabled = true - } - println("AVB: $isAvbEnabled") - if (project.findProperty("group")) { - println("Pull: $group") - } else { - println("Pull /boot, /recovery, /vbmeta") - pullDefault(isAvbEnabled) - } - } -} - -void pullDefault(Boolean avb = true) { - Run(["adb", "shell", "dd if=/dev/block/by-name/boot of=/cache/boot.img"]) - Run(["adb", "shell", "dd if=/dev/block/by-name/recovery of=/cache/recovery.img"]) - if (avb) Run(["adb", "shell", "dd if=/dev/block/by-name/vbmeta of=/cache/vbmeta.img"]) - - Run(["adb", "pull", "/cache/boot.img"]) - Run(["adb", "pull", "/cache/recovery.img"]) - if (avb) Run(["adb", "pull", "/cache/vbmeta.img"]) - - Run(["adb", "shell", "rm /cache/boot.img"]) - Run(["adb", "shell", "rm /cache/recovery.img"]) - if (avb) Run(["adb", "shell", "rm /cache/vbmeta.img"]) -} - -void Run(List inCmd, String inWorkdir = null) { - println("CMD:" + inCmd) - if (inWorkdir == null) { - inWorkdir = "."; - } - ProcessBuilder pb = new ProcessBuilder(inCmd) - .directory(new File(inWorkdir)) - .redirectErrorStream(true); - Process p = pb.start() - p.inputStream.eachLine { println it } - p.waitFor(); - assert 0 == p.exitValue() -} - -void Run(String inCmd, String inWorkdir = null) { - Run(Arrays.asList(inCmd.split()), inWorkdir); -} - -void rebootRecovery() { - Run("adb reboot recovery") -} - -task rr { - group GROUP_ANDROID - doLast { - rebootRecovery() - } -} - -task unpack(type: JavaExec, dependsOn: ["bbootimg:jar"]) { - group GROUP_ANDROID - main = "cfig.packable.PackableLauncherKt" - classpath = files("bbootimg/build/libs/bbootimg.jar") - maxHeapSize '512m' - args "unpack" -} - -task pack(type: JavaExec, dependsOn: ["bbootimg:jar", "aosp:boot_signer:build"]) { - group GROUP_ANDROID - main = "cfig.packable.PackableLauncherKt" - classpath = files("bbootimg/build/libs/bbootimg.jar") - maxHeapSize '512m' - args "pack" -} - -task flash(type: JavaExec, dependsOn: ["bbootimg:jar"]) { - group GROUP_ANDROID - main = "cfig.packable.PackableLauncherKt" - classpath = files("bbootimg/build/libs/bbootimg.jar") - maxHeapSize '512m' - args "flash" -} - -//sparse image dependencies -if (System.getProperty("os.name").contains("Mac")) { - unpack.dependsOn("aosp:libsparse:simg2img:installReleaseMacos") - pack.dependsOn("aosp:libsparse:img2simg:installReleaseMacos") - pack.dependsOn("aosp:mkbootfs:installReleaseMacos") -} else { - unpack.dependsOn("aosp:libsparse:simg2img:installReleaseLinux") - pack.dependsOn("aosp:libsparse:img2simg:installReleaseLinux") - pack.dependsOn("aosp:mkbootfs:installReleaseLinux") -} - -int parseGradleVersion(String version) { - Pattern VERSION_PATTERN = Pattern.compile("((\\d+)(\\.\\d+)+)(-(\\p{Alpha}+)-(\\w+))?(-(SNAPSHOT|\\d{14}([-+]\\d{4})?))?") - Matcher matcher = VERSION_PATTERN.matcher(version) - if (!matcher.matches()) { - throw new IllegalArgumentException(format("'%s' is not a valid Gradle version string (examples: '1.0', '1.0-rc-1')", version)) - } - String versionPart = matcher.group(1) - int majorPart = Integer.parseInt(matcher.group(2), 10) - logger.info("Gradle: versionPart {}, majorPart {}", versionPart, majorPart) - return majorPart -} - -String adbCmd(String cmd) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream() - def exec = new DefaultExecutor() - exec.streamHandler = new PumpStreamHandler(outputStream) - def cmdline = "adb shell $cmd" - //println(cmdline) - exec.execute(CommandLine.parse(cmdline)) - //println(outputStream) - return outputStream.toString().trim() -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..d452ec6 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,74 @@ +import java.util.regex.Matcher +import java.util.regex.Pattern +import org.apache.commons.exec.CommandLine +import org.apache.commons.exec.DefaultExecutor +import org.apache.commons.exec.PumpStreamHandler + +val GROUP_ANDROID = "android" +if (parseGradleVersion(gradle.gradleVersion) < 5) { + logger.error("ERROR: Gradle Version MUST >= 5.0, current is {}", gradle.gradleVersion) + throw RuntimeException("ERROR: Gradle Version") +} else { + logger.info("Gradle Version {}", gradle.gradleVersion) +} + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath("org.apache.commons:commons-exec:1.3") + } +} + +tasks { + val unpackTask by register("unpack") { + group = GROUP_ANDROID + main = "cfig.packable.PackableLauncherKt" + classpath = files("bbootimg/build/libs/bbootimg.jar") + this.maxHeapSize = "512m" + args("unpack") + } + unpackTask.dependsOn("bbootimg:jar") + + val packTask by register("pack") { + group = GROUP_ANDROID + main = "cfig.packable.PackableLauncherKt" + classpath = files("bbootimg/build/libs/bbootimg.jar") + this.maxHeapSize = "512m" + args("pack") + } + packTask.dependsOn("bbootimg:jar", "aosp:boot_signer:build") + + val flashTask by register("flash", JavaExec::class) { + group = GROUP_ANDROID + main = "cfig.packable.PackableLauncherKt" + classpath = files("bbootimg/build/libs/bbootimg.jar") + this.maxHeapSize = "512m" + args("flash") + } + flashTask.dependsOn("bbootimg:jar") + + //sparse image dependencies + if (System.getProperty("os.name").contains("Mac")) { + unpackTask.dependsOn("aosp:libsparse:simg2img:installReleaseMacos") + packTask.dependsOn("aosp:libsparse:img2simg:installReleaseMacos") + packTask.dependsOn("aosp:mkbootfs:installReleaseMacos") + } else { + unpackTask.dependsOn("aosp:libsparse:simg2img:installReleaseLinux") + packTask.dependsOn("aosp:libsparse:img2simg:installReleaseLinux") + packTask.dependsOn("aosp:mkbootfs:installReleaseLinux") + } +} + +fun parseGradleVersion(version: String): Int { + val VERSION_PATTERN = Pattern.compile("((\\d+)(\\.\\d+)+)(-(\\p{Alpha}+)-(\\w+))?(-(SNAPSHOT|\\d{14}([-+]\\d{4})?))?") + val matcher = VERSION_PATTERN.matcher(version) + if (!matcher.matches()) { + throw IllegalArgumentException(String.format("'%s' is not a valid Gradle version string (examples: '1.0', '1.0-rc-1')", version)) + } + val versionPart: String = matcher.group(1) + val majorPart = Integer.parseInt(matcher.group(2), 10) + logger.info("Gradle: versionPart {}, majorPart {}", versionPart, majorPart) + return majorPart +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index a1fdb7d..0000000 --- a/settings.gradle +++ /dev/null @@ -1,9 +0,0 @@ -include "bbootimg" -include "aosp:boot_signer" -include "aosp:mkbootfs" -include "aosp:libsparse:base" -include "aosp:libsparse:sparse" -include "aosp:libsparse:img2simg" -include "aosp:libsparse:simg2img" -include "aosp:libsparse:simg2simg" -include "aosp:libsparse:append2simg" diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..c1c33fb --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,9 @@ +include("bbootimg") +include("aosp:boot_signer") +include("aosp:mkbootfs") +include("aosp:libsparse:base") +include("aosp:libsparse:sparse") +include("aosp:libsparse:img2simg") +include("aosp:libsparse:simg2img") +include("aosp:libsparse:simg2simg") +include("aosp:libsparse:append2simg")