squashed commit

gradle 8.14
kotlin 2.1.21
updated support of be

Signed-off-by: cfig <yuyezhong@gmail.com>
master
cfig 3 weeks ago
parent 763427af01
commit 7451a29f45
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -15,7 +15,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "2.0.20"
kotlin("jvm") version "2.1.21"
application
}
@ -62,11 +62,13 @@ java {
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType<KotlinCompile>().all {
kotlinOptions {
freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes"
jvmTarget = "11"
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
freeCompilerArgs.addAll(
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalUnsignedTypes"
)
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
}
}

@ -206,7 +206,7 @@ class Avb {
val readBackInfo = ObjectMapper().readValue(File(getJsonFileName(fileName)), AVBInfo::class.java)
val intermediateDir = Helper.joinPath(Helper.prop("workDir")!!, "intermediate")
val newHashDesc = if (File(intermediateDir).exists()) {
AVBInfo.parseFrom(Dumpling(Helper.joinPath(intermediateDir, "$fileName.signed")))
AVBInfo.parseFrom(Dumpling(Helper.joinPath(intermediateDir, File("$fileName.signed").name)))
} else {
//FIXME: before BootV2 supports abe mode
AVBInfo.parseFrom(Dumpling("$fileName.signed"))

@ -20,6 +20,7 @@ import cfig.bootimg.cpio.AndroidCpio
import rom.fdt.DTC
import cfig.helper.Helper
import cfig.helper.ZipHelper
import cfig.packable.BootImgParser
import cfig.utils.KernelExtractor
import com.github.freva.asciitable.HorizontalAlign
import org.apache.commons.exec.CommandLine
@ -32,6 +33,8 @@ import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.lang.NumberFormatException
import java.nio.ByteBuffer
import java.nio.ByteOrder
@ -52,6 +55,12 @@ class Common {
private val log = LoggerFactory.getLogger(Common::class.java)
private const val MAX_ANDROID_VER = 11
val loadProperties: (String) -> Properties = { fileName ->
Properties().apply {
File(fileName).inputStream().use { load(it) }
}
}
@Throws(IllegalArgumentException::class)
fun packOsVersion(x: String?): Int {
if (x.isNullOrBlank()) return 0
@ -423,19 +432,24 @@ class Common {
)
}
fun printPackSummary(imageName: String) {
fun printPackSummary(imageName: String, outFile: String? = null) {
val prints: MutableList<Pair<String, String>> = mutableListOf()
val tableHeader = de.vandermeer.asciitable.AsciiTable().apply {
addRule(); addRow("What", "Where"); addRule()
}
val tab = de.vandermeer.asciitable.AsciiTable().let {
it.addRule()
if (File("$imageName.signed").exists()) {
it.addRow("re-packed $imageName", "$imageName.signed")
prints.add(Pair("re-packed $imageName", "$imageName.signed"))
if (outFile != null) {
it.addRow("re-packed $imageName", outFile)
prints.add(Pair("re-packed $imageName", outFile))
} else {
it.addRow("re-packed $imageName", "$imageName.clear")
prints.add(Pair("re-packed $imageName", "$imageName.clear"))
if (File("$imageName.signed").exists()) {
it.addRow("re-packed $imageName", "$imageName.signed")
prints.add(Pair("re-packed $imageName", "$imageName.signed"))
} else {
it.addRow("re-packed $imageName", "$imageName.clear")
prints.add(Pair("re-packed $imageName", "$imageName.clear"))
}
}
it.addRule()
it
@ -457,6 +471,31 @@ class Common {
}
}
/*
be_caller_dir: set in "be" script, to support out of tree invocation
*/
fun shortenPath(fullPath: String, inCurrentPath: String = System.getProperty("user.dir")): String {
val currentPath = System.getenv("be_caller_dir") ?: inCurrentPath
val full = Paths.get(fullPath).normalize().toAbsolutePath()
val base = Paths.get(currentPath).normalize().toAbsolutePath()
return try {
base.relativize(full).toString()
} catch (e: IllegalArgumentException) {
full.toString()
}
}
fun String.toShortenPath(inCurrentPath: String = System.getProperty("user.dir")): String {
val currentPath = System.getenv("be_caller_dir") ?: inCurrentPath
val full = Paths.get(this).normalize().toAbsolutePath()
val base = Paths.get(currentPath).normalize().toAbsolutePath()
return try {
base.relativize(full).toString()
} catch (e: IllegalArgumentException) {
full.toString()
}
}
fun printPackSummaryInternal(imageName: String) {
val prints: MutableList<Pair<String, String>> = mutableListOf()
val tableHeader = de.vandermeer.asciitable.AsciiTable().apply {
@ -485,5 +524,47 @@ class Common {
log.info("\n\t\t\tPack Summary of ${imageName}\n{}\n{}", tableHeader.render(), tab.render())
}
}
fun createWorkspaceIni(fileName: String, iniFileName: String = "workspace.ini", prefix: String? = null) {
log.trace("create workspace file")
val workDir = Helper.prop("workDir")
val workspaceFile = File(workDir, iniFileName)
try {
if (prefix.isNullOrBlank()) {
// override existing file entirely when prefix is null or empty
val props = Properties().apply {
setProperty("file", fileName)
setProperty("workDir", workDir)
setProperty("role", File(fileName).name)
}
FileOutputStream(workspaceFile).use { out ->
props.store(out, "unpackInternal configuration (overridden)")
}
log.info("workspace file overridden: ${workspaceFile.canonicalPath}")
} else {
// merge into existing (or create new) with prefixed keys
val props = Properties().apply {
if (workspaceFile.exists()) {
FileInputStream(workspaceFile).use { load(it) }
}
}
fun key(name: String) = "${prefix.trim()}.$name"
props.setProperty(key("file"), fileName)
props.setProperty(key("workDir"), workDir)
props.setProperty(key("role"), File(fileName).name)
FileOutputStream(workspaceFile).use { out ->
props.store(out, "unpackInternal configuration (with prefix='$prefix')")
}
log.info("workspace file created/updated with prefix '$prefix': ${workspaceFile.canonicalPath}")
}
} catch (e: IOException) {
log.error("error writing workspace file: ${e.message}")
}
log.trace("create workspace file done")
}
}
}

@ -30,8 +30,58 @@ class Signer {
companion object {
private val log = LoggerFactory.getLogger(Signer::class.java)
fun signAVB2(inFile: String, //"$output.clear"
outFile: String, //"$output.signed"
aiFile: String, //AVBInfo
imageSize: Long,
avbtool: String) {
log.info("Adding hash_footer with verified-boot 2.0 style: $inFile -> $outFile")
val ai = ObjectMapper().readValue(File(aiFile), AVBInfo::class.java)
val alg = Algorithms.get(ai.header!!.algorithm_type)
val bootDesc = ai.auxBlob!!.hashDescriptors[0]
val newAvbInfo = ObjectMapper().readValue(File(aiFile), AVBInfo::class.java)
//our signer
File(inFile).copyTo(File(outFile), overwrite = true)
Avb().addHashFooter(outFile,
imageSize,
partition_name = bootDesc.partition_name,
newAvbInfo = newAvbInfo)
//original signer
val cmdPrefix = if (EnvironmentVerifier().isWindows) "python " else ""
CommandLine.parse("$cmdPrefix$avbtool add_hash_footer").apply {
addArguments("--image ${outFile}2") //boot.img.signed2
addArguments("--flags ${ai.header!!.flags}")
addArguments("--partition_size ${imageSize}")
addArguments("--salt ${Helper.toHexString(bootDesc.salt)}")
addArguments("--partition_name ${bootDesc.partition_name}")
addArguments("--hash_algorithm ${bootDesc.hash_algorithm}")
addArguments("--algorithm ${alg!!.name}")
addArguments("--rollback_index ${ai.header!!.rollback_index}")
if (alg.defaultKey.isNotBlank()) {
addArguments("--key ${alg.defaultKey}")
}
newAvbInfo.auxBlob?.let { newAuxblob ->
newAuxblob.propertyDescriptors.forEach { newProp ->
addArguments(arrayOf("--prop", "${newProp.key}:${newProp.value}"))
}
}
addArgument("--internal_release_string")
addArgument(ai.header!!.release_string, false)
log.info(this.toString())
File(inFile).copyTo(File("${outFile}2"), overwrite = true)
DefaultExecutor().execute(this)
}
Helper.assertFileEquals(outFile, "${outFile}2")
File("${outFile}2").delete()
//TODO: decide what to verify
//Parser.verifyAVBIntegrity(cfg.info.output + ".signed", avbtool)
//Parser.verifyAVBIntegrity(cfg.info.output + ".signed2", avbtool)
}
fun signAVB(output: String, imageSize: Long, avbtool: String) {
log.info("Adding hash_footer with verified-boot 2.0 style")
log.info("Adding hash_footer with verified-boot 2.0 style: $output")
val ai = ObjectMapper().readValue(File(getJsonFileName(output)), AVBInfo::class.java)
val alg = Algorithms.get(ai.header!!.algorithm_type)
val bootDesc = ai.auxBlob!!.hashDescriptors[0]

@ -194,6 +194,7 @@ class AndroidCpio {
val rounded = Helper.round_to_multiple(len, 256) //file in page 256
if (len != rounded) {
FileOutputStream(outFile, true).use { fos ->
log.info("cpio padding size: " + (rounded - len) + " bytes")
fos.write(ByteArray((rounded - len).toInt()))
}
}

@ -16,15 +16,14 @@ package cfig.bootimg.v2
import avb.AVBInfo
import cfig.Avb
import cfig.bootimg.Common as C
import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Companion.shortenPath
import cfig.bootimg.Signer
import cfig.helper.Dumpling
import cfig.helper.Helper
import cfig.helper.ZipHelper
import cfig.packable.VBMetaParser
import rom.fdt.DTC
import cfig.utils.EnvironmentVerifier
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.freva.asciitable.HorizontalAlign
@ -32,11 +31,13 @@ import de.vandermeer.asciitable.AsciiTable
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.slf4j.LoggerFactory
import rom.fdt.DTC
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import cfig.bootimg.Common as C
data class BootV2(
var info: MiscInfo = MiscInfo(),
@ -95,7 +96,9 @@ data class BootV2(
companion object {
private val log = LoggerFactory.getLogger(BootV2::class.java)
private val workDir = Helper.prop("workDir")
private val workDir: () -> String = {
Helper.prop("workDir")!!
}
private val mapper = ObjectMapper()
private val dtsSuffix = Helper.prop("config.dts_suffix")
@ -128,7 +131,7 @@ data class BootV2(
}
}
ret.kernel.let { theKernel ->
theKernel.file = File(workDir, "kernel").path
theKernel.file = File(workDir(), "kernel").path
theKernel.size = bh2.kernelLength
theKernel.loadOffset = bh2.kernelOffset
theKernel.position = ret.getKernelPosition()
@ -138,28 +141,28 @@ data class BootV2(
theRamdisk.loadOffset = bh2.ramdiskOffset
theRamdisk.position = ret.getRamdiskPosition()
if (bh2.ramdiskLength > 0) {
theRamdisk.file = File(workDir, "ramdisk.img").path
theRamdisk.file = File(workDir(), "ramdisk.img").path
}
}
if (bh2.secondBootloaderLength > 0) {
ret.secondBootloader = CommArgs()
ret.secondBootloader!!.size = bh2.secondBootloaderLength
ret.secondBootloader!!.loadOffset = bh2.secondBootloaderOffset
ret.secondBootloader!!.file = File(workDir, "second").path
ret.secondBootloader!!.file = File(workDir(), "second").path
ret.secondBootloader!!.position = ret.getSecondBootloaderPosition()
}
if (bh2.recoveryDtboLength > 0) {
ret.recoveryDtbo = CommArgsLong()
ret.recoveryDtbo!!.size = bh2.recoveryDtboLength
ret.recoveryDtbo!!.loadOffset = bh2.recoveryDtboOffset //Q
ret.recoveryDtbo!!.file = File(workDir, "recoveryDtbo").path
ret.recoveryDtbo!!.file = File(workDir(), "recoveryDtbo").path
ret.recoveryDtbo!!.position = ret.getRecoveryDtboPosition()
}
if (bh2.dtbLength > 0) {
ret.dtb = DtbArgsLong()
ret.dtb!!.size = bh2.dtbLength
ret.dtb!!.loadOffset = bh2.dtbOffset //Q
ret.dtb!!.file = File(workDir, "dtb").path
ret.dtb!!.file = File(workDir(), "dtb").path
ret.dtb!!.position = ret.getDtbPosition()
}
}
@ -206,7 +209,7 @@ data class BootV2(
fun extractImages(): BootV2 {
//info
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir(), info.json), this)
//kernel
if (kernel.size > 0) {
Common.dumpKernel(Helper.Slice(info.output, kernel.position.toInt(), kernel.size, kernel.file!!))
@ -220,15 +223,17 @@ data class BootV2(
//ramdisk
if (this.ramdisk.size > 0) {
val fmt = C.dumpRamdisk(
Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!), File(workDir, "root").path
Helper.Slice(info.output, ramdisk.position.toInt(), ramdisk.size, ramdisk.file!!),
File(workDir(), "root").path
)
this.ramdisk.file = this.ramdisk.file!! + ".$fmt"
if (fmt == "xz") {
val checkType = ZipHelper.xzStreamFlagCheckTypeToString(ZipHelper.parseStreamFlagCheckType(this.ramdisk.file!!))
val checkType =
ZipHelper.xzStreamFlagCheckTypeToString(ZipHelper.parseStreamFlagCheckType(this.ramdisk.file!!))
this.ramdisk.xzFlags = checkType
}
//dump info again
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + this.info.json), this)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir(), this.info.json), this)
}
//second bootloader
secondBootloader?.let {
@ -254,7 +259,7 @@ data class BootV2(
this.dtb!!.dtbList = DTC.parseMultiple(dtb!!.file!!)
log.info("dtb sz = " + this.dtb!!.dtbList.size)
//dump info again
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir + info.json), this)
mapper.writerWithDefaultPrettyPrinter().writeValue(File(workDir(), info.json), this)
//dump dtb items
DTC.extractMultiple(dtb!!.file!!, this.dtb!!.dtbList)
@ -285,8 +290,8 @@ data class BootV2(
}
val tab = AsciiTable().let {
it.addRule()
it.addRow("image info", workDir + info.output.removeSuffix(".img") + ".json")
prints.add(Pair("image info", workDir + info.output.removeSuffix(".img") + ".json"))
it.addRow("image info", shortenPath( File(Helper.prop("workDir"), info.output.removeSuffix(".img") + ".json").path))
prints.add(Pair("image info", shortenPath(File(workDir(), info.output.removeSuffix(".img") + ".json").path)))
if (this.info.verify.startsWith("VB2.0")) {
it.addRule()
val verifyStatus = if (this.info.verify.contains("PASS")) {
@ -295,8 +300,8 @@ data class BootV2(
"verify fail"
}
Avb.getJsonFileName(info.output).let { jsonFile ->
it.addRow("AVB info [$verifyStatus]", jsonFile)
prints.add(Pair("AVB info [$verifyStatus]", jsonFile))
it.addRow("AVB info [$verifyStatus]", shortenPath(jsonFile))
prints.add(Pair("AVB info [$verifyStatus]", shortenPath(jsonFile)))
if (File(jsonFile).exists()) {
mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai ->
val inspectRet = Avb.inspectKey(ai)
@ -311,11 +316,11 @@ data class BootV2(
//kernel
it.addRule()
if (this.kernel.size > 0) {
it.addRow("kernel", this.kernel.file)
it.addRow("kernel", shortenPath(this.kernel.file!!))
} else { //only for ramdisk.img, Issue #122
it.addRow("kernel", "NONE")
}
prints.add(Pair("kernel", this.kernel.file.toString()))
prints.add(Pair("kernel", shortenPath(this.kernel.file.toString())))
File(Helper.prop("kernelVersionFile")).let { kernelVersionFile ->
if (kernelVersionFile.exists()) {
it.addRow("\\-- version " + kernelVersionFile.readLines().toString(), kernelVersionFile.path)
@ -331,10 +336,10 @@ data class BootV2(
//ramdisk
if (this.ramdisk.size > 0) {
it.addRule()
it.addRow("ramdisk", this.ramdisk.file)
prints.add(Pair("ramdisk", this.ramdisk.file.toString()))
it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path)
prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path))
it.addRow("ramdisk", shortenPath(this.ramdisk.file!!))
prints.add(Pair("ramdisk", shortenPath(this.ramdisk.file.toString())))
it.addRow("\\-- extracted ramdisk rootfs", shortenPath(File(workDir(), "root").path))
prints.add(Pair("\\-- extracted ramdisk rootfs", shortenPath(File(workDir(), "root").path)))
}
//second
this.secondBootloader?.let { theSecondBootloader ->
@ -357,11 +362,11 @@ data class BootV2(
if (theDtb.size > 0) {
val dtbCount = this.dtb!!.dtbList.size
it.addRule()
it.addRow("dtb", theDtb.file)
prints.add(Pair("dtb", theDtb.file.toString()))
it.addRow("dtb", theDtb.file?.let { fullPath -> shortenPath(fullPath) })
prints.add(Pair("dtb", shortenPath(theDtb.file.toString())))
if (File(theDtb.file + ".0.${dtsSuffix}").exists()) {
it.addRow("\\-- decompiled dts [$dtbCount]", theDtb.file + ".*.${dtsSuffix}")
prints.add(Pair("\\-- decompiled dts [$dtbCount]", theDtb.file + ".*.${dtsSuffix}"))
it.addRow("\\-- decompiled dts [$dtbCount]", shortenPath(theDtb.file!!) + ".*.${dtsSuffix}")
prints.add(Pair("\\-- decompiled dts [$dtbCount]", shortenPath(theDtb.file!!) + ".*.${dtsSuffix}"))
}
}
}
@ -372,8 +377,8 @@ data class BootV2(
val tabVBMeta = AsciiTable().let {
if (File("vbmeta.img").exists()) {
it.addRule()
it.addRow("vbmeta.img", Avb.getJsonFileName("vbmeta.img"))
prints.add(Pair("vbmeta.img", Avb.getJsonFileName("vbmeta.img")))
it.addRow("vbmeta.img", shortenPath(Avb.getJsonFileName("vbmeta.img")))
prints.add(Pair("vbmeta.img", shortenPath(Avb.getJsonFileName("vbmeta.img"))))
it.addRule()
"\n" + it.render()
} else {
@ -382,15 +387,17 @@ data class BootV2(
}
if (EnvironmentVerifier().isWindows) {
log.info("\n" +
com.github.freva.asciitable.AsciiTable.getTable(
com.github.freva.asciitable.AsciiTable.BASIC_ASCII,
prints, mutableListOf(
com.github.freva.asciitable.Column().header("What")
.dataAlign(HorizontalAlign.LEFT)
.with { it.first },
com.github.freva.asciitable.Column().header("Where").with { it.second })
))
log.info(
"\n" +
com.github.freva.asciitable.AsciiTable.getTable(
com.github.freva.asciitable.AsciiTable.BASIC_ASCII,
prints, mutableListOf(
com.github.freva.asciitable.Column().header("What")
.dataAlign(HorizontalAlign.LEFT)
.with { it.first },
com.github.freva.asciitable.Column().header("Where").with { it.second })
)
)
} else {
log.info(
"\n\t\t\tUnpack Summary of ${info.output}\n{}\n{}{}",
@ -433,14 +440,14 @@ data class BootV2(
ramdisk.file = null
ramdisk.loadOffset = 0
} else {
if (File(this.ramdisk.file!!).exists() && !File(workDir + "root").exists()) {
if (File(this.ramdisk.file!!).exists() && !File(workDir(), "root").exists()) {
//do nothing if we have ramdisk.img.gz but no /root
log.warn("Use prebuilt ramdisk file: ${this.ramdisk.file}")
} else {
File(this.ramdisk.file!!).deleleIfExists()
File(this.ramdisk.file!!.removeSuffix(".gz")).deleleIfExists()
//Common.packRootfs("${workDir}/root", this.ramdisk.file!!, Common.parseOsMajor(info.osVersion.toString()))
Common.packRootfs(File(workDir, "root").path, this.ramdisk.file!!, this.ramdisk.xzFlags)
Common.packRootfs(File(workDir(), "root").path, this.ramdisk.file!!, this.ramdisk.xzFlags)
}
this.ramdisk.size = File(this.ramdisk.file!!).length().toInt()
}
@ -466,18 +473,21 @@ data class BootV2(
0 -> {
Common.hashFileAndSize(kernel.file, ramdisk.file, secondBootloader?.file)
}
1 -> {
Common.hashFileAndSize(
kernel.file, ramdisk.file,
secondBootloader?.file, recoveryDtbo?.file
)
}
2 -> {
Common.hashFileAndSize(
kernel.file, ramdisk.file,
secondBootloader?.file, recoveryDtbo?.file, dtb?.file
)
}
else -> {
throw IllegalArgumentException("headerVersion ${info.headerVersion} illegal")
}

@ -22,6 +22,7 @@ import cfig.bootimg.Common
import cfig.utils.EnvironmentVerifier
import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Companion.getPaddingSize
import cfig.bootimg.Common.Companion.shortenPath
import cfig.bootimg.Signer
import cfig.helper.Helper
import cfig.helper.Dumpling
@ -228,22 +229,26 @@ data class BootV3(
}
if (fileName != info.role) {
Helper.setProp("out.file", fileName)
if (bSigningNeeded) {
log.info("x1")
Helper.setProp("out.file", "$fileName.signed")
//@formatter:off
File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".signed"))
.copyTo(File(fileName), true)
.copyTo(File(Helper.prop("out.file")!!), true)
//@formatter:on
log.info("Signed image saved as $fileName")
log.info("Signed image saved as " + Helper.prop("out.file"))
} else {
log.info("x2")
Helper.setProp("out.file", fileName)
//@formatter:off
File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".clear"))
.copyTo(File(fileName), true)
.copyTo(File(Helper.prop("out.file")!!), true)
//@formatter:on
log.info("Unsigned image saved as $fileName")
log.info("Unsigned image saved as " + Helper.prop("out.file"))
}
} else {
if (bSigningNeeded) {
log.info("x3")
Helper.setProp("out.file", info.role + ".signed")
//@formatter:off
File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".signed"))
@ -251,6 +256,7 @@ data class BootV3(
//@formatter:on
log.info("Signed image saved as ${info.role}.signed")
} else {
log.info("x4")
Helper.setProp("out.file", info.role + ".clear")
//@formatter:off
File(Helper.joinPath(Helper.prop("intermediateDir")!!, info.role + ".clear"))
@ -385,27 +391,27 @@ data class BootV3(
}
val tab = AsciiTable().let {
it.addRule()
it.addRow("image info", Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json"))
prints.add(Pair("image info", Helper.joinPath(workDir, info.role.removeSuffix(".img") + ".json")))
it.addRow("image info", shortenPath(Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json")))
prints.add(Pair("image info", shortenPath(Helper.joinPath(workDir, info.role.removeSuffix(".img") + ".json"))))
it.addRule()
if (this.kernel.size > 0) {
it.addRow("kernel", this.kernel.file)
prints.add(Pair("kernel", this.kernel.file))
it.addRow("kernel", shortenPath(this.kernel.file))
prints.add(Pair("kernel", shortenPath(this.kernel.file)))
File(Helper.joinPath(workDir, Helper.prop("kernelVersionStem")!!)).let { kernelVersionFile ->
if (kernelVersionFile.exists()) {
it.addRow("\\-- version " + kernelVersionFile.readLines().toString(), kernelVersionFile.path)
it.addRow("\\-- version " + kernelVersionFile.readLines().toString(), shortenPath( kernelVersionFile.path))
prints.add(
Pair(
"\\-- version " + kernelVersionFile.readLines().toString(),
kernelVersionFile.path
shortenPath(kernelVersionFile.path)
)
)
}
}
File(Helper.joinPath(workDir, Helper.prop("kernelConfigStem")!!)).let { kernelConfigFile ->
if (kernelConfigFile.exists()) {
it.addRow("\\-- config", kernelConfigFile.path)
prints.add(Pair("\\-- config", kernelConfigFile.path))
it.addRow("\\-- config", shortenPath(kernelConfigFile.path))
prints.add(Pair("\\-- config", shortenPath(kernelConfigFile.path)))
}
}
it.addRule()
@ -442,11 +448,11 @@ data class BootV3(
File(Avb.getJsonFileName("sig.boot")).let { jsonFile ->
if (jsonFile.exists()) {
it.addRow("GKI signature 2.0", this.bootSignature.file)
it.addRow("\\-- boot", jsonFile.path)
it.addRow("\\-- boot", shortenPath(jsonFile.path))
it.addRow("\\------ signing key", Avb.inspectKey(mapper.readValue(jsonFile, AVBInfo::class.java)))
//basic
prints.add(Pair("GKI signature 2.0", this.bootSignature.file))
prints.add(Pair("\\-- boot", jsonFile.path))
prints.add(Pair("\\-- boot", shortenPath(jsonFile.path)))
prints.add(
Pair(
"\\------ signing key",
@ -458,19 +464,19 @@ data class BootV3(
File(Avb.getJsonFileName("sig.kernel")).let { jsonFile ->
if (jsonFile.exists()) {
val readBackAvb = mapper.readValue(jsonFile, AVBInfo::class.java)
it.addRow("\\-- kernel", jsonFile.path)
it.addRow("\\-- kernel", shortenPath(jsonFile.path))
it.addRow("\\------ signing key", Avb.inspectKey(readBackAvb))
it.addRule()
//basic
prints.add(Pair("\\-- kernel", jsonFile.path))
prints.add(Pair("\\-- kernel", shortenPath(jsonFile.path)))
prints.add(Pair("\\------ signing key", Avb.inspectKey(readBackAvb)))
}
}
//AVB info
Avb.getJsonFileName(info.role).let { jsonFile ->
it.addRow("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE")
prints.add(Pair("AVB info", if (File(jsonFile).exists()) jsonFile else "NONE"))
it.addRow("AVB info", if (File(jsonFile).exists()) shortenPath(jsonFile) else "NONE")
prints.add(Pair("AVB info", if (File(jsonFile).exists()) shortenPath(jsonFile) else "NONE"))
if (File(jsonFile).exists()) {
mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai ->
it.addRow("\\------ signing key", Avb.inspectKey(ai))
@ -487,10 +493,10 @@ data class BootV3(
if (File(vbmetaCompanion).exists()) {
log.warn("XXXX: Found vbmeta.img, parsing ...")
//basic
prints.add(Pair("vbmeta.img", Avb.getJsonFileName("vbmeta.img")))
prints.add(Pair("vbmeta.img", shortenPath(Avb.getJsonFileName("vbmeta.img"))))
//table
it.addRule()
it.addRow("vbmeta.img", Avb.getJsonFileName("vbmeta.img"))
it.addRow("vbmeta.img", shortenPath(Avb.getJsonFileName("vbmeta.img")))
it.addRule()
"\n" + it.render()
} else {

@ -19,18 +19,19 @@ import cc.cfig.io.Struct
import cfig.Avb
import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.deleleIfExists
import cfig.bootimg.Common.Companion.toShortenPath
import cfig.bootimg.Signer
import cfig.helper.Dumpling
import cfig.helper.Helper
import cfig.helper.ZipHelper
import cfig.packable.VBMetaParser
import rom.fdt.DTC
import cfig.utils.EnvironmentVerifier
import com.fasterxml.jackson.databind.ObjectMapper
import de.vandermeer.asciitable.AsciiTable
import org.apache.commons.exec.CommandLine
import org.apache.commons.exec.DefaultExecutor
import org.slf4j.LoggerFactory
import rom.fdt.DTC
import java.io.*
import java.nio.ByteBuffer
import java.nio.ByteOrder
@ -265,7 +266,9 @@ data class VendorBoot(
}
this.dtb.size = File(this.dtb.file).length().toInt()
//header
FileOutputStream(this.info.role + ".clear", false).use { fos ->
val clearFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".clear")
val googleFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".google")
FileOutputStream(clearFile, false).use { fos ->
val encodedHeader = this.toHeader().encode()
fos.write(encodedHeader)
fos.write(ByteArray(Helper.round_to_multiple(encodedHeader.size, this.info.pageSize) - encodedHeader.size))
@ -303,17 +306,17 @@ data class VendorBoot(
}
}
//write
FileOutputStream("${this.info.role}.clear", true).use { fos ->
FileOutputStream(clearFile, true).use { fos ->
fos.write(bf.array(), 0, bf.position())
}
//google way
this.toCommandLine().addArgument(this.info.role + ".google").let {
this.toCommandLine().addArgument(googleFile).let {
log.info(it.toString())
DefaultExecutor().execute(it)
}
Helper.assertFileEquals(this.info.role + ".clear", this.info.role + ".google")
Helper.assertFileEquals(clearFile, googleFile)
return this
}
@ -321,7 +324,15 @@ data class VendorBoot(
val avbtool = String.format(Helper.prop("avbtool")!!, "v1.2")
File(Avb.getJsonFileName(info.role)).let {
if (it.exists()) {
Signer.signAVB(info.role, this.info.imageSize, avbtool)
//Signer.signAVB(info.role, this.info.imageSize, avbtool)
val clearFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".clear")
val signedFile = Helper.joinPath(Helper.prop("intermediateDir")!!, this.info.role + ".signed")
Signer.signAVB2(clearFile,
signedFile,
Avb.getJsonFileName(info.role),
this.info.imageSize,
avbtool
)
} else {
log.warn("skip signing of ${info.role}")
}
@ -329,6 +340,15 @@ data class VendorBoot(
return this
}
fun postCopy(outFile: String): VendorBoot {
val dir = Helper.prop("intermediateDir")!!
val signedFile = Helper.joinPath(dir, "${info.role}.signed").takeIf { File(it).exists() }
?: Helper.joinPath(dir, "${info.role}.clear")
log.info("COPY $signedFile -> $outFile")
File(signedFile).copyTo(File(outFile), overwrite = true)
return this
}
fun updateVbmeta(): VendorBoot {
Avb.updateVbmeta(info.role)
return this
@ -416,31 +436,31 @@ data class VendorBoot(
}
val tab = AsciiTable().let {
it.addRule()
val imageInfoJsonFile = Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json")
val imageInfoJsonFile = Helper.joinPath(workDir!!, info.role.removeSuffix(".img") + ".json").toShortenPath()
it.addRow("image info", imageInfoJsonFile)
prints.add(Pair("image info", imageInfoJsonFile))
it.addRule()
it.addRow("ramdisk", this.ramdisk.file)
prints.add(Pair("ramdisk", this.ramdisk.file))
it.addRow("ramdisk", this.ramdisk.file.toShortenPath())
prints.add(Pair("ramdisk", this.ramdisk.file.toShortenPath()))
if (this.ramdisk_table.size > 0) {
this.ramdisk_table.ramdidks.forEachIndexed { index, entry ->
//fancy ascii
it.addRow("-- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file)
it.addRow("------- extracted rootfs", File(workDir, "root.${index + 1}").path)
it.addRow("-- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file.toShortenPath())
it.addRow("------- extracted rootfs", File(workDir, "root.${index + 1}").path.toShortenPath())
//basic ascii
//@formatter:off
prints.add(Pair(" -- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file))
prints.add(Pair(" -- ${entry.type} ramdisk[${index + 1}/${this.ramdisk_table.ramdidks.size}]", entry.file.toShortenPath()))
//@formatter:on
prints.add(Pair(" ------- extracted rootfs", File(workDir, "root.${index + 1}").path))
prints.add(Pair(" ------- extracted rootfs", File(workDir, "root.${index + 1}").path.toShortenPath()))
}
} else {
it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path)
prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path))
it.addRow("\\-- extracted ramdisk rootfs", File(workDir, "root").path.toShortenPath())
prints.add(Pair("\\-- extracted ramdisk rootfs", File(workDir, "root").path.toShortenPath()))
}
it.addRule()
if (this.dtb.size > 0) {
it.addRow("dtb", this.dtb.file)
prints.add(Pair("dtb", this.dtb.file))
it.addRow("dtb", this.dtb.file.toShortenPath())
prints.add(Pair("dtb", this.dtb.file.toShortenPath()))
if (File(this.dtb.file + ".0.${dtsSuffix}").exists()) {
it.addRow("\\-- decompiled dts [${dtb.dtbList.size}]", dtb.file + "*.${dtsSuffix}")
prints.add(Pair("\\-- decompiled dts [${dtb.dtbList.size}]", dtb.file + "*.${dtsSuffix}"))
@ -451,14 +471,14 @@ data class VendorBoot(
}
if (this.bootconfig.size > 0) {
it.addRule()
it.addRow("bootconfig", this.bootconfig.file)
prints.add(Pair("bootconfig", this.bootconfig.file))
it.addRow("bootconfig", this.bootconfig.file.toShortenPath())
prints.add(Pair("bootconfig", this.bootconfig.file.toShortenPath()))
}
it.addRule()
Avb.getJsonFileName(info.role).let { jsonFile ->
if (File(jsonFile).exists()) {
it.addRow("AVB info", jsonFile)
prints.add(Pair("AVB info", jsonFile))
it.addRow("AVB info", jsonFile.toShortenPath())
prints.add(Pair("AVB info", jsonFile.toShortenPath()))
mapper.readValue(File(jsonFile), AVBInfo::class.java).let { ai ->
it.addRow("\\-- signing key", Avb.inspectKey(ai))
prints.add(Pair(" \\-- signing key", Avb.inspectKey(ai)))
@ -474,8 +494,8 @@ data class VendorBoot(
val tabVBMeta = AsciiTable().let {
if (File("vbmeta.img").exists()) {
it.addRule()
it.addRow("vbmeta.img", Avb.getJsonFileName("vbmeta.img"))
prints.add(Pair("vbmeta.img", Avb.getJsonFileName("vbmeta.img")))
it.addRow("vbmeta.img", Avb.getJsonFileName("vbmeta.img").toShortenPath())
prints.add(Pair("vbmeta.img", Avb.getJsonFileName("vbmeta.img").toShortenPath()))
it.addRule()
"\n" + it.render()
} else {
@ -492,8 +512,8 @@ data class VendorBoot(
return this
}
fun printPackSummary(): VendorBoot {
Common.printPackSummary(info.role)
fun printPackSummary(outFileName: String): VendorBoot {
Common.printPackSummary(info.role, outFileName)
return this
}

@ -15,6 +15,7 @@
package cfig.packable
import avb.blob.Footer
import cfig.bootimg.Common
import cfig.bootimg.Common.Companion.probeHeaderVersion
import cfig.bootimg.v2.BootV2
import cfig.bootimg.v2.BootV2Dialects
@ -45,14 +46,20 @@ class BootImgParser : IPackable {
}
override fun unpack(fileName: String) {
unpackInternal(fileName, fileName, outDir)
unpackInternal(fileName, outDir)
}
fun unpackInternal(targetFile: String, fileName: String, unpackDir: String) {
log.info("unpackInternal(fileName: $fileName, unpackDir: $unpackDir)")
Helper.setProp("workDir", unpackDir)
// called via reflection
fun unpackInternal(inFileName: String, unpackDir: String) {
log.info("unpackInternal(fileName: $inFileName, unpackDir: $unpackDir)")
val fileName = File(inFileName).canonicalPath
Helper.setProp("workDir", File(unpackDir).canonicalPath)
log.info("workspace set to $unpackDir")
clear()
File("$outDir/role").writeText(File(File(targetFile).canonicalPath).name)
//create workspace file
Common.createWorkspaceIni(fileName)
//create workspace file done
val hv = probeHeaderVersion(fileName)
log.info("header version $hv")
when (hv) {
@ -85,7 +92,64 @@ class BootImgParser : IPackable {
}
}
fun packInternal(targetFile: String, workspace: String, fileName: String) {
// called via reflection
fun packInternal(workspace: String, outFileName: String) {
log.info("packInternal($workspace, $outFileName)")
Helper.setProp("workDir", workspace)
val targetFile = outFileName
val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("role")
val cfgFile = File(workspace, iniRole.removeSuffix(".img") + ".json").canonicalPath
log.info("Loading config from $cfgFile")
if (!File(cfgFile).exists()) {
val tab = AsciiTable().let {
it.addRule()
it.addRow("'$cfgFile' doesn't exist, did you forget to 'unpack' ?")
it.addRule()
it
}
log.info("\n{}", tab.render())
return
}
val worker =
try {
ObjectMapper().readValue(File(cfgFile), BootV2::class.java)
} catch (e: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException) {
try {
ObjectMapper().readValue(File(cfgFile), BootV3::class.java)
} catch (e: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException) {
null
}
}
if (worker == null) {
log.error("no worker available")
exitProcess(2)
}
when (worker) {
is BootV2 -> {
worker
.pack()
.sign()
.updateVbmeta()
.printPackSummary()
}
is BootV3 -> {
worker
.pack()
.sign(targetFile)
.updateVbmeta()
.printPackSummary(worker.info.role)
}
else -> {
log.error("unsupported boot image format")
exitProcess(2)
}
}
}
fun packInternalLegacy(targetFile: String, workspace: String, fileName: String) {
log.info("packInternal(targetFile: $targetFile, fileName: $fileName, workspace: $workspace)")
Helper.setProp("workDir", workspace)
val cfgFile = Helper.joinPath(outDir, targetFile.removeSuffix(".img") + ".json")
@ -140,7 +204,8 @@ class BootImgParser : IPackable {
}
override fun pack(fileName: String) {
packInternal(fileName, outDir, fileName)
val targetFile = Common.loadProperties(File(outDir, "workspace.ini").canonicalPath).getProperty("role")
packInternal(outDir, targetFile)
}
fun flash(fileName: String) {

@ -1,6 +1,7 @@
package packable
import cfig.bootimg.Common
import cfig.bootimg.v3.VendorBoot
import cfig.helper.Helper
import cfig.helper.Helper.Companion.check_call
import cfig.helper.Helper.Companion.check_output
@ -15,13 +16,24 @@ class DeviceTreeParser : IPackable {
override fun capabilities(): List<String> {
return listOf("^.*\\.dtb$")
}
override val loopNo: Int
get() = 1
override fun unpack(fileName: String) {
super.clear()
log.info("unpacking $fileName")
val outFile = workDir + fileName.removeSuffix(".dtb") + "." + Helper.prop("config.dts_suffix")
unpackInternal(fileName, Helper.prop("workDir")!!)
}
fun unpackInternal(fileName: String, unpackDir: String) {
//set workdir
log.info("unpackInternal(fileName: $fileName, unpackDir: $unpackDir)")
Helper.setProp("workDir", unpackDir)
clear()
//create workspace file
Common.createWorkspaceIni(fileName)
//create workspace file done
val outFile = File(workDir, File(fileName).nameWithoutExtension + "." + Helper.prop("config.dts_suffix")).path
DTC().decompile(fileName, outFile)
//print summary
@ -32,15 +44,26 @@ class DeviceTreeParser : IPackable {
}
override fun pack(fileName: String) {
log.info("packing $fileName")
val outFile = workDir + fileName.removeSuffix(".dtb") + "." + Helper.prop("config.dts_suffix")
check(DTC().compile(outFile, "$fileName.new")) { "fail to compile dts" }
packInternal(Helper.prop("workDir")!!, fileName)
}
fun packInternal(workspace: String, outFileName: String) {
log.info("packInternal($workspace, $outFileName)")
Helper.setProp("workDir", workspace)
//workspace+cfg
val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("role")
val dtsSrc = File(workDir, File(iniRole).nameWithoutExtension + "." + Helper.prop("config.dts_suffix")).path
val origFile = File(workDir, File(outFileName).name + ".orig").path
log.info("COPY $outFileName -> $origFile")
File(outFileName).copyTo(File(origFile), overwrite = true)
check(DTC().compile(dtsSrc, outFileName)) { "fail to compile dts" }
//print summary
val prints: MutableList<Pair<String, String>> = mutableListOf()
prints.add(Pair("DTS", outFile))
prints.add(Pair("updated DTB", "$fileName.new"))
log.info("\n\t\t\tPack Summary of {}\n{}\n", fileName, Common.table2String(prints))
prints.add(Pair("DTS", dtsSrc))
prints.add(Pair("updated DTB", outFileName))
log.info("\n\t\t\tPack Summary of {}\n{}\n", outFileName, Common.table2String(prints))
}
fun pull(fileName: String) {

@ -14,10 +14,12 @@
package cfig.packable
import cfig.helper.Helper
import org.slf4j.LoggerFactory
import packable.DeviceTreeParser
import rom.sparse.SparseImgParser
import java.io.File
import java.util.Properties
import java.util.regex.Pattern
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
@ -28,6 +30,12 @@ class PackableLauncher
fun main(args: Array<String>) {
val log = LoggerFactory.getLogger(PackableLauncher::class.java)
val devLog = LoggerFactory.getLogger("XXXX")
val loadProperties: (String) -> Properties = { fileName ->
Properties().apply {
File(fileName).inputStream().use { load(it) }
}
}
val packablePool = mutableMapOf<List<String>, KClass<IPackable>>()
listOf(
BootImgParser(),
@ -44,25 +52,43 @@ fun main(args: Array<String>) {
packablePool.put(it.capabilities(), it::class as KClass<IPackable>)
}
packablePool.forEach {
log.debug("" + it.key + "/" + it.value)
log.info("" + it.key + "/" + it.value)
}
var targetFile: String? = null
var targetHandler: KClass<IPackable>? = null
var targetDir: String? = null
log.info("XXXX: args: " + args.asList().toString())
devLog.info("args: " + args.asList().toString())
args.forEachIndexed { index, s ->
devLog.info(" arg: #$index - $s")
}
run found@{
for (currentLoopNo in 0..1) { //currently we have only 2 loops
devLog.info("loop #" + currentLoopNo)
if (args.size > 1) { // manual mode
targetFile = if (File(args[1]).isFile) {
devLog.info("manual mode")
targetFile = if (File(args[1]).isFile) { //unpack
File(args[1]).canonicalPath
} else if (File(args[1] + "/role").isFile) {
File(args[1] + "/role").readText().trim()
} else {
log.error("Not sure of what to do: " + args.asList().toString())
} else if (File(args[1]).isDirectory and File(args[1], "workspace.ini").isFile) { //pack
loadProperties(File(args[1], "workspace.ini").canonicalPath).getProperty("role")
} else { //wrong
log.error("Not a file: " + args[1])
exitProcess(1)
}
log.warn("manual mode: args= ${args[1]}, $targetFile")
devLog.warn("manual mode: file=$targetFile")
targetDir = when (args.size) {
2 -> if (File(args[1]).isDirectory and File(args[1], "workspace.ini").isFile) { //arg = outDir
File(args[1]).canonicalPath
} else {
Helper.prop("workDir") // arg = outDir
}
3 -> File(args[2]).canonicalPath // arg = file
else -> {
throw IllegalArgumentException("too many args")
}
}
devLog.warn("manual mode: file=$targetFile, dir=$targetDir")
packablePool
.filter { it.value.createInstance().loopNo == currentLoopNo }
.forEach { p ->
@ -75,14 +101,15 @@ fun main(args: Array<String>) {
}
}
} else { // lazy mode
devLog.info("lazy mode (in current dir)")
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
log.info("Found: " + file.name + ", " + item)
targetFile = File(file.name).canonicalPath
targetHandler = p.value
return@found
}
@ -140,43 +167,26 @@ fun main(args: Array<String>) {
log.warn("'${args[0]}' sequence initialized")
log.warn("XXXX: args.size: ${args.size}")
val convertedArgs = args.copyOf().apply { set(0, targetFile!!) }
functions[0].call(it.createInstance(), *convertedArgs)
/*
val reflectRet = when (functions[0].parameters.size) {
1 -> {
log.warn("1: call null")
functions[0].call(it.createInstance())
}
2 -> {
log.warn("2: call $targetFile")
functions[0].call(it.createInstance(), targetFile!!)
}
3 -> {
if (args.size != 2) {
log.info("invoke: ${it.qualifiedName}, $targetFile, " + targetFile!!.removeSuffix(".img"))
functions[0].call(it.createInstance(), targetFile!!, targetFile!!.removeSuffix(".img"))
} else {
log.info("invoke: ${it.qualifiedName}, $targetFile, " + args[1])
functions[0].call(it.createInstance(), targetFile!!, args[1])
}
}
else -> {
functions[0].parameters.forEach { kp ->
println("Param: " + kp.index + " " + kp.type + " " + kp.name)
}
log.error("I am confused by so many parameters")
exitProcess(4)
val c = (if (args.size == 1) { //lazy mode
args.drop(1).toMutableList().apply {
add(targetFile!!)
//add(System.getProperty("user.dir"))
}
} else {
args.drop(1)
}).toTypedArray()
val convertedArgs = c
println("clazz = " + it.simpleName)
println("func = " + functions[0])
println("orig args:")
args.forEachIndexed { index, s ->
println("$index: $s")
}
if (functions[0].returnType.toString() != Unit.toString()) {
log.info("ret: $reflectRet")
println("Converted args:")
convertedArgs.forEachIndexed { index, s ->
println("$index: $s")
}
*/
functions[0].call(it.createInstance(), *convertedArgs)
log.warn("'${args[0]}' sequence completed")
}
}

@ -16,6 +16,7 @@ package cfig.packable
import avb.AVBInfo
import cfig.Avb
import cfig.bootimg.Common
import cfig.helper.Dumpling
import cfig.helper.Helper
import com.fasterxml.jackson.databind.ObjectMapper
@ -35,20 +36,43 @@ class VBMetaParser : IPackable {
return listOf("^vbmeta\\.img$", "^vbmeta\\_[a-z]+.img$")
}
// lazy mode
override fun unpack(fileName: String) {
File(Helper.prop("workDir")).let {
unpackInternal(fileName, Helper.prop("workDir")!!)
}
// manual mode
fun unpackInternal(inFileName: String, unpackDir: String) {
//common
log.info("unpackInternal(fileName: $inFileName, unpackDir: $unpackDir)")
val fileName = File(inFileName).canonicalPath
Helper.setProp("workDir", File(unpackDir).canonicalPath)
//prepare workdir
File(Helper.prop("workDir")!!).let {
if (!it.exists()) {
it.mkdirs()
}
}
//workspace.ini
log.info("workspace set to $unpackDir")
Common.createWorkspaceIni(fileName, prefix = "vbmeta")
val ai = AVBInfo.parseFrom(Dumpling(fileName)).dumpDefault(fileName)
log.info("Signing Key: " + Avb.inspectKey(ai))
}
override fun pack(fileName: String) {
val blob = ObjectMapper().readValue(File(Avb.getJsonFileName(fileName)), AVBInfo::class.java).encodePadded()
log.info("Writing padded vbmeta to file: $fileName.signed")
Files.write(Paths.get("$fileName.signed"), blob, StandardOpenOption.CREATE)
packInternal(outDir, fileName)
}
// called via reflection
fun packInternal(workspace: String, outFileName: String) {
log.info("packInternal(workspace: $workspace, $outFileName)")
Helper.setProp("workDir", workspace)
val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("vbmeta.role")
val blob = ObjectMapper().readValue(File(Avb.getJsonFileName(iniRole)), AVBInfo::class.java).encodePadded()
log.info("Writing padded vbmeta to file: $outFileName.signed")
Files.write(Paths.get("$outFileName.signed"), blob, StandardOpenOption.CREATE)
}
override fun flash(fileName: String, deviceName: String) {

@ -14,6 +14,7 @@
package cfig.packable
import cfig.bootimg.Common
import cfig.bootimg.v3.VendorBoot
import cfig.helper.Helper
import com.fasterxml.jackson.databind.ObjectMapper
@ -30,19 +31,18 @@ class VendorBootParser : IPackable {
}
override fun unpack(fileName: String) {
clear()
val vb = VendorBoot
.parse(fileName)
.extractImages()
.extractVBMeta()
.printUnpackSummary()
log.debug(vb.toString())
log.info("unpack(fileName: $fileName)")
unpackInternal(fileName, Helper.prop("workDir")!!)
}
fun unpackInternal(targetFile: String, fileName: String, unpackDir: String) {
fun unpackInternal(fileName: String, unpackDir: String) {
//set workdir
log.info("unpackInternal(fileName: $fileName, unpackDir: $unpackDir)")
Helper.setProp("workDir", unpackDir)
clear()
//create workspace file
Common.createWorkspaceIni(fileName)
//create workspace file done
val vb = VendorBoot
.parse(fileName)
.extractImages()
@ -52,13 +52,32 @@ class VendorBootParser : IPackable {
}
override fun pack(fileName: String) {
val cfgFile = "$outDir/${fileName.removeSuffix(".img")}.json"
log.info("packInternal($fileName)")
packInternal(Helper.prop("workDir")!!, fileName)
}
fun packInternal(workspace: String, outFileName: String) {
log.info("packInternal($workspace, $outFileName)")
Helper.setProp("workDir", workspace)
//intermediate
Helper.joinPath(workspace, "intermediate").also { intermediateDir ->
File(intermediateDir).let {
if (!it.exists()) {
it.mkdir()
}
}
Helper.setProp("intermediateDir", intermediateDir)
}
//workspace+cfg
val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("role")
val cfgFile = File(workspace, iniRole.removeSuffix(".img") + ".json").canonicalPath
log.info("Loading config from $cfgFile")
ObjectMapper().readValue(File(cfgFile), VendorBoot::class.java)
.pack()
.sign()
.postCopy(outFileName)
.updateVbmeta()
.printPackSummary()
.printPackSummary(outFileName)
}
override fun `@verify`(fileName: String) {

@ -34,7 +34,7 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
partName,
workDir,
File(workDir, File(info.output).nameWithoutExtension).path,
File(workDir, File(info.pulp).name + ".signed").path
File(Helper.prop("intermediateDir"), File(info.pulp).name + ".signed").path
)
}
@ -44,7 +44,7 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
partName,
workDir,
File(workDir, File(info.output).nameWithoutExtension).path,
File(workDir, "${info.output}.signed").path
File(Helper.prop("intermediateDir"), "${info.output}.signed").path
)
}
@ -119,7 +119,7 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
if (info.outerFsType == "sparse") {
img2simg(File(workDir, (File(info.output).name + ".signed")).path, File(info.output).name + ".signed")
} else {
val s = info.pulp + ".signed"
val s = Helper.joinPath(Helper.prop("intermediateDir")!!, File(info.pulp + ".signed").name)
val t = info.output + ".signed"
log.info("Moving $s -> $t")
Files.move(Path(s), Path(t), java.nio.file.StandardCopyOption.REPLACE_EXISTING)
@ -147,29 +147,32 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
val ret = SparseImage()
ret.info.json = File(fileName).name.removeSuffix(".img") + ".json"
ret.info.output = fileName
ret.info.pulp = workDir + fileName
ret.info.pulp = File(Helper.prop("workDir")!!, File(fileName).nameWithoutExtension).path
log.info(ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(ret.info))
if (isSparse(fileName)) {
val tempFile = UUID.randomUUID().toString()
ret.info.outerFsType = "sparse"
val rawFile = "${workDir}${File(fileName).nameWithoutExtension}"
simg2img(fileName, tempFile)
ret.info.pulp = if (isExt4(tempFile)) {
ret.info.innerFsType = "ext4"
"$rawFile.ext4"
ret.info.pulp + ".ext4"
} else if (isErofs(tempFile)) {
ret.info.innerFsType = "erofs"
"$rawFile.erofs"
ret.info.pulp + ".erofs"
} else {
"$rawFile.raw"
ret.info.pulp + ".raw"
}
Files.move(Path(tempFile), Path(ret.info.pulp))
} else if (isExt4(fileName)) {
ret.info.outerFsType = "ext4"
ret.info.innerFsType = "ext4"
ret.info.pulp = ret.info.pulp + ".ext4"
log.info("COPY $fileName -> ${ret.info.pulp}")
File(fileName).copyTo(File(ret.info.pulp))
} else if (isErofs(fileName)) {
ret.info.outerFsType = "erofs"
ret.info.innerFsType = "erofs"
ret.info.pulp = ret.info.pulp + ".erofs"
File(fileName).copyTo(File(ret.info.pulp))
}
when (ret.info.innerFsType) {
@ -258,7 +261,7 @@ data class SparseImage(var info: SparseInfo = SparseInfo()) {
DefaultExecutor().apply {
streamHandler = PumpStreamHandler(System.out, System.err)
}.execute(CommandLine.parse("aosp/plugged/bin/sefcontext_compile").apply {
addArguments("-o " + Helper.prop("workDir") + "file_contexts.bin")
addArguments("-o " + File(Helper.prop("workDir"), "file_contexts.bin").path)
addArgument("aosp/plugged/res/file_contexts.concat")
}.also { log.warn(it.toString()) }, env)
}

@ -15,6 +15,7 @@
package rom.sparse
import avb.blob.Footer
import cfig.bootimg.Common
import cfig.helper.Helper
import cfig.packable.IPackable
import cfig.packable.VBMetaParser
@ -38,15 +39,45 @@ class SparseImgParser : IPackable {
}
override fun unpack(fileName: String) {
log.info("unpack(fileName: $fileName)")
unpackInternal(fileName, Helper.prop("workDir")!!)
}
fun unpackInternal(fileName: String, unpackDir: String) {
//set workdir
log.info("unpackInternal(fileName: $fileName, unpackDir: $unpackDir)")
Helper.setProp("workDir", unpackDir)
clear()
//create workspace file
Common.createWorkspaceIni(fileName)
//create workspace file done
SparseImage
.parse(fileName)
.printSummary(fileName)
}
override fun pack(fileName: String) {
//TODO("not implemented: refer to https://github.com/cfig/Android_boot_image_editor/issues/133")
val cfgFile = outDir + fileName.removeSuffix(".img") + ".json"
packInternal(Helper.prop("workDir")!!, fileName)
}
fun packInternal(workspace: String, outFileName: String) {
log.info("packInternal($workspace, $outFileName)")
Helper.setProp("workDir", workspace)
//intermediate
Helper.joinPath(workspace, "intermediate").also { intermediateDir ->
File(intermediateDir).let {
if (!it.exists()) {
it.mkdir()
}
}
Helper.setProp("intermediateDir", intermediateDir)
}
//workspace+cfg
val iniRole = Common.loadProperties(File(workspace, "workspace.ini").canonicalPath).getProperty("role")
val cfgFile = File(workspace, iniRole.removeSuffix(".img") + ".json").canonicalPath
log.info("Loading config from $cfgFile")
val readBackSi = ObjectMapper().readValue(File(cfgFile), SparseImage::class.java)
readBackSi
.pack()

@ -0,0 +1,15 @@
@startuml
participant init
participant Service
participant "Service Started" as C
participant "Command" as D
init -> Service: +ueventd
Service -> C: ueventd started
init -> Service: +apexd-bootstrap
Service -> C: apexd-bootstrap started
init -> D !!: 'mkdir /acct/uid'
init -> D: update_linker_config
@enduml

@ -21,16 +21,16 @@ should be compatible with "/usr/bin/env sh"
## TODO: command line usage
unpack
```
abe unpack boot.img out
be unpack file
be unpack file dir
```
pack
```
abe pack out boot.img
be pack dir
```
properties: "out.file": the final output file
### something interesting in abe
### something interesting in be
a zsh script, parse the input command line parameters, for example,
if args are: "unpack file dir", the shell script will print 'gradle unpack --args="unpackInternal file dir"';
if args are "pack dir file", the shell script will print 'gradle pack --args="packInternal dir file"'.

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

@ -15,7 +15,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "2.0.0"
kotlin("jvm") version "2.1.21"
`java-library`
application
}
@ -58,11 +58,13 @@ java {
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType<KotlinCompile>().all {
kotlinOptions {
freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes"
jvmTarget = "11"
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
freeCompilerArgs.addAll(
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalUnsignedTypes"
)
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
}
}

@ -8,7 +8,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "2.0.20"
kotlin("jvm") version "2.1.21"
application
}
@ -30,6 +30,7 @@ dependencies {
// Use the JUnit 5 integration.
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.3")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation(kotlin("stdlib"))
}
java {
@ -37,11 +38,13 @@ java {
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType<KotlinCompile>().all {
kotlinOptions {
freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
freeCompilerArgs += "-opt-in=kotlin.ExperimentalUnsignedTypes"
jvmTarget = "11"
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
freeCompilerArgs.addAll(
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalUnsignedTypes"
)
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
}
}

@ -70,7 +70,17 @@ fun main(args: Array<String>) {
if (args[0] == "apps") {
//AppList.retrieveList()
}
if (args[0] == "rel") {
ImageRelease.run()
}
if (args[0] == "x") {
AMS.computeRankAndBucket(AMS.getProcRank(), AMS.getStandbyBucket2())
}
if (args[0] == "mount") {
MountAnalyzer().run()
}
if (args[0] == "booting") {
//BootingParser.run()
BootingParser.run2()
}
}

@ -0,0 +1,90 @@
package cfig.lazybox
import org.slf4j.LoggerFactory
import java.io.File
import java.util.regex.Pattern
class BootingParser {
companion object {
private val log = LoggerFactory.getLogger(BootingParser::class.java)
fun run() {
val logLines = File("booting.log").readLines()
val regex = Pattern.compile("""\[([^]]+)] \[\s*([0-9.]+)]\[\s*(T\d+)] init: starting service '([^']+)'.*""")
for (line in logLines) {
val matcher = regex.matcher(line)
if (matcher.find()) {
val timestamp = matcher.group(1)
val kernelTime = matcher.group(2)
val tLevel = matcher.group(3)
val serviceName = matcher.group(4)
println("Timestamp: $timestamp, Kernel Time: $kernelTime, T-Level: $tLevel, Service Name: $serviceName")
}
}
}
fun run2() {
val logLines = File("booting.log2").readLines()
val actionRegex = Pattern.compile("""\[([^]]+)] \[\s*([0-9.]+)]\[\s*(T\d+)] init: processing action \(([^)]+)\) from \(([^)]+)\).*""")
val commandRegex = Pattern.compile("""\[([^]]+)] \[\s*([0-9.]+)]\[\s*(T\d+)] init: Command '([^']+)' action=([^\(]+) \(([^)]+)\) took (\d+)ms and (succeeded|failed)(.*)?""")
val svcExecRegex = Pattern.compile("""\[([^]]+)] \[\s*([0-9.]+)]\[\s*(T\d+)] init: SVC_EXEC service '([^']+)' pid (\d+) \(([^)]+)\) started; waiting\.""")
val serviceStartRegex = Pattern.compile("""\[([^]]+)] \[\s*([0-9.]+)]\[\s*(T\d+)] init: starting service '([^']+)'.*""")
for (line in logLines) {
val actionMatcher = actionRegex.matcher(line)
if (actionMatcher.find()) {
val timestamp = actionMatcher.group(1)
val kernelTime = actionMatcher.group(2)
val tLevel = actionMatcher.group(3)
val actionName = actionMatcher.group(4)
val fromComponent = actionMatcher.group(5)
println("Timestamp: $timestamp, Kernel Time: $kernelTime, T-Level: $tLevel, Action Name: $actionName, From: $fromComponent")
}
val commandMatcher = commandRegex.matcher(line)
if (commandMatcher.find()) {
val timestamp = commandMatcher.group(1)
val kernelTime = commandMatcher.group(2)
val tLevel = commandMatcher.group(3)
val command = commandMatcher.group(4)
val action = commandMatcher.group(5).trim()
val fromComponent = commandMatcher.group(6)
val duration = commandMatcher.group(7)
val status = commandMatcher.group(8)
val failReason = commandMatcher.group(9)?.trim()
println("Timestamp: $timestamp, Kernel Time: $kernelTime, T-Level: $tLevel, Command: $command, Action: $action, From: $fromComponent, Duration: ${duration}ms, Status: $status${if (failReason != null) ", Reason: $failReason" else ""}")
}
val svcExecMatcher = svcExecRegex.matcher(line)
if (svcExecMatcher.find()) {
val timestamp = svcExecMatcher.group(1)
val kernelTime = svcExecMatcher.group(2)
val tLevel = svcExecMatcher.group(3)
val serviceName = svcExecMatcher.group(4)
val pid = svcExecMatcher.group(5)
val context = svcExecMatcher.group(6)
println("Timestamp: $timestamp, Kernel Time: $kernelTime, T-Level: $tLevel, Service Name: $serviceName, PID: $pid, Context: $context")
}
val serviceStartMatcher = serviceStartRegex.matcher(line)
if (serviceStartMatcher.find()) {
val timestamp = serviceStartMatcher.group(1)
val kernelTime = serviceStartMatcher.group(2)
val tLevel = serviceStartMatcher.group(3)
val serviceName = serviceStartMatcher.group(4)
println("Timestamp: $timestamp, Kernel Time: $kernelTime, T-Level: $tLevel, Service Name: $serviceName")
}
}
} // end-of-fun
} // end-of-companion
}

@ -0,0 +1,67 @@
package cfig.lazybox
import cfig.helper.Helper
import cfig.helper.Helper.Companion.check_call
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
class ImageRelease {
companion object {
private val log = LoggerFactory.getLogger(ImageRelease::class.java)
fun run() {
val buildId = Helper.adbCmd("getprop ro.build.id").lowercase()
val variant = Helper.adbCmd("getprop ro.build.type")
val product = Helper.adbCmd("getprop ro.build.product")
val rel = Helper.adbCmd("getprop ro.build.version.release")
val increment = Helper.adbCmd("getprop ro.build.version.incremental")
val fp = Helper.adbCmd("getprop ro.build.fingerprint")
val computFacDir = "$product-factory-$buildId"
val computFacZip = "$product-factory-$buildId-$increment.zip"
val computOtaZip = "$product-ota-$buildId-$increment.zip"
val computEmmcZip = "$product-eMMCimg-$buildId-$increment.zip"
log.info("fingerprint: $fp")
log.info("$product-factory-$buildId-$increment -> $product-factory-$buildId")
log.info("$product-ota-$buildId-$increment")
log.info("$product-eMMCimg-$buildId-$increment")
//factory
if (File("factory.zip").exists()) {
"rm -fr factory_image factory_img $computFacDir $computFacZip".check_call()
"unzip factory.zip".check_call()
val facDir = if (File("factory_img").exists()) {
//user
"factory_img"
} else if (File("factory_image").exists()) {
//userdebug
"factory_image"
} else {
throw IllegalStateException("can not find factory image folder")
}
File(facDir).listFiles()?.filter { it.name.endsWith(".sh") }?.forEach { it.delete() }
"cp -v /tftp/flash_platypus.sh $facDir/flash.sh".check_call()
"mv -v $facDir $computFacDir".check_call()
"zip $computFacZip -r $computFacDir".check_call()
"rm -fr $computFacDir".check_call()
}
File("factory.zip").delete()
if (File("ota.zip").exists()) {
Files.move(Paths.get("ota.zip"), Paths.get(computOtaZip))
}
if (File("emmc.zip").exists()) {
Files.move(Paths.get("emmc.zip"), Paths.get(computEmmcZip))
}
log.info("fingerprint: $fp")
log.info("$product-factory-$buildId-$increment -> $product-factory-$buildId")
log.info(computFacZip)
log.info(computOtaZip)
log.info(computEmmcZip)
}
}
}

@ -0,0 +1,148 @@
package cfig.lazybox
import org.slf4j.LoggerFactory
import java.io.BufferedWriter
import java.io.File
import java.io.FileOutputStream
import java.io.FileWriter
class MountAnalyzer {
data class MountInfo(
var dev: String = "",
var mountPoint: String = "",
var fsType: String = "",
var flags: String? = null,
)
class MiComparator : Comparator<MountInfo> {
override fun compare(p1: MountInfo, p2: MountInfo): Int {
var ret = p1.fsType.compareTo(p2.fsType) * 100
ret += p1.dev.compareTo(p2.dev) * 10
ret += p1.mountPoint.compareTo(p2.mountPoint) * 1
return ret
}
}
fun run() {
val loopApex = mutableListOf<MountInfo>()
val dmApex = mutableListOf<MountInfo>()
val tmpApex = mutableListOf<MountInfo>()
val bootApex = mutableListOf<MountInfo>()
val fuseInfo = mutableListOf<MountInfo>()
val sysInfo = mutableListOf<MountInfo>()
val androidRo = mutableListOf<MountInfo>()
val androidRw = mutableListOf<MountInfo>()
val otherRw = mutableListOf<MountInfo>()
val unknownMi = mutableListOf<MountInfo>()
val lines = File("mount.log").readLines()
lines.forEachIndexed { n, line ->
val regex = Regex("(\\S+)\\s+on\\s+(\\S+)\\s+type\\s+(\\w+)\\s+\\(([^)]*)\\)") // Capture flags
val matchResult = regex.find(line)
if (matchResult != null) {
val dev = matchResult.groupValues[1]
val mountPoint = matchResult.groupValues[2]
val fsType = matchResult.groupValues[3]
val flags =
if (matchResult.groupValues.size > 4) matchResult.groupValues[4] else null // Handle no flags
val mi = MountInfo(dev, mountPoint, fsType, flags)
if (mi.mountPoint.startsWith("/apex") || mi.mountPoint.startsWith("/bootstrap-apex")) {
if (mi.mountPoint.startsWith("/bootstrap-apex")) {
bootApex.add(mi)
} else if (mi.dev.startsWith("/dev/block/loop")) {
loopApex.add(mi)
} else if (mi.dev.startsWith("/dev/block/dm")) {
dmApex.add(mi)
} else if (mi.dev.startsWith("tmpfs")) {
tmpApex.add(mi)
} else {
log.info("$fsType: $dev -> $mountPoint")
throw IllegalStateException("X1")
}
} else if (mi.mountPoint.startsWith("/sys/") || mi.mountPoint == "/sys") {
sysInfo.add(mi)
} else if (mi.fsType == "fuse") {
fuseInfo.add(mi)
} else {
log.info("$fsType: $dev -> $mountPoint")
if (mi.flags!!.contains("ro,") or mi.flags!!.contains("ro)")) {
androidRo.add(mi)
} else if (mi.flags!!.contains("rw,") or mi.flags!!.contains("rw)")) {
if (mi.dev.startsWith("/dev/")) {
androidRw.add(mi)
} else {
otherRw.add(mi)
}
} else {
throw IllegalStateException("X2")
}
}
} else { //For lines without flags
val regexNoFlags = Regex("(\\S+)\\s+on\\s+(\\S+)\\s+type\\s+(\\w+)")
val matchResultNoFlags = regexNoFlags.find(line)
if (matchResultNoFlags != null) {
val dev = matchResultNoFlags.groupValues[1]
val mountPoint = matchResultNoFlags.groupValues[2]
val fsType = matchResultNoFlags.groupValues[3]
val mi = MountInfo(dev, mountPoint, fsType, null)
unknownMi.add(mi)
} else {
throw IllegalStateException("X3")
}
}
} // end-of-lines
//sanity check, make sure consistent
check(
listOf(
loopApex,
dmApex,
tmpApex,
bootApex,
fuseInfo,
sysInfo,
androidRo,
androidRw,
otherRw,
unknownMi
).sumOf { it.size } == lines.size)
//dump
val infoNames = listOf(
"fusefs",
"sysfs",
"Android RO",
"Android RW",
"other Rw",
"loop apex",
"dm apex",
"tmp apex",
"boot apex",
"unknown"
)
BufferedWriter(FileWriter(File("sorted_mount.log"))).use { fos ->
listOf(
fuseInfo,
sysInfo,
androidRo,
androidRw,
otherRw,
loopApex,
dmApex,
tmpApex,
bootApex,
unknownMi
).forEachIndexed { n, mis ->
mis.sortWith(MiComparator())
log.info(infoNames.get(n))
fos.write(infoNames.get(n) + "\n")
mis.forEachIndexed { index, it ->
log.info("[$index] ${it.fsType} : ${it.dev} -> ${it.mountPoint} (${it.flags})")
fos.write("#$index | ${it.fsType} | ${it.dev} | ${it.mountPoint} | (${it.flags})\n")
}
fos.write("\n")
}
}
}
companion object {
private val log = LoggerFactory.getLogger(MountAnalyzer::class.java)
}
}

@ -1,3 +1,8 @@
pluginManagement {
plugins {
kotlin("jvm") version "2.1.21"
}
}
rootProject.name = "boot"
include("bbootimg")
include("aosp:apksigner")

@ -1 +1 @@
Subproject commit 9b783be45a23379d024157a6740fb06ba535e4d4
Subproject commit 012bda615f1c02f16064c30b828f2307f08a6730

@ -1 +1 @@
Subproject commit 15c3878486abe3f2e42e6e0f4f0d82682546afaf
Subproject commit b1bff460f66fb916e30a0f7ca0b93bc922412b53

@ -73,6 +73,12 @@ unknown_list = [
"super_empty_all.img", #ADT-3
"bootloader.img", #many
"dt.img",
"fastboot.img", #anon
"fastlogo.img", #anon
"bl.img", #anon
"tzl.img", #anon
"preboot.img", #anon
"tzk.img", #anon
]
tmp2 = "tmp2"

@ -2,10 +2,12 @@
baseDir=${0:a:h}
export baseDir
be_caller_dir=${PWD}
export be_caller_dir
set -e
# Parse command line arguments
if [[ $# -eq 0 ]]; then
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <operation> [<file> <dir>]"
exit 1
fi
@ -13,34 +15,48 @@ fi
operation=$1
echo arg num: $#
echo "args: $0 $1 $2"
echo "baseDir: $baseDir"
echo "callerDir: $be_caller_dir"
source_code_dir=/home/yu/work/boot
# Determine which operation to perform
set -x
case $operation in
"unpack")
if [[ $# -eq 2 ]]; then
file=`realpath $2`
dir=`realpath out`
cd ${baseDir}/../../../
cd ${source_code_dir}
pwd
gradle unpack --args="unpackInternal $file $dir"
elif [[ $# -eq 3 ]]; then
file=`realpath $2`
dir=`realpath $3`
cd ${baseDir}/../../../
cd ${source_code_dir}
gradle unpack --args="unpackInternal $file $dir"
else
echo "Invalid args"
cd ${baseDir}/../../../
gradle unpack
fi
;;
"pack")
if [[ $# -eq 3 ]]; then
pwd
if [[ $# -eq 2 ]]; then
dir=`realpath $2`
cd ${source_code_dir}
file=${be_caller_dir}/$(grep -m1 '^role=' $dir/workspace.ini | cut -d'=' -f2-)
gradle pack --args="packInternal $dir $file"
elif [[ $# -eq 3 ]]; then
dir=`realpath $2`
file=`realpath $3`
cd ${baseDir}/../../../
cd ${source_code_dir}
gradle pack --args="packInternal $dir $file"
else
cd ${baseDir}/../../../
gradle pack
##cd ${baseDir}/../../../
#cd ${source_code_dir}
#gradle pack
echo "Invalid args"
fi
;;
*)
Loading…
Cancel
Save