diff --git a/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt b/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt index e0efdd5..3f9af00 100644 --- a/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt +++ b/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt @@ -3,6 +3,9 @@ package cfig.bootloader_message import cfig.io.Struct3 import org.junit.Assert import org.slf4j.LoggerFactory +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream import java.lang.IllegalStateException data class BootloaderMsg( @@ -14,17 +17,55 @@ data class BootloaderMsg( ) { companion object { private const val FORMAT_STRING = "32s32s768s32s1184b" + private const val miscFile = "misc.file" const val SIZE = 2048 private val log = LoggerFactory.getLogger("BootloaderMsg") init { Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize()) } + } + + constructor(fis: FileInputStream) : this() { + val info = Struct3(FORMAT_STRING).unpack(fis) + this.command = info[0] as String + this.status = info[1] as String + this.recovery = info[2] as String + this.stage = info[3] as String + this.reserved = info[4] as ByteArray + } - fun writeBootloaderMessage(options: Array) { - val boot = BootloaderMsg() - boot.updateBootloaderMessageInStruct(options) - boot.writeBootloaderMessage() + fun encode(): ByteArray { + return Struct3(FORMAT_STRING).pack( + this.command, + this.stage, + this.recovery, + this.stage, + byteArrayOf()) + } + + fun clearBootloaderMessage() { + val boot = BootloaderMsg() + boot.writeBootloaderMessage() + } + + fun writeBootloaderMessage(options: Array) { + this.updateBootloaderMessageInStruct(options) + this.writeBootloaderMessage() + } + + fun readBootloaderMsg() { + if (File(miscFile).exists()) { + log.info("readBootloaderMsg() from $miscFile") + val fis = FileInputStream(miscFile) + val info = Struct3(FORMAT_STRING).unpack(fis) + this.command = info[0] as String + this.status = info[1] as String + this.recovery = info[2] as String + this.stage = info[3] as String + this.reserved = info[4] as ByteArray + } else { + log.info("$miscFile missing") } } @@ -38,11 +79,17 @@ data class BootloaderMsg( fun writeBootloaderMessage() { log.info("writing ... $this") + if (!File(miscFile).exists()) { + File(miscFile).createNewFile() + } + FileOutputStream(miscFile, false).use { fos -> + fos.write(this.encode()) + } } fun updateBootloaderMessageInStruct(options: Array) { this.command = "boot-recovery" - this.recovery = "recovery" + this.recovery = "recovery\n" options.forEach { this.recovery += if (it.endsWith("\n")) { it diff --git a/bbootimg/src/main/kotlin/init/Reboot.kt b/bbootimg/src/main/kotlin/init/Reboot.kt index eac2535..2a493bd 100644 --- a/bbootimg/src/main/kotlin/init/Reboot.kt +++ b/bbootimg/src/main/kotlin/init/Reboot.kt @@ -78,7 +78,7 @@ class Reboot { BootloaderMsg().writeRebootBootloader() } else { log.info("$dynamicPartitionKey=true, using fastbootd") - BootloaderMsg.writeBootloaderMessage(arrayOf("--fastboot")) + BootloaderMsg().writeBootloaderMessage(arrayOf("--fastboot")) rebootTarget = "recovery" } } @@ -86,7 +86,7 @@ class Reboot { BootloaderMsg().writeRebootBootloader() } "sideload", "sideload-auto-reboot" -> { - BootloaderMsg.writeBootloaderMessage( + BootloaderMsg().writeBootloaderMessage( arrayOf("--" + rebootTarget.replace("-", "_"))) rebootTarget = "recovery" } diff --git a/bbootimg/src/test/kotlin/bootloader_message/BootloaderMsgTest.kt b/bbootimg/src/test/kotlin/bootloader_message/BootloaderMsgTest.kt new file mode 100644 index 0000000..20c93db --- /dev/null +++ b/bbootimg/src/test/kotlin/bootloader_message/BootloaderMsgTest.kt @@ -0,0 +1,56 @@ +package bootloader_message + +import cfig.bootloader_message.BootloaderMsg +import org.junit.Test + +import org.junit.Assert.* +import org.slf4j.LoggerFactory + +class BootloaderMsgTest { + private val log = LoggerFactory.getLogger(BootloaderMsgTest::class.java) + + @Test + fun writeRebootBootloaderTest() { + val msg = BootloaderMsg() + msg.clearBootloaderMessage() + } + + @Test + fun readBootloaderMsgTest() { + val msg = BootloaderMsg() + msg.readBootloaderMsg() + log.info(msg.toString()) + } + + @Test + fun writeOptions() { + val msg = BootloaderMsg() + msg.updateBootloaderMessageInStruct(arrayOf( + "--prompt_and_wipe_data", + "--locale=zh_CN")) + msg.writeBootloaderMessage() + } + + + @Test + fun rebootRecovery() { + val msg = BootloaderMsg() + msg.updateBootloaderMessageInStruct(arrayOf()) + msg.writeBootloaderMessage() + } + + @Test + fun rebootCrash() { + val msg = BootloaderMsg() + msg.writeBootloaderMessage(arrayOf( + "--prompt_and_wipe_data", + "--reason=RescueParty", + "--locale=en_US")) + } + + @Test + fun rebootOTA() { + val msg = BootloaderMsg() + msg.writeBootloaderMessage(arrayOf("--update_package=/cache/update.zip", "--security")) + } +} diff --git a/build.gradle b/build.gradle index 5d21e86..495c83e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,13 @@ -apply plugin: 'java' +apply plugin: "java" + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath "org.apache.commons:commons-exec:1.3" + } +} subprojects { tasks.withType(JavaCompile) { @@ -11,6 +20,10 @@ subprojects { // ---------------------------------------------------------------------------- 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") @@ -93,27 +106,33 @@ task pull() { } 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() + pullDefault(isAvbEnabled) } } } -void pullDefault() { +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"]) - Run(["adb", "shell", "dd if=/dev/block/by-name/vbmeta of=/cache/vbmeta.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"]) - Run(["adb", "pull", "/cache/vbmeta.img"]) + if (avb) Run(["adb", "pull", "/cache/vbmeta.img"]) Run(["adb", "shell", "rm /cache/boot.img"]) Run(["adb", "shell", "rm /cache/recovery.img"]) - Run(["adb", "shell", "rm /cache/vbmeta.img"]) + if (avb) Run(["adb", "shell", "rm /cache/vbmeta.img"]) } void Run(List inCmd, String inWorkdir = null) { @@ -177,7 +196,7 @@ task rr { } int parseGradleVersion(String version) { - Pattern VERSION_PATTERN = Pattern.compile("((\\d+)(\\.\\d+)+)(-(\\p{Alpha}+)-(\\w+))?(-(SNAPSHOT|\\d{14}([-+]\\d{4})?))?") + 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)) @@ -185,5 +204,16 @@ int parseGradleVersion(String version) { String versionPart = matcher.group(1) int majorPart = Integer.parseInt(matcher.group(2), 10) logger.info("Gradle: versionPart {}, majorPart {}", versionPart, majorPart) - return 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() }