diff --git a/bbootimg/build.gradle b/bbootimg/build.gradle index 6e8258a..64574b8 100644 --- a/bbootimg/build.gradle +++ b/bbootimg/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlinVersion = "1.3.20" + kotlinVersion = "1.3.30" } repositories { mavenCentral() diff --git a/bbootimg/src/main/java/cfig/io/Struct.java b/bbootimg/src/main/java/cfig/io/Struct.java index 4a1510f..4d2e4bd 100644 --- a/bbootimg/src/main/java/cfig/io/Struct.java +++ b/bbootimg/src/main/java/cfig/io/Struct.java @@ -28,6 +28,9 @@ public class Struct { if (formatString.startsWith(">") || formatString.startsWith("!")) { this.byteOrder = ByteOrder.BIG_ENDIAN; log.debug("Parsing BIG_ENDIAN format: " + formatString); + } else if (formatString.startsWith("@") || formatString.startsWith("=")) { + this.byteOrder = ByteOrder.nativeOrder(); + log.debug("Parsing native ENDIAN format: " + formatString); } else { log.debug("Parsing LITTLE_ENDIAN format: " + formatString); } @@ -38,7 +41,12 @@ public class Struct { if (!m.group(1).isEmpty()) { mul = Integer.decode(m.group(1)); } + //item[0]: Type, item[1]: multiple + // if need to expand format items, explode it + // eg: "4L" will be exploded to "1L 1L 1L 1L" + // eg: "10x" won't be exploded, it's still "10x" Object item[] = new Object[2]; + switch (m.group(2)) { case "x": {//byte 1 item[0] = PadByte.class; @@ -98,7 +106,7 @@ public class Struct { } public Integer calcSize() { - Integer ret = 0; + int ret = 0; for (Object[] format : formats) { if (format[0] == Byte.class || format[0] == Character.class || format[0] == PadByte.class) { ret += (int) format[1]; @@ -239,7 +247,7 @@ public class Struct { for (int i = 0; i < args.length; i++) { Object arg = args[i]; Class format = (Class) formats.get(i)[0]; - Integer size = (int) formats.get(i)[1]; + int size = (int) formats.get(i)[1]; log.debug("Index[" + i + "], fmt = " + format + ", arg = " + arg + ", multi = " + size); //padding diff --git a/bbootimg/src/main/kotlin/Avb.kt b/bbootimg/src/main/kotlin/Avb.kt index 0fc62f7..74ebbdc 100755 --- a/bbootimg/src/main/kotlin/Avb.kt +++ b/bbootimg/src/main/kotlin/Avb.kt @@ -4,7 +4,7 @@ import avb.* import avb.alg.Algorithms import avb.desc.* import avb.AuxBlob -import cfig.io.Struct +import cfig.io.Struct3 import com.fasterxml.jackson.databind.ObjectMapper import org.apache.commons.codec.binary.Hex import org.slf4j.LoggerFactory @@ -35,7 +35,7 @@ class Avb { common_algorithm: String, inReleaseString: String?) { log.info("add_hash_footer($image_file) ...") - var original_image_size: Long + var original_image_size: ULong //required libavb version if (use_persistent_digest || do_not_use_ab) { required_libavb_version_minor = 1 @@ -72,11 +72,11 @@ class Avb { FileOutputStream(File(image_file), true).channel.use { log.info("original image $image_file has AVB footer, " + "truncate it to original SIZE: ${footer.originalImageSize}") - it.truncate(footer.originalImageSize) + it.truncate(footer.originalImageSize.toLong()) } } catch (e: IllegalArgumentException) { log.info("original image $image_file doesn't have AVB footer") - original_image_size = originalFileSize + original_image_size = originalFileSize.toULong() } //salt @@ -103,12 +103,12 @@ class Avb { //HashDescriptor val hd = HashDescriptor() - hd.image_size = File(image_file).length() - hd.hash_algorithm = hash_algorithm.toByteArray() + hd.image_size = File(image_file).length().toULong() + hd.hash_algorithm = hash_algorithm hd.partition_name = partition_name hd.salt = saltByteArray - hd.flags = 0 - if (do_not_use_ab) hd.flags = hd.flags or 1 + hd.flags = 0U + if (do_not_use_ab) hd.flags = hd.flags or 1u if (!use_persistent_digest) hd.digest = digest log.info("encoded hash descriptor:" + Hex.encodeHexString(hd.encode())) @@ -121,15 +121,15 @@ class Avb { 0, null, null, - 0, + 0U, inReleaseString) log.debug("vbmeta_blob: " + Helper.toHexString(vbmeta_blob)) Helper.dumpToFile("hashDescriptor.vbmeta.blob", vbmeta_blob) log.info("Padding image ...") // image + padding - if (hd.image_size % BLOCK_SIZE != 0L) { - val padding_needed = BLOCK_SIZE - (hd.image_size % BLOCK_SIZE) + if (hd.image_size.toLong() % BLOCK_SIZE != 0L) { + val padding_needed = BLOCK_SIZE - (hd.image_size.toLong() % BLOCK_SIZE) FileOutputStream(image_file, true).use { fos -> fos.write(ByteArray(padding_needed.toInt())) } @@ -142,7 +142,7 @@ class Avb { log.info("Appending vbmeta ...") val vbmeta_offset = File(image_file).length() val padding_needed = Helper.round_to_multiple(vbmeta_blob.size.toLong(), BLOCK_SIZE) - vbmeta_blob.size - val vbmeta_blob_with_padding = Helper.join(vbmeta_blob, Struct("${padding_needed}x").pack(null)) + val vbmeta_blob_with_padding = Helper.join(vbmeta_blob, Struct3("${padding_needed}x").pack(null)) FileOutputStream(image_file, true).use { fos -> fos.write(vbmeta_blob_with_padding) } @@ -151,18 +151,18 @@ class Avb { log.info("Appending DONT_CARE chunk ...") val vbmeta_end_offset = vbmeta_offset + vbmeta_blob_with_padding.size FileOutputStream(image_file, true).use { fos -> - fos.write(Struct("${partition_size - vbmeta_end_offset - 1 * BLOCK_SIZE}x").pack(null)) + fos.write(Struct3("${partition_size - vbmeta_end_offset - 1 * BLOCK_SIZE}x").pack(null)) } // + AvbFooter + padding log.info("Appending footer ...") val footer = Footer() footer.originalImageSize = original_image_size - footer.vbMetaOffset = vbmeta_offset - footer.vbMetaSize = vbmeta_blob.size.toLong() + footer.vbMetaOffset = vbmeta_offset.toULong() + footer.vbMetaSize = vbmeta_blob.size.toULong() val footerBob = footer.encode() val footerBlobWithPadding = Helper.join( - Struct("${BLOCK_SIZE - Footer.SIZE}x").pack(null), footerBob) + Struct3("${BLOCK_SIZE - Footer.SIZE}x").pack(null), footerBob) log.info("footer:" + Helper.toHexString(footerBob)) log.info(footer.toString()) FileOutputStream(image_file, true).use { fos -> @@ -181,7 +181,7 @@ class Avb { inFlags: Long, props: Map?, kernel_cmdlines: List?, - required_libavb_version_minor: Int, + required_libavb_version_minor: UInt, inReleaseString: String?): ByteArray { //encoded descriptors var encodedDesc: ByteArray = byteArrayOf() @@ -207,31 +207,31 @@ class Avb { //1 - whole header blob val headerBlob = Header().apply { bump_required_libavb_version_minor(required_libavb_version_minor) - auxiliary_data_block_size = auxBlob.size.toLong() + auxiliary_data_block_size = auxBlob.size.toULong() authentication_data_block_size = Helper.round_to_multiple( - (alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64) + (alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64).toULong() - algorithm_type = alg.algorithm_type.toLong() + algorithm_type = alg.algorithm_type.toUInt() - hash_offset = 0 - hash_size = alg.hash_num_bytes.toLong() + hash_offset = 0U + hash_size = alg.hash_num_bytes.toULong() - signature_offset = alg.hash_num_bytes.toLong() - signature_size = alg.signature_num_bytes.toLong() + signature_offset = alg.hash_num_bytes.toULong() + signature_size = alg.signature_num_bytes.toULong() - descriptors_offset = 0 - descriptors_size = encodedDesc.size.toLong() + descriptors_offset = 0U + descriptors_size = encodedDesc.size.toULong() public_key_offset = descriptors_size - public_key_size = encodedKey.size.toLong() + public_key_size = encodedKey.size.toULong() //TODO: support pubkey metadata - public_key_metadata_size = 0 + public_key_metadata_size = 0U public_key_metadata_offset = public_key_offset + public_key_size - rollback_index = inRollbackIndex - flags = inFlags + rollback_index = inRollbackIndex.toULong() + flags = inFlags.toUInt() if (inReleaseString != null) { log.info("Using preset release string: $inReleaseString") this.release_string = inReleaseString @@ -248,7 +248,7 @@ class Avb { log.info("parsing $image_file ...") val jsonFile = getJsonFileName(image_file) var footer: Footer? = null - var vbMetaOffset = 0L + var vbMetaOffset: ULong = 0U FileInputStream(image_file).use { fis -> fis.skip(File(image_file).length() - Footer.SIZE) try { @@ -262,13 +262,13 @@ class Avb { var vbMetaHeader = Header() FileInputStream(image_file).use { fis -> - fis.skip(vbMetaOffset) + fis.skip(vbMetaOffset.toLong()) vbMetaHeader = Header(fis) } log.info(vbMetaHeader.toString()) log.debug(ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(vbMetaHeader)) - val authBlockOffset = vbMetaOffset + Header.SIZE + val authBlockOffset = vbMetaOffset + Header.SIZE.toUInt() val auxBlockOffset = authBlockOffset + vbMetaHeader.authentication_data_block_size val descStartOffset = auxBlockOffset + vbMetaHeader.descriptors_offset @@ -276,22 +276,22 @@ class Avb { ai.footer = footer ai.auxBlob = AuxBlob() ai.header = vbMetaHeader - if (vbMetaHeader.public_key_size > 0L) { + if (vbMetaHeader.public_key_size > 0U) { ai.auxBlob!!.pubkey = AuxBlob.PubKeyInfo() - ai.auxBlob!!.pubkey!!.offset = vbMetaHeader.public_key_offset - ai.auxBlob!!.pubkey!!.size = vbMetaHeader.public_key_size + ai.auxBlob!!.pubkey!!.offset = vbMetaHeader.public_key_offset.toLong() + ai.auxBlob!!.pubkey!!.size = vbMetaHeader.public_key_size.toLong() } - if (vbMetaHeader.public_key_metadata_size > 0L) { + if (vbMetaHeader.public_key_metadata_size > 0U) { ai.auxBlob!!.pubkeyMeta = AuxBlob.PubKeyMetadataInfo() - ai.auxBlob!!.pubkeyMeta!!.offset = vbMetaHeader.public_key_metadata_offset - ai.auxBlob!!.pubkeyMeta!!.size = vbMetaHeader.public_key_metadata_size + ai.auxBlob!!.pubkeyMeta!!.offset = vbMetaHeader.public_key_metadata_offset.toLong() + ai.auxBlob!!.pubkeyMeta!!.size = vbMetaHeader.public_key_metadata_size.toLong() } var descriptors: List = mutableListOf() - if (vbMetaHeader.descriptors_size > 0) { + if (vbMetaHeader.descriptors_size > 0U) { FileInputStream(image_file).use { fis -> - fis.skip(descStartOffset) - descriptors = UnknownDescriptor.parseDescriptors2(fis, vbMetaHeader.descriptors_size) + fis.skip(descStartOffset.toLong()) + descriptors = UnknownDescriptor.parseDescriptors2(fis, vbMetaHeader.descriptors_size.toLong()) } descriptors.forEach { @@ -299,32 +299,32 @@ class Avb { } } - if (vbMetaHeader.public_key_size > 0) { + if (vbMetaHeader.public_key_size > 0U) { FileInputStream(image_file).use { fis -> - fis.skip(auxBlockOffset) - fis.skip(vbMetaHeader.public_key_offset) + fis.skip(auxBlockOffset.toLong()) + fis.skip(vbMetaHeader.public_key_offset.toLong()) ai.auxBlob!!.pubkey!!.pubkey = ByteArray(vbMetaHeader.public_key_size.toInt()) fis.read(ai.auxBlob!!.pubkey!!.pubkey) log.debug("Parsed Pub Key: " + Hex.encodeHexString(ai.auxBlob!!.pubkey!!.pubkey)) } } - if (vbMetaHeader.public_key_metadata_size > 0) { + if (vbMetaHeader.public_key_metadata_size > 0U) { FileInputStream(image_file).use { fis -> - fis.skip(vbMetaOffset) + fis.skip(vbMetaOffset.toLong()) fis.skip(Header.SIZE.toLong()) - fis.skip(vbMetaHeader.public_key_metadata_offset) + fis.skip(vbMetaHeader.public_key_metadata_offset.toLong()) val ba = ByteArray(vbMetaHeader.public_key_metadata_size.toInt()) fis.read(ba) log.debug("Parsed Pub Key Metadata: " + Hex.encodeHexString(ba)) } } - if (vbMetaHeader.authentication_data_block_size > 0) { + if (vbMetaHeader.authentication_data_block_size > 0U) { FileInputStream(image_file).use { fis -> - fis.skip(vbMetaOffset) + fis.skip(vbMetaOffset.toLong()) fis.skip(Header.SIZE.toLong()) - fis.skip(vbMetaHeader.hash_offset) + fis.skip(vbMetaHeader.hash_offset.toLong()) val ba = ByteArray(vbMetaHeader.hash_size.toInt()) fis.read(ba) log.debug("Parsed Auth Hash (Header & Aux Blob): " + Hex.encodeHexString(ba)) @@ -382,7 +382,7 @@ class Avb { //3 - whole aux blob var auxBlob = byteArrayOf() - if (ai.header!!.auxiliary_data_block_size > 0) { + if (ai.header!!.auxiliary_data_block_size > 0U) { if (encodedKey.contentEquals(ai.auxBlob!!.pubkey!!.pubkey)) { log.info("Using the same key as original vbmeta") } else { @@ -395,24 +395,24 @@ class Avb { //1 - whole header blob val headerBlob = ai.header!!.apply { - auxiliary_data_block_size = auxBlob.size.toLong() + auxiliary_data_block_size = auxBlob.size.toULong() authentication_data_block_size = Helper.round_to_multiple( - (alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64) + (alg.hash_num_bytes + alg.signature_num_bytes).toLong(), 64).toULong() - descriptors_offset = 0 - descriptors_size = encodedDesc.size.toLong() + descriptors_offset = 0U + descriptors_size = encodedDesc.size.toULong() - hash_offset = 0 - hash_size = alg.hash_num_bytes.toLong() + hash_offset = 0U + hash_size = alg.hash_num_bytes.toULong() - signature_offset = alg.hash_num_bytes.toLong() - signature_size = alg.signature_num_bytes.toLong() + signature_offset = alg.hash_num_bytes.toULong() + signature_size = alg.signature_num_bytes.toULong() public_key_offset = descriptors_size - public_key_size = encodedKey.size.toLong() + public_key_size = encodedKey.size.toULong() //TODO: support pubkey metadata - public_key_metadata_size = 0 + public_key_metadata_size = 0U public_key_metadata_offset = public_key_offset + public_key_size }.encode() @@ -430,7 +430,7 @@ class Avb { fun packVbMetaWithPadding(info: AVBInfo? = null) { val rawBlob = packVbMeta(info) val paddingSize = Helper.round_to_multiple(rawBlob.size.toLong(), BLOCK_SIZE) - rawBlob.size - val paddedBlob = Helper.join(rawBlob, Struct("${paddingSize}x").pack(null)) + val paddedBlob = Helper.join(rawBlob, Struct3("${paddingSize}x").pack(null)) log.info("raw vbmeta size ${rawBlob.size}, padding size $paddingSize, total blob size ${paddedBlob.size}") log.info("Writing padded vbmeta to file: vbmeta.img.signed") Files.write(Paths.get("vbmeta.img.signed"), paddedBlob, StandardOpenOption.CREATE) @@ -438,8 +438,8 @@ class Avb { companion object { private val log = LoggerFactory.getLogger(Avb::class.java) - const val AVB_VERSION_MAJOR = 1 - const val AVB_VERSION_MINOR = 1 + const val AVB_VERSION_MAJOR = 1U + const val AVB_VERSION_MINOR = 1U const val AVB_VERSION_SUB = 0 fun getJsonFileName(image_file: String): String { diff --git a/bbootimg/src/main/kotlin/Helper.kt b/bbootimg/src/main/kotlin/Helper.kt index aeec9ff..5670547 100644 --- a/bbootimg/src/main/kotlin/Helper.kt +++ b/bbootimg/src/main/kotlin/Helper.kt @@ -1,7 +1,8 @@ package cfig -import cfig.io.Struct +import cfig.io.Struct3 import com.google.common.math.BigIntegerMath +import com.sun.org.apache.xml.internal.utils.UnImplNode import org.apache.commons.codec.binary.Hex import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream import org.apache.commons.compress.compressors.gzip.GzipParameters @@ -42,6 +43,14 @@ class Helper { return baos.toByteArray() } + fun toHexString(inData: UByteArray): String { + val sb = StringBuilder() + for (i in inData.indices) { + sb.append(Integer.toString((inData[i].toInt().and(0xff)) + 0x100, 16).substring(1)) + } + return sb.toString() + } + fun toHexString(inData: ByteArray): String { val sb = StringBuilder() for (i in inData.indices) { @@ -169,6 +178,15 @@ class Helper { } } + fun round_to_multiple(size: UInt, page: UInt): UInt { + val remainder = size % page + return if (remainder == 0U) { + size + } else { + size + page - remainder + } + } + fun round_to_multiple(size: Long, page: Int): Long { val remainder = size % page return if (remainder == 0L) { @@ -201,7 +219,7 @@ class Helper { log.debug("BB: " + numBits / 8 + ", mod_len: " + rsa.modulus.toByteArray().size + ", rrmodn = " + rrModn.toByteArray().size) val unsignedModulo = rsa.modulus.toByteArray().sliceArray(1..numBits / 8) //remove sign byte log.debug("unsigned modulo: " + Hex.encodeHexString(unsignedModulo)) - val ret = Struct("!II${numBits / 8}b${numBits / 8}b").pack( + val ret = Struct3("!II${numBits / 8}b${numBits / 8}b").pack( numBits, n0inv, unsignedModulo, diff --git a/bbootimg/src/main/kotlin/Packer.kt b/bbootimg/src/main/kotlin/Packer.kt index f55093e..04c388c 100644 --- a/bbootimg/src/main/kotlin/Packer.kt +++ b/bbootimg/src/main/kotlin/Packer.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import org.apache.commons.exec.CommandLine import org.apache.commons.exec.DefaultExecutor import org.apache.commons.exec.PumpStreamHandler +import org.junit.Assert import org.junit.Assert.assertTrue import org.slf4j.LoggerFactory import java.io.* @@ -49,6 +50,11 @@ class Packer { return md.digest() } + private fun writePaddedFile(inBF: ByteBuffer, srcFile: String, padding: UInt) { + Assert.assertTrue(padding < Int.MAX_VALUE.toUInt()) + writePaddedFile(inBF, srcFile, padding.toInt()) + } + private fun writePaddedFile(inBF: ByteBuffer, srcFile: String, padding: Int) { FileInputStream(srcFile).use { iS -> var byteRead: Int @@ -64,6 +70,11 @@ class Packer { } } + private fun padFile(inBF: ByteBuffer, padding: UInt) { + Assert.assertTrue(padding < Int.MAX_VALUE.toUInt()) + padFile(inBF, padding.toInt()) + } + private fun padFile(inBF: ByteBuffer, padding: Int) { val pad = padding - (inBF.position() and padding - 1) and padding - 1 inBF.put(ByteArray(pad)) @@ -77,16 +88,16 @@ class Packer { bf.order(ByteOrder.LITTLE_ENDIAN) writePaddedFile(bf, param.kernel, info2.pageSize) - if (info2.ramdiskLength > 0) { + if (info2.ramdiskLength > 0U) { writePaddedFile(bf, param.ramdisk!!, info2.pageSize) } - if (info2.secondBootloaderLength > 0) { + if (info2.secondBootloaderLength > 0U) { writePaddedFile(bf, param.second!!, info2.pageSize) } - if (info2.recoveryDtboLength > 0) { + if (info2.recoveryDtboLength > 0U) { writePaddedFile(bf, param.dtbo!!, info2.pageSize) } - if (info2.dtbLength > 0) { + if (info2.dtbLength > 0U) { writePaddedFile(bf, param.dtb!!, info2.pageSize) } //write @@ -131,7 +142,7 @@ class Packer { File(cfg.info.output + ".signed2").deleleIfExists() File("${UnifiedConfig.workDir}ramdisk.img").deleleIfExists() - if (info2.ramdiskLength > 0) { + if (info2.ramdiskLength > 0U) { if (File(param.ramdisk).exists() && !File(UnifiedConfig.workDir + "root").exists()) { //do nothing if we have ramdisk.img.gz but no /root log.warn("Use prebuilt ramdisk file: ${param.ramdisk}") @@ -145,7 +156,7 @@ class Packer { //write FileOutputStream(cfg.info.output + ".clear", false).use { fos -> fos.write(encodedHeader) - fos.write(ByteArray(info2.pageSize - encodedHeader.size)) + fos.write(ByteArray(info2.pageSize.toInt() - encodedHeader.size)) } writeData(info2, cfg.info.output) diff --git a/bbootimg/src/main/kotlin/Parser.kt b/bbootimg/src/main/kotlin/Parser.kt index f629f2f..c8ba14d 100644 --- a/bbootimg/src/main/kotlin/Parser.kt +++ b/bbootimg/src/main/kotlin/Parser.kt @@ -56,17 +56,17 @@ class Parser { fun extractBootImg(fileName: String, info2: BootImgInfo) { val param = ParamConfig() - if (info2.kernelLength > 0) { + if (info2.kernelLength > 0U) { Helper.extractFile(fileName, param.kernel, info2.kernelPosition.toLong(), info2.kernelLength.toInt()) - log.info(" kernel dumped to: ${param.kernel}, size=${info2.kernelLength / 1024.0 / 1024.0}MB") + log.info(" kernel dumped to: ${param.kernel}, size=${info2.kernelLength.toInt() / 1024.0 / 1024.0}MB") } else { throw RuntimeException("bad boot image: no kernel found") } - if (info2.ramdiskLength > 0) { + if (info2.ramdiskLength > 0U) { Helper.extractFile(fileName, param.ramdisk!!, info2.ramdiskPosition.toLong(), @@ -78,7 +78,7 @@ class Parser { log.info("no ramdisk found") } - if (info2.secondBootloaderLength > 0) { + if (info2.secondBootloaderLength > 0U) { Helper.extractFile(fileName, param.second!!, info2.secondBootloaderPosition.toLong(), @@ -88,28 +88,28 @@ class Parser { log.info("no second bootloader found") } - if (info2.recoveryDtboLength > 0) { + if (info2.recoveryDtboLength > 0U) { Helper.extractFile(fileName, param.dtbo!!, info2.recoveryDtboPosition.toLong(), info2.recoveryDtboLength.toInt()) log.info("dtbo dumped to ${param.dtbo}") } else { - if (info2.headerVersion > 0) { + if (info2.headerVersion > 0U) { log.info("no recovery dtbo found") } else { log.debug("no recovery dtbo for header v0") } } - if (info2.dtbLength > 0) { + if (info2.dtbLength > 0U) { Helper.extractFile(fileName, param.dtb!!, info2.dtbPosition.toLong(), info2.dtbLength.toInt()) log.info("dtb dumped to ${param.dtb}") } else { - if (info2.headerVersion > 1) { + if (info2.headerVersion > 1U) { log.info("no dtb found") } else { log.debug("no dtb for header v0") diff --git a/bbootimg/src/main/kotlin/Signer.kt b/bbootimg/src/main/kotlin/Signer.kt index b9abe4c..3ac143f 100644 --- a/bbootimg/src/main/kotlin/Signer.kt +++ b/bbootimg/src/main/kotlin/Signer.kt @@ -44,7 +44,7 @@ class Signer { salt = Helper.toHexString(bootDesc.salt), hash_algorithm = bootDesc.hash_algorithm_str, partition_name = bootDesc.partition_name, - rollback_index = ai.header!!.rollback_index, + rollback_index = ai.header!!.rollback_index.toLong(), common_algorithm = alg!!.name, inReleaseString = ai.header!!.release_string) //original signer diff --git a/bbootimg/src/main/kotlin/UnifiedConfig.kt b/bbootimg/src/main/kotlin/UnifiedConfig.kt index 0ecf75e..b7bdcb2 100644 --- a/bbootimg/src/main/kotlin/UnifiedConfig.kt +++ b/bbootimg/src/main/kotlin/UnifiedConfig.kt @@ -24,12 +24,12 @@ data class UnifiedConfig( data class MiscInfo( var output: String = "", - var headerVersion: Int = 0, - var headerSize: Int = 0, + var headerVersion: UInt = 0U, + var headerSize: UInt = 0U, var loadBase: String = "", var tagsOffset: String = "0", var board: String? = null, - var pageSize: Int = 0, + var pageSize: UInt = 0U, var cmdline: String = "", var osVersion: String? = null, var osPatchLevel: String? = null, @@ -39,36 +39,36 @@ data class UnifiedConfig( fun toBootImgInfo(): BootImgInfo { val ret = BootImgInfo() - ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toLong(16) - ret.kernelLength = Integer.decode(this.kernel.size).toLong() + ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toUInt(16) + ret.kernelLength = Integer.decode(this.kernel.size).toUInt() - ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toLong(16) - ret.kernelLength = Integer.decode(this.kernel.size).toLong() + ret.kernelOffset = this.kernel.loadOffset.removePrefix("0x").toUInt(16) + ret.kernelLength = Integer.decode(this.kernel.size).toUInt() this.ramdisk?.let { - ret.ramdiskOffset = it.loadOffset.removePrefix("0x").toLong(16) - ret.ramdiskLength = it.size.removePrefix("0x").toLong(16) + ret.ramdiskOffset = it.loadOffset.removePrefix("0x").toUInt(16) + ret.ramdiskLength = it.size.removePrefix("0x").toUInt(16) } this.secondBootloader?.let { - ret.secondBootloaderOffset = it.loadOffset.removePrefix("0x").toLong(16) - ret.secondBootloaderLength = it.size.removePrefix("0x").toLong(16) + ret.secondBootloaderOffset = it.loadOffset.removePrefix("0x").toUInt(16) + ret.secondBootloaderLength = it.size.removePrefix("0x").toUInt(16) } this.recoveryDtbo?.let { - ret.recoveryDtboOffset = it.loadOffset.removePrefix("0x").toLong(16) - ret.recoveryDtboLength = it.size.removePrefix("0x").toLong(16) + ret.recoveryDtboOffset = it.loadOffset.removePrefix("0x").toULong(16) + ret.recoveryDtboLength = it.size.removePrefix("0x").toUInt(16) } this.dtb?.let { - ret.dtbOffset = it.loadOffset.removePrefix("0x").toLong(16) - ret.dtbLength = it.size.removePrefix("0x").toLong(16) + ret.dtbOffset = it.loadOffset.removePrefix("0x").toULong(16) + ret.dtbLength = it.size.removePrefix("0x").toUInt(16) } - ret.headerSize = this.info.headerSize.toLong() + ret.headerSize = this.info.headerSize ret.headerVersion = this.info.headerVersion this.info.board?.let { ret.board = it } - ret.tagsOffset = this.info.tagsOffset.removePrefix("0x").toLong(16) + ret.tagsOffset = this.info.tagsOffset.removePrefix("0x").toUInt(16) ret.cmdline = this.info.cmdline ret.osVersion = this.info.osVersion ret.osPatchLevel = this.info.osPatchLevel @@ -88,52 +88,52 @@ data class UnifiedConfig( val ret = UnifiedConfig() val param = ParamConfig() ret.kernel.file = param.kernel - ret.kernel.loadOffset = "0x${java.lang.Long.toHexString(info.kernelOffset)}" + ret.kernel.loadOffset = "0x${Integer.toHexString(info.kernelOffset.toInt())}" ret.kernel.size = "0x${Integer.toHexString(info.kernelLength.toInt())}" - ret.kernel.position = "0x${Integer.toHexString(info.kernelPosition)}" + ret.kernel.position = "0x${Integer.toHexString(info.kernelPosition.toInt())}" ret.ramdisk = CommArgs() - ret.ramdisk!!.loadOffset = "0x${java.lang.Long.toHexString(info.ramdiskOffset)}" + ret.ramdisk!!.loadOffset = "0x${Integer.toHexString(info.ramdiskOffset.toInt())}" ret.ramdisk!!.size = "0x${Integer.toHexString(info.ramdiskLength.toInt())}" - ret.ramdisk!!.position = "0x${Integer.toHexString(info.ramdiskPosition)}" - if (info.ramdiskLength > 0) { + ret.ramdisk!!.position = "0x${java.lang.Long.toHexString(info.ramdiskPosition.toInt().toLong())}" + if (info.ramdiskLength > 0U) { ret.ramdisk!!.file = param.ramdisk } ret.secondBootloader = CommArgs() - ret.secondBootloader!!.loadOffset = "0x${java.lang.Long.toHexString(info.secondBootloaderOffset)}" + ret.secondBootloader!!.loadOffset = "0x${Integer.toHexString(info.secondBootloaderOffset.toInt())}" ret.secondBootloader!!.size = "0x${Integer.toHexString(info.secondBootloaderLength.toInt())}" - ret.secondBootloader!!.position = "0x${Integer.toHexString(info.secondBootloaderPosition)}" - if (info.secondBootloaderLength > 0) { + ret.secondBootloader!!.position = "0x${Integer.toHexString(info.secondBootloaderPosition.toInt())}" + if (info.secondBootloaderLength > 0U) { ret.secondBootloader!!.file = param.second } - if (info.headerVersion > 0) { + if (info.headerVersion > 0U) { ret.recoveryDtbo = CommArgs() - if (info.recoveryDtboLength > 0) { + if (info.recoveryDtboLength > 0U) { ret.recoveryDtbo!!.file = param.dtbo } - ret.recoveryDtbo!!.loadOffset = "0x${java.lang.Long.toHexString(info.recoveryDtboOffset)}" + ret.recoveryDtbo!!.loadOffset = "0x${java.lang.Long.toHexString(info.recoveryDtboOffset.toLong())}" ret.recoveryDtbo!!.size = "0x${Integer.toHexString(info.recoveryDtboLength.toInt())}" - ret.recoveryDtbo!!.position = "0x${Integer.toHexString(info.recoveryDtboPosition)}" + ret.recoveryDtbo!!.position = "0x${java.lang.Long.toHexString(info.recoveryDtboPosition.toLong())}" } - if (info.headerVersion > 1) { + if (info.headerVersion > 1U) { ret.dtb = CommArgs() - if (info.dtbLength > 0) { + if (info.dtbLength > 0U) { ret.dtb!!.file = param.dtb } - ret.dtb!!.loadOffset = "0x${java.lang.Long.toHexString(info.dtbOffset)}" + ret.dtb!!.loadOffset = "0x${java.lang.Integer.toHexString(info.dtbOffset.toInt())}" ret.dtb!!.size = "0x${Integer.toHexString(info.dtbLength.toInt())}" - ret.dtb!!.position = "0x${Integer.toHexString(info.dtbPosition)}" + ret.dtb!!.position = "0x${java.lang.Long.toHexString(info.dtbPosition.toLong())}" } //ret.info.output = //unknown - ret.info.headerSize = info.headerSize.toInt() + ret.info.headerSize = info.headerSize ret.info.headerVersion = info.headerVersion ret.info.loadBase = "0x${java.lang.Long.toHexString(0)}" ret.info.board = if (info.board.isBlank()) null else info.board - ret.info.tagsOffset = "0x${java.lang.Long.toHexString(info.tagsOffset)}" + ret.info.tagsOffset = "0x${java.lang.Long.toHexString(info.tagsOffset.toLong())}" ret.info.cmdline = info.cmdline ret.info.osVersion = info.osVersion ret.info.osPatchLevel = info.osPatchLevel diff --git a/bbootimg/src/main/kotlin/avb/AuthBlob.kt b/bbootimg/src/main/kotlin/avb/AuthBlob.kt index 2191ce4..93329f7 100644 --- a/bbootimg/src/main/kotlin/avb/AuthBlob.kt +++ b/bbootimg/src/main/kotlin/avb/AuthBlob.kt @@ -1,7 +1,7 @@ package avb data class AuthBlob( - var offset: Long = 0L, - var size: Long = 0L, + var offset: ULong = 0U, + var size: ULong = 0U, var hash: String? = null, var signature: String? = null) \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/AuxBlob.kt b/bbootimg/src/main/kotlin/avb/AuxBlob.kt index 1b98b7b..bd1cc59 100644 --- a/bbootimg/src/main/kotlin/avb/AuxBlob.kt +++ b/bbootimg/src/main/kotlin/avb/AuxBlob.kt @@ -3,7 +3,7 @@ package avb import avb.alg.Algorithm import avb.desc.* import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import org.junit.Assert import org.slf4j.LoggerFactory import java.nio.file.Files @@ -56,7 +56,7 @@ data class AuxBlob( this.pubkey?.let { sumOfSize += it.pubkey.size } this.pubkeyMeta?.let { sumOfSize += it.pkmd.size } val auxSize = Helper.round_to_multiple(sumOfSize.toLong(), 64) - return Struct("${auxSize}b").pack( + return Struct3("${auxSize}b").pack( Helper.joinWithNulls(encodedDesc, this.pubkey?.pubkey, this.pubkeyMeta?.pkmd)) } diff --git a/bbootimg/src/main/kotlin/avb/Blob.kt b/bbootimg/src/main/kotlin/avb/Blob.kt index ab3fc35..de951cb 100644 --- a/bbootimg/src/main/kotlin/avb/Blob.kt +++ b/bbootimg/src/main/kotlin/avb/Blob.kt @@ -2,7 +2,7 @@ package avb import avb.alg.Algorithms import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import org.slf4j.LoggerFactory import java.security.MessageDigest @@ -15,7 +15,7 @@ class Blob { val auxSize = Helper.round_to_multiple( (encodedDesc.size + encodedKey.size + pkmdBlob.size).toLong(), 64) - return Struct("${auxSize}b").pack(Helper.join(encodedDesc, encodedKey, pkmdBlob)) + return Struct3("${auxSize}b").pack(Helper.join(encodedDesc, encodedKey, pkmdBlob)) } fun getAuthBlob(header_data_blob: ByteArray, @@ -40,7 +40,7 @@ class Blob { binarySignature = Helper.rawSign(alg.defaultKey.replace(".pem", ".pk8"), Helper.join(alg.padding, binaryHash)) } val authData = Helper.join(binaryHash, binarySignature) - return Helper.join(authData, Struct("${authBlockSize - authData.size}x").pack(0)) + return Helper.join(authData, Struct3("${authBlockSize - authData.size}x").pack(0)) } } } diff --git a/bbootimg/src/main/kotlin/avb/Footer.kt b/bbootimg/src/main/kotlin/avb/Footer.kt index c918bb7..2d60b79 100644 --- a/bbootimg/src/main/kotlin/avb/Footer.kt +++ b/bbootimg/src/main/kotlin/avb/Footer.kt @@ -1,6 +1,6 @@ package avb -import cfig.io.Struct +import cfig.io.Struct3 import org.junit.Assert import java.io.InputStream /* @@ -22,41 +22,41 @@ https://github.com/cfig/Android_boot_image_editor/blob/master/doc/layout.md#32-a */ data class Footer constructor( - var versionMajor: Long = FOOTER_VERSION_MAJOR, - var versionMinor: Long = FOOTER_VERSION_MINOR, - var originalImageSize: Long = 0L, - var vbMetaOffset: Long = 0L, - var vbMetaSize: Long = 0L + var versionMajor: UInt = FOOTER_VERSION_MAJOR, + var versionMinor: UInt = FOOTER_VERSION_MINOR, + var originalImageSize: ULong = 0U, + var vbMetaOffset: ULong = 0U, + var vbMetaSize: ULong = 0U ) { companion object { const val MAGIC = "AVBf" const val SIZE = 64 private const val RESERVED = 28 - const val FOOTER_VERSION_MAJOR = 1L - const val FOOTER_VERSION_MINOR = 0L + const val FOOTER_VERSION_MAJOR = 1U + const val FOOTER_VERSION_MINOR = 0U private const val FORMAT_STRING = "!4s2L3Q${RESERVED}x" init { - Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize()) + Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize()) } } @Throws(IllegalArgumentException::class) constructor(iS: InputStream) : this() { - val info = Struct(FORMAT_STRING).unpack(iS) + val info = Struct3(FORMAT_STRING).unpack(iS) Assert.assertEquals(7, info.size) - if (!MAGIC.toByteArray().contentEquals(info[0] as ByteArray)) { + if (MAGIC != (info[0] as String)) { throw IllegalArgumentException("stream doesn't look like valid AVB Footer") } - versionMajor = info[1] as Long - versionMinor = info[2] as Long - originalImageSize = info[3] as Long - vbMetaOffset = info[4] as Long - vbMetaSize = info[5] as Long + versionMajor = info[1] as UInt + versionMinor = info[2] as UInt + originalImageSize = info[3] as ULong + vbMetaOffset = info[4] as ULong + vbMetaSize = info[5] as ULong } fun encode(): ByteArray { - return Struct(FORMAT_STRING).pack(Footer.MAGIC.toByteArray(), + return Struct3(FORMAT_STRING).pack(MAGIC, this.versionMajor, this.versionMinor, this.originalImageSize, diff --git a/bbootimg/src/main/kotlin/avb/Header.kt b/bbootimg/src/main/kotlin/avb/Header.kt index 6562390..3ecc56f 100644 --- a/bbootimg/src/main/kotlin/avb/Header.kt +++ b/bbootimg/src/main/kotlin/avb/Header.kt @@ -2,61 +2,61 @@ package avb import cfig.Avb import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import org.junit.Assert import java.io.InputStream //avbtool::AvbVBMetaHeader data class Header( - var required_libavb_version_major: Int = Avb.AVB_VERSION_MAJOR, - var required_libavb_version_minor: Int = 0, - var authentication_data_block_size: Long = 0L, - var auxiliary_data_block_size: Long = 0L, - var algorithm_type: Long = 0L, - var hash_offset: Long = 0L, - var hash_size: Long = 0L, - var signature_offset: Long = 0L, - var signature_size: Long = 0L, - var public_key_offset: Long = 0L, - var public_key_size: Long = 0L, - var public_key_metadata_offset: Long = 0L, - var public_key_metadata_size: Long = 0L, - var descriptors_offset: Long = 0L, - var descriptors_size: Long = 0L, - var rollback_index: Long = 0L, - var flags: Long = 0, + var required_libavb_version_major: UInt = Avb.AVB_VERSION_MAJOR, + var required_libavb_version_minor: UInt = 0U, + var authentication_data_block_size: ULong = 0U, + var auxiliary_data_block_size: ULong = 0U, + var algorithm_type: UInt = 0U, + var hash_offset: ULong = 0U, + var hash_size: ULong = 0U, + var signature_offset: ULong = 0U, + var signature_size: ULong = 0U, + var public_key_offset: ULong = 0U, + var public_key_size: ULong = 0U, + var public_key_metadata_offset: ULong = 0U, + var public_key_metadata_size: ULong = 0U, + var descriptors_offset: ULong = 0U, + var descriptors_size: ULong = 0U, + var rollback_index: ULong = 0U, + var flags: UInt = 0U, var release_string: String = "avbtool ${Avb.AVB_VERSION_MAJOR}.${Avb.AVB_VERSION_MINOR}.${Avb.AVB_VERSION_SUB}") { @Throws(IllegalArgumentException::class) constructor(iS: InputStream) : this() { - val info = Struct(FORMAT_STRING).unpack(iS) + val info = Struct3(FORMAT_STRING).unpack(iS) Assert.assertEquals(22, info.size) - if (!(info[0] as ByteArray).contentEquals(magic.toByteArray())) { + if (info[0] != magic) { throw IllegalArgumentException("stream doesn't look like valid VBMeta Header") } - this.required_libavb_version_major = (info[1] as Long).toInt() - this.required_libavb_version_minor = (info[2] as Long).toInt() - this.authentication_data_block_size = info[3] as Long - this.auxiliary_data_block_size = info[4] as Long - this.algorithm_type = info[5] as Long - this.hash_offset = info[6] as Long - this.hash_size = info[7] as Long - this.signature_offset = info[8] as Long - this.signature_size = info[9] as Long - this.public_key_offset = info[10] as Long - this.public_key_size = info[11] as Long - this.public_key_metadata_offset = info[12] as Long - this.public_key_metadata_size = info[13] as Long - this.descriptors_offset = info[14] as Long - this.descriptors_size = info[15] as Long - this.rollback_index = info[16] as Long - this.flags = info[17] as Long + this.required_libavb_version_major = info[1] as UInt + this.required_libavb_version_minor = info[2] as UInt + this.authentication_data_block_size = info[3] as ULong + this.auxiliary_data_block_size = info[4] as ULong + this.algorithm_type = info[5] as UInt + this.hash_offset = info[6] as ULong + this.hash_size = info[7] as ULong + this.signature_offset = info[8] as ULong + this.signature_size = info[9] as ULong + this.public_key_offset = info[10] as ULong + this.public_key_size = info[11] as ULong + this.public_key_metadata_offset = info[12] as ULong + this.public_key_metadata_size = info[13] as ULong + this.descriptors_offset = info[14] as ULong + this.descriptors_size = info[15] as ULong + this.rollback_index = info[16] as ULong + this.flags = info[17] as UInt //padding: info[18] - this.release_string = Helper.toCString(info[19] as ByteArray) + this.release_string = info[19] as String } fun encode(): ByteArray { - return Struct(FORMAT_STRING).pack( - magic.toByteArray(), + return Struct3(FORMAT_STRING).pack( + magic, this.required_libavb_version_major, this.required_libavb_version_minor, this.authentication_data_block_size, this.auxiliary_data_block_size, this.algorithm_type, @@ -68,12 +68,12 @@ data class Header( this.rollback_index, this.flags, null, //${REVERSED0}x - this.release_string.toByteArray(), //47s + this.release_string, //47s null, //x null) //${REVERSED}x } - fun bump_required_libavb_version_minor(minor: Int) { + fun bump_required_libavb_version_minor(minor: UInt) { this.required_libavb_version_minor = maxOf(required_libavb_version_minor, minor) } @@ -85,7 +85,7 @@ data class Header( const val FORMAT_STRING = ("!4s2L2QL11QL${REVERSED0}x47sx" + "${REVERSED}x") init { - Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize()) + Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize()) } } } \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt b/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt index e407336..5c77c3e 100644 --- a/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt +++ b/bbootimg/src/main/kotlin/avb/alg/Algorithms.kt @@ -1,6 +1,6 @@ package avb.alg -import cfig.io.Struct +import cfig.io.Struct3 class Algorithms { companion object { @@ -28,7 +28,7 @@ class Algorithms { hash_num_bytes = 32, signature_num_bytes = 256, public_key_num_bytes = 8 + 2 * 2048 / 8, - padding = Struct("2b202x1b19b").pack( + padding = Struct3("2b202x1b19B").pack( byteArrayOf(0x00, 0x01), 0xff, byteArrayOf(0x00), @@ -44,7 +44,7 @@ class Algorithms { hash_num_bytes = 32, signature_num_bytes = 512, public_key_num_bytes = 8 + 2 * 4096 / 8, - padding = Struct("2b458x1x19b").pack( + padding = Struct3("2b458x1x19B").pack( byteArrayOf(0x00, 0x01), 0xff, 0x00, @@ -62,7 +62,7 @@ class Algorithms { hash_num_bytes = 32, signature_num_bytes = 1024, public_key_num_bytes = 8 + 2 * 8192 / 8, - padding = Struct("2b970x1x19b").pack( + padding = Struct3("2b970x1x19B").pack( intArrayOf(0x00, 0x01), 0xff, 0x00, @@ -78,7 +78,7 @@ class Algorithms { hash_num_bytes = 64, signature_num_bytes = 256, public_key_num_bytes = 8 + 2 * 2048 / 8, - padding = Struct("2b170x1x19b").pack( + padding = Struct3("2b170x1x19B").pack( intArrayOf(0x00, 0x01), 0xff, 0x00, @@ -94,7 +94,7 @@ class Algorithms { hash_num_bytes = 64, signature_num_bytes = 512, public_key_num_bytes = 8 + 2 * 4096 / 8, - padding = Struct("2b426x1x19b").pack( + padding = Struct3("2b426x1x19B").pack( intArrayOf(0x00, 0x01), 0xff, 0x00, @@ -111,7 +111,7 @@ class Algorithms { signature_num_bytes = 1024, public_key_num_bytes = 8 + 2 * 8192 / 8, - padding = Struct("2b938x1x19b").pack( + padding = Struct3("2b938x1x19B").pack( intArrayOf(0x00, 0x01), 0xff, 0x00, diff --git a/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt index 6b5243b..60daabd 100644 --- a/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/ChainPartitionDescriptor.kt @@ -1,61 +1,61 @@ package avb.desc import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import java.io.InputStream import java.security.MessageDigest import java.util.* class ChainPartitionDescriptor( - var rollback_index_location: Long = 0, - var partition_name_len: Long = 0, - var public_key_len: Long = 0, + var rollback_index_location: UInt = 0U, + var partition_name_len: UInt = 0U, + var public_key_len: UInt = 0U, var partition_name: String = "", var pubkey: ByteArray = byteArrayOf(), var pubkey_sha1: String = "" -) : Descriptor(TAG, 0, 0) { +) : Descriptor(TAG, 0U, 0) { override fun encode(): ByteArray { - this.partition_name_len = this.partition_name.length.toLong() - this.public_key_len = this.pubkey.size.toLong() - this.num_bytes_following = SIZE + this.partition_name_len + this.public_key_len - 16 - val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following, 8) + this.partition_name_len = this.partition_name.length.toUInt() + this.public_key_len = this.pubkey.size.toUInt() + this.num_bytes_following = (SIZE.toUInt() + this.partition_name_len + this.public_key_len - 16U).toULong() + val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8).toULong() val padding_size = nbf_with_padding - this.num_bytes_following - val desc = Struct(FORMAT_STRING + "${RESERVED}s").pack( + val desc = Struct3(FORMAT_STRING + "${RESERVED}x").pack( TAG, - nbf_with_padding, + nbf_with_padding.toULong(), this.rollback_index_location, - this.partition_name.length, + this.partition_name.length.toUInt(), this.public_key_len, null) - val padding = Struct("${padding_size}s").pack(null) + val padding = Struct3("${padding_size}x").pack(null) return Helper.join(desc, this.partition_name.toByteArray(), this.pubkey, padding) } companion object { - const val TAG = 4L + const val TAG: ULong = 4U const val RESERVED = 64 const val SIZE = 28L + RESERVED const val FORMAT_STRING = "!2Q3L" } constructor(data: InputStream, seq: Int = 0) : this() { - if (SIZE - RESERVED != Struct(FORMAT_STRING).calcSize().toLong()) { + if (SIZE - RESERVED != Struct3(FORMAT_STRING).calcSize()!!.toLong()) { throw RuntimeException() } this.sequence = seq - val info = Struct(FORMAT_STRING + "${RESERVED}s").unpack(data) - this.tag = info[0] as Long - this.num_bytes_following = info[1] as Long - this.rollback_index_location = info[2] as Long - this.partition_name_len = info[3] as Long - this.public_key_len = info[4] as Long + val info = Struct3(FORMAT_STRING + "${RESERVED}s").unpack(data) + this.tag = info[0] as ULong + this.num_bytes_following = info[1] as ULong + this.rollback_index_location = info[2] as UInt + this.partition_name_len = info[3] as UInt + this.public_key_len = info[4] as UInt val expectedSize = Helper.round_to_multiple( - SIZE - 16 + this.partition_name_len + this.public_key_len, 8) - if (this.tag != TAG || this.num_bytes_following != expectedSize) { + SIZE.toUInt() - 16U + this.partition_name_len + this.public_key_len, 8U) + if (this.tag != TAG || this.num_bytes_following != expectedSize.toULong()) { throw IllegalArgumentException("Given data does not look like a chain/delegation descriptor") } - val info2 = Struct("${this.partition_name_len}s${this.public_key_len}s").unpack(data) - this.partition_name = Helper.toCString(info2[0] as ByteArray) + val info2 = Struct3("${this.partition_name_len}s${this.public_key_len}b").unpack(data) + this.partition_name = info2[0] as String this.pubkey = info2[1] as ByteArray val md = MessageDigest.getInstance("SHA1") md.update(this.pubkey) diff --git a/bbootimg/src/main/kotlin/avb/desc/Descriptor.kt b/bbootimg/src/main/kotlin/avb/desc/Descriptor.kt index 1cfba41..1b06462 100755 --- a/bbootimg/src/main/kotlin/avb/desc/Descriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/Descriptor.kt @@ -1,5 +1,5 @@ package avb.desc -abstract class Descriptor(var tag: Long, var num_bytes_following: Long, var sequence: Int = 0) { +abstract class Descriptor(var tag: ULong, var num_bytes_following: ULong, var sequence: Int = 0) { abstract fun encode(): ByteArray } \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt index 8efec44..b97d38e 100755 --- a/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/HashDescriptor.kt @@ -1,50 +1,51 @@ package avb.desc import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import org.junit.Assert import java.io.File import java.io.InputStream import java.security.MessageDigest -class HashDescriptor(var image_size: Long = 0L, - var hash_algorithm: ByteArray = byteArrayOf(), +class HashDescriptor(var image_size: ULong = 0U, + var hash_algorithm: String = "", var hash_algorithm_str: String = "", - var partition_name_len: Long = 0L, - var salt_len: Long = 0L, - var digest_len: Long = 0L, - var flags: Long = 0L, + var partition_name_len: UInt = 0U, + var salt_len: UInt = 0U, + var digest_len: UInt = 0U, + var flags: UInt = 0U, var partition_name: String = "", var salt: ByteArray = byteArrayOf(), - var digest: ByteArray = byteArrayOf()) : Descriptor(TAG, 0, 0) { + var digest: ByteArray = byteArrayOf()) : Descriptor(TAG, 0U, 0) { constructor(data: InputStream, seq: Int = 0) : this() { - val info = Struct(FORMAT_STRING).unpack(data) - this.tag = info[0] as Long - this.num_bytes_following = info[1] as Long - this.image_size = info[2] as Long - this.hash_algorithm = info[3] as ByteArray - this.partition_name_len = info[4] as Long - this.salt_len = info[5] as Long - this.digest_len = info[6] as Long - this.flags = info[7] as Long + val info = Struct3(FORMAT_STRING).unpack(data) + this.tag = info[0] as ULong + this.num_bytes_following = info[1] as ULong + this.image_size = info[2] as ULong + this.hash_algorithm = info[3] as String + this.partition_name_len = info[4] as UInt + this.salt_len = info[5] as UInt + this.digest_len = info[6] as UInt + this.flags = info[7] as UInt this.sequence = seq - val expectedSize = Helper.round_to_multiple(SIZE - 16 + partition_name_len + salt_len + digest_len, 8) + val expectedSize = Helper.round_to_multiple( + SIZE - 16 + (partition_name_len + salt_len + digest_len).toLong(), 8).toULong() if (this.tag != TAG || expectedSize != this.num_bytes_following) { throw IllegalArgumentException("Given data does not look like a |hash| descriptor") } - val payload = Struct("${this.partition_name_len}s${this.salt_len}b${this.digest_len}b").unpack(data) + val payload = Struct3("${this.partition_name_len}s${this.salt_len}b${this.digest_len}b").unpack(data) Assert.assertEquals(3, payload.size) - this.partition_name = Helper.toCString(payload[0] as ByteArray) + this.partition_name = payload[0] as String this.salt = payload[1] as ByteArray this.digest = payload[2] as ByteArray - this.hash_algorithm_str = Helper.toCString(this.hash_algorithm) + this.hash_algorithm_str = this.hash_algorithm } override fun encode(): ByteArray { val payload_bytes_following = SIZE + this.partition_name.length + this.salt.size + this.digest.size - 16L - this.num_bytes_following = Helper.round_to_multiple(payload_bytes_following, 8) - val padding_size = num_bytes_following - payload_bytes_following - val desc = Struct(FORMAT_STRING).pack( + this.num_bytes_following = Helper.round_to_multiple(payload_bytes_following, 8).toULong() + val padding_size = num_bytes_following - payload_bytes_following.toUInt() + val desc = Struct3(FORMAT_STRING).pack( TAG, this.num_bytes_following, this.image_size, @@ -54,7 +55,7 @@ class HashDescriptor(var image_size: Long = 0L, this.digest.size, this.flags, null) - val padding = Struct("${padding_size}x").pack(null) + val padding = Struct3("${padding_size}x").pack(null) return Helper.join(desc, partition_name.toByteArray(), this.salt, this.digest, padding) } @@ -66,13 +67,13 @@ class HashDescriptor(var image_size: Long = 0L, } companion object { - const val TAG = 2L + const val TAG: ULong = 2U private const val RESERVED = 60 private const val SIZE = 72 + RESERVED - private const val FORMAT_STRING = "!3Q32s4L${RESERVED}s" + private const val FORMAT_STRING = "!3Q32s4L${RESERVED}x" } override fun toString(): String { - return "HashDescriptor(TAG=$TAG, image_size=$image_size, hash_algorithm=${Helper.toCString(hash_algorithm)}, flags=$flags, partition_name='$partition_name', salt=${Helper.toHexString(salt)}, digest=${Helper.toHexString(digest)})" + return "HashDescriptor(TAG=$TAG, image_size=$image_size, hash_algorithm=$hash_algorithm, flags=$flags, partition_name='$partition_name', salt=${Helper.toHexString(salt)}, digest=${Helper.toHexString(digest)})" } } \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt index 4c635b9..121e69a 100755 --- a/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/HashTreeDescriptor.kt @@ -1,63 +1,63 @@ package avb.desc import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import org.slf4j.LoggerFactory import java.io.InputStream import java.util.* class HashTreeDescriptor( - var dm_verity_version: Long = 0L, - var image_size: Long = 0L, - var tree_offset: Long = 0L, - var tree_size: Long = 0L, - var data_block_size: Long = 0L, - var hash_block_size: Long = 0L, - var fec_num_roots: Long = 0L, - var fec_offset: Long = 0L, - var fec_size: Long = 0L, + var dm_verity_version: UInt = 0u, + var image_size: ULong = 0UL, + var tree_offset: ULong = 0UL, + var tree_size: ULong = 0UL, + var data_block_size: UInt = 0U, + var hash_block_size: UInt = 0U, + var fec_num_roots: UInt = 0U, + var fec_offset: ULong = 0U, + var fec_size: ULong = 0U, var hash_algorithm: String = "", var partition_name: String = "", var salt: ByteArray = byteArrayOf(), var root_digest: ByteArray = byteArrayOf(), - var flags: Long = 0L) : Descriptor(TAG, 0, 0) { + var flags: UInt = 0U) : Descriptor(TAG, 0U, 0) { constructor(data: InputStream, seq: Int = 0) : this() { this.sequence = seq - val info = Struct(FORMAT_STRING).unpack(data) - this.tag = info[0] as Long - this.num_bytes_following = info[1] as Long - this.dm_verity_version = info[2] as Long - this.image_size = info[3] as Long - this.tree_offset = info[4] as Long - this.tree_size = info[5] as Long - this.data_block_size = info[6] as Long - this.hash_block_size = info[7] as Long - this.fec_num_roots = info[8] as Long - this.fec_offset = info[9] as Long - this.fec_size = info[10] as Long - this.hash_algorithm = Helper.toCString(info[11] as ByteArray) - val partition_name_len = info[12] as Long - val salt_len = info[13] as Long - val root_digest_len = info[14] as Long - this.flags = info[15] as Long - val expectedSize = Helper.round_to_multiple(SIZE - 16 + partition_name_len + salt_len + root_digest_len, 8) - if (this.tag != TAG || this.num_bytes_following != expectedSize) { + val info = Struct3(FORMAT_STRING).unpack(data) + this.tag = info[0] as ULong + this.num_bytes_following = info[1] as ULong + this.dm_verity_version = info[2] as UInt + this.image_size = info[3] as ULong + this.tree_offset = info[4] as ULong + this.tree_size = info[5] as ULong + this.data_block_size = info[6] as UInt + this.hash_block_size = info[7] as UInt + this.fec_num_roots = info[8] as UInt + this.fec_offset = info[9] as ULong + this.fec_size = info[10] as ULong + this.hash_algorithm = info[11] as String + val partition_name_len = info[12] as UInt + val salt_len = info[13] as UInt + val root_digest_len = info[14] as UInt + this.flags = info[15] as UInt + val expectedSize = Helper.round_to_multiple(SIZE.toUInt() - 16U + partition_name_len + salt_len + root_digest_len, 8U) + if (this.tag != TAG || this.num_bytes_following != expectedSize.toULong()) { throw IllegalArgumentException("Given data does not look like a hashtree descriptor") } - val info2 = Struct("${partition_name_len}s${salt_len}s${root_digest_len}s").unpack(data) - this.partition_name = Helper.toCString(info2[0] as ByteArray) + val info2 = Struct3("${partition_name_len}s${salt_len}b${root_digest_len}b").unpack(data) + this.partition_name = info2[0] as String this.salt = info2[1] as ByteArray this.root_digest = info2[2] as ByteArray } override fun encode(): ByteArray { - this.num_bytes_following = SIZE + this.partition_name.length + this.salt.size + this.root_digest.size - 16 - val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following, 8) - val padding_size = nbf_with_padding - this.num_bytes_following - val desc = Struct(FORMAT_STRING).pack( + this.num_bytes_following = (SIZE + this.partition_name.length + this.salt.size + this.root_digest.size - 16).toULong() + val nbf_with_padding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8) + val padding_size = nbf_with_padding - this.num_bytes_following.toLong() + val desc = Struct3(FORMAT_STRING).pack( TAG, - nbf_with_padding, + nbf_with_padding.toULong(), this.dm_verity_version, this.image_size, this.tree_offset, @@ -67,13 +67,13 @@ class HashTreeDescriptor( this.fec_num_roots, this.fec_offset, this.fec_size, - this.hash_algorithm.toByteArray(), + this.hash_algorithm, this.partition_name.length, this.salt.size, this.root_digest.size, this.flags, null) - val padding = Struct("${padding_size}x").pack(null) + val padding = Struct3("${padding_size}x").pack(null) return Helper.join(desc, this.partition_name.toByteArray(), this.salt, this.root_digest, padding) } @@ -82,10 +82,10 @@ class HashTreeDescriptor( } companion object { - const val TAG = 1L + const val TAG: ULong = 1U private const val RESERVED = 60L private const val SIZE = 120 + RESERVED - private const val FORMAT_STRING = "!2QL3Q3L2Q32s4L${RESERVED}s" + private const val FORMAT_STRING = "!2QL3Q3L2Q32s4L${RESERVED}x" private val log = LoggerFactory.getLogger(HashTreeDescriptor::class.java) } } \ No newline at end of file diff --git a/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt index bdd1894..a33c51f 100755 --- a/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/KernelCmdlineDescriptor.kt @@ -1,51 +1,51 @@ package avb.desc import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import org.junit.Assert import java.io.InputStream class KernelCmdlineDescriptor( - var flags: Long = 0, - var cmdlineLength: Long = 0, - var cmdline: String = "") : Descriptor(TAG, 0, 0) { + var flags: UInt = 0U, + var cmdlineLength: UInt = 0U, + var cmdline: String = "") : Descriptor(TAG, 0U, 0) { @Throws(IllegalArgumentException::class) constructor(data: InputStream, seq: Int = 0) : this() { - val info = Struct(FORMAT_STRING).unpack(data) - this.tag = info[0] as Long - this.num_bytes_following = info[1] as Long - this.flags = info[2] as Long - this.cmdlineLength = info[3] as Long + val info = Struct3(FORMAT_STRING).unpack(data) + this.tag = info[0] as ULong + this.num_bytes_following = info[1] as ULong + this.flags = info[2] as UInt + this.cmdlineLength = info[3] as UInt this.sequence = seq - val expectedSize = Helper.round_to_multiple(SIZE - 16 + this.cmdlineLength, 8) - if ((this.tag != TAG) || (this.num_bytes_following != expectedSize)) { + val expectedSize = Helper.round_to_multiple(SIZE.toUInt() - 16U + this.cmdlineLength, 8U) + if ((this.tag != TAG) || (this.num_bytes_following != expectedSize.toULong())) { throw IllegalArgumentException("Given data does not look like a kernel cmdline descriptor") } - this.cmdline = Helper.toCString(Struct("${this.cmdlineLength}s").unpack(data)[0] as ByteArray) + this.cmdline = Struct3("${this.cmdlineLength}s").unpack(data)[0] as String } override fun encode(): ByteArray { val num_bytes_following = SIZE - 16 + cmdline.toByteArray().size val nbf_with_padding = Helper.round_to_multiple(num_bytes_following.toLong(), 8) val padding_size = nbf_with_padding - num_bytes_following - val desc = Struct(FORMAT_STRING).pack( + val desc = Struct3(FORMAT_STRING).pack( TAG, nbf_with_padding, this.flags, - cmdline.toByteArray().size) - val padding = Struct("${padding_size}x").pack(null) + cmdline.length) + val padding = Struct3("${padding_size}x").pack(null) return Helper.join(desc, cmdline.toByteArray(), padding) } companion object { - const val TAG = 3L + const val TAG: ULong = 3U const val SIZE = 24 const val FORMAT_STRING = "!2Q2L" //# tag, num_bytes_following (descriptor header), flags, cmdline length (bytes) const val flagHashTreeEnabled = 1 const val flagHashTreeDisabled = 2 init { - Assert.assertEquals(SIZE, Struct(FORMAT_STRING).calcSize()) + Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize()) } } -} \ No newline at end of file +} diff --git a/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt index 189b275..273a8cf 100755 --- a/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/PropertyDescriptor.kt @@ -1,21 +1,21 @@ package avb.desc import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import java.io.InputStream class PropertyDescriptor( - private var key: String = "", - private var value: String = "") : Descriptor(TAG, 0, 0) { + var key: String = "", + var value: String = "") : Descriptor(TAG, 0U, 0) { override fun encode(): ByteArray { - if (SIZE != Struct(FORMAT_STRING).calcSize()) { + if (SIZE != Struct3(FORMAT_STRING).calcSize()) { throw RuntimeException() } - this.num_bytes_following = SIZE + this.key.length + this.value.length + 2 - 16L - val nbfWithPadding = Helper.round_to_multiple(this.num_bytes_following, 8) + this.num_bytes_following = (SIZE + this.key.length + this.value.length + 2 - 16L).toULong() + val nbfWithPadding = Helper.round_to_multiple(this.num_bytes_following.toLong(), 8).toULong() val paddingSize = nbfWithPadding - num_bytes_following - val padding = Struct("${paddingSize}x").pack(0) - val desc = Struct(FORMAT_STRING).pack( + val padding = Struct3("${paddingSize}x").pack(0) + val desc = Struct3(FORMAT_STRING).pack( TAG, nbfWithPadding, this.key.length, @@ -27,24 +27,24 @@ class PropertyDescriptor( } constructor(data: InputStream, seq: Int = 0) : this() { - val info = Struct(FORMAT_STRING).unpack(data) - this.tag = info[0] as Long - this.num_bytes_following = info[1] as Long + val info = Struct3(FORMAT_STRING).unpack(data) + this.tag = info[0] as ULong + this.num_bytes_following = info[1] as ULong val keySize = info[2] as Long val valueSize = info[3] as Long val expectedSize = Helper.round_to_multiple(SIZE - 16 + keySize + 1 + valueSize + 1, 8) - if (this.tag != TAG || expectedSize != this.num_bytes_following) { + if (this.tag != TAG || expectedSize.toULong() != this.num_bytes_following) { throw IllegalArgumentException("Given data does not look like a |property| descriptor") } this.sequence = seq - val info2 = Struct("${keySize}sx${valueSize}s").unpack(data) + val info2 = Struct3("${keySize}sx${valueSize}s").unpack(data) this.key = String(info2[0] as ByteArray) this.value = String(info2[2] as ByteArray) } companion object { - const val TAG = 0L + const val TAG: ULong = 0U const val SIZE = 32 const val FORMAT_STRING = "!4Q" } diff --git a/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt b/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt index 041daa4..7af0f58 100755 --- a/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt +++ b/bbootimg/src/main/kotlin/avb/desc/UnknownDescriptor.kt @@ -1,20 +1,20 @@ package avb.desc import cfig.Helper -import cfig.io.Struct +import cfig.io.Struct3 import org.apache.commons.codec.binary.Hex import org.junit.Assert import org.slf4j.LoggerFactory import java.io.ByteArrayInputStream import java.io.InputStream -class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0, 0) { +class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0U, 0U, 0) { @Throws(IllegalArgumentException::class) constructor(stream: InputStream, seq: Int = 0) : this() { this.sequence = seq - val info = Struct(FORMAT).unpack(stream) - this.tag = info[0] as Long - this.num_bytes_following = info[1] as Long + val info = Struct3(FORMAT).unpack(stream) + this.tag = info[0] as ULong + this.num_bytes_following = info[1] as ULong log.debug("UnknownDescriptor: tag = $tag, len = ${this.num_bytes_following}") this.data = ByteArray(this.num_bytes_following.toInt()) if (this.num_bytes_following.toInt() != stream.read(data)) { @@ -23,7 +23,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0, } override fun encode(): ByteArray { - return Helper.join(Struct(FORMAT).pack(this.tag, this.data.size.toLong()), data) + return Helper.join(Struct3(FORMAT).pack(this.tag, this.data.size.toLong()), data) } override fun toString(): String { @@ -31,20 +31,20 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0, } fun analyze(): Any { - return when (this.tag) { - 0L -> { + return when (this.tag.toUInt()) { + 0U -> { PropertyDescriptor(ByteArrayInputStream(this.encode()), this.sequence) } - 1L -> { + 1U -> { HashTreeDescriptor(ByteArrayInputStream(this.encode()), this.sequence) } - 2L -> { + 2U -> { HashDescriptor(ByteArrayInputStream(this.encode()), this.sequence) } - 3L -> { + 3U -> { KernelCmdlineDescriptor(ByteArrayInputStream(this.encode()), this.sequence) } - 4L -> { + 4U -> { ChainPartitionDescriptor(ByteArrayInputStream(this.encode()), this.sequence) } else -> { @@ -107,7 +107,7 @@ class UnknownDescriptor(var data: ByteArray = byteArrayOf()) : Descriptor(0, 0, } init { - Assert.assertEquals(SIZE, Struct(FORMAT).calcSize()) + Assert.assertEquals(SIZE, Struct3(FORMAT).calcSize()) } } } diff --git a/bbootimg/src/main/kotlin/bootimg/BootImgHeader.kt b/bbootimg/src/main/kotlin/bootimg/BootImgHeader.kt index 00c02a2..db9201c 100644 --- a/bbootimg/src/main/kotlin/bootimg/BootImgHeader.kt +++ b/bbootimg/src/main/kotlin/bootimg/BootImgHeader.kt @@ -2,8 +2,7 @@ package cfig.bootimg import cfig.Helper import cfig.ParamConfig -import cfig.io.Struct -import com.fasterxml.jackson.databind.ObjectMapper +import cfig.io.Struct3 import org.junit.Assert import org.slf4j.LoggerFactory import java.io.File @@ -15,27 +14,27 @@ import java.security.MessageDigest import java.util.regex.Pattern open class BootImgHeader( - var kernelLength: Long = 0, - var kernelOffset: Long = 0, + var kernelLength: UInt = 0U, + var kernelOffset: UInt = 0U, - var ramdiskLength: Long = 0, - var ramdiskOffset: Long = 0, + var ramdiskLength: UInt = 0U, + var ramdiskOffset: UInt = 0U, - var secondBootloaderLength: Long = 0, - var secondBootloaderOffset: Long = 0, + var secondBootloaderLength: UInt = 0U, + var secondBootloaderOffset: UInt = 0U, - var recoveryDtboLength: Long = 0, - var recoveryDtboOffset: Long = 0, + var recoveryDtboLength: UInt = 0U, + var recoveryDtboOffset: ULong = 0UL,//Q - var dtbLength: Long = 0, - var dtbOffset: Long = 0, + var dtbLength: UInt = 0U, + var dtbOffset: ULong = 0UL,//Q - var tagsOffset: Long = 0, + var tagsOffset: UInt = 0U, - var pageSize: Int = 0, + var pageSize: UInt = 0U, - var headerSize: Long = 0, - var headerVersion: Int = 0, + var headerSize: UInt = 0U, + var headerVersion: UInt = 0U, var board: String = "", @@ -51,40 +50,40 @@ open class BootImgHeader( return } log.warn("BootImgHeader constructor") - val info = Struct(FORMAT_STRING).unpack(iS) + val info = Struct3(FORMAT_STRING).unpack(iS) Assert.assertEquals(20, info.size) - if (!(info[0] as ByteArray).contentEquals(magic.toByteArray())) { + if (info[0] != magic) { throw IllegalArgumentException("stream doesn't look like Android Boot Image Header") } - this.kernelLength = info[1] as Long - this.kernelOffset = info[2] as Long - this.ramdiskLength = info[3] as Long - this.ramdiskOffset = info[4] as Long - this.secondBootloaderLength = info[5] as Long - this.secondBootloaderOffset = info[6] as Long - this.tagsOffset = info[7] as Long - this.pageSize = (info[8] as Long).toInt() - this.headerVersion = (info[9] as Long).toInt() - val osNPatch = (info[10] as Long).toInt() - if (0 != osNPatch) { //treated as 'reserved' in this boot image - this.osVersion = parseOsVersion(osNPatch shr 11) - this.osPatchLevel = parseOsPatchLevel(osNPatch and 0x7ff) + this.kernelLength = info[1] as UInt + this.kernelOffset = info[2] as UInt + this.ramdiskLength = info[3] as UInt + this.ramdiskOffset = info[4] as UInt + this.secondBootloaderLength = info[5] as UInt + this.secondBootloaderOffset = info[6] as UInt + this.tagsOffset = info[7] as UInt + this.pageSize = info[8] as UInt + this.headerVersion = info[9] as UInt + val osNPatch = info[10] as UInt + if (0U != osNPatch) { //treated as 'reserved' in this boot image + this.osVersion = parseOsVersion(osNPatch.toInt() shr 11) + this.osPatchLevel = parseOsPatchLevel((osNPatch and 0x7ff.toUInt()).toInt()) } - this.board = Helper.toCString(info[11] as ByteArray).trim() - this.cmdline = Helper.toCString(info[12] as ByteArray) + Helper.toCString(info[14] as ByteArray) + this.board = info[11] as String + this.cmdline = (info[12] as String) + (info[14] as String) this.hash = info[13] as ByteArray - if (this.headerVersion > 0) { - this.recoveryDtboLength = info[15] as Long - this.recoveryDtboOffset = info[16] as Long + if (this.headerVersion > 0U) { + this.recoveryDtboLength = info[15] as UInt + this.recoveryDtboOffset = info[16] as ULong } - this.headerSize = info[17] as Long + this.headerSize = info[17] as UInt assert(this.headerSize.toInt() in intArrayOf(BOOT_IMAGE_HEADER_V2_SIZE, BOOT_IMAGE_HEADER_V1_SIZE)) - if (this.headerVersion > 1) { - this.dtbLength = info[18] as Long - this.dtbOffset = info[19] as Long + if (this.headerVersion > 1U) { + this.dtbLength = info[18] as UInt + this.dtbOffset = info[19] as ULong } } @@ -181,45 +180,45 @@ open class BootImgHeader( private fun refresh() { val param = ParamConfig() //refresh kernel size - if (0L == this.kernelLength) { + if (0U == this.kernelLength) { throw java.lang.IllegalArgumentException("kernel size can not be 0") } else { - this.kernelLength = File(param.kernel).length() + this.kernelLength = File(param.kernel).length().toUInt() } //refresh ramdisk size - if (0L == this.ramdiskLength) { + if (0U == this.ramdiskLength) { param.ramdisk = null } else { - this.ramdiskLength = File(param.ramdisk).length() + this.ramdiskLength = File(param.ramdisk).length().toUInt() } //refresh second bootloader size - if (0L == this.secondBootloaderLength) { + if (0U == this.secondBootloaderLength) { param.second = null } else { - this.secondBootloaderLength = File(param.second).length() + this.secondBootloaderLength = File(param.second).length().toUInt() } //refresh recovery dtbo size - if (0L == this.recoveryDtboLength) { + if (0U == this.recoveryDtboLength) { param.dtbo = null } else { - this.recoveryDtboLength = File(param.dtbo).length() + this.recoveryDtboLength = File(param.dtbo).length().toUInt() } //refresh recovery dtbo size - if (0L == this.dtbLength) { + if (0U == this.dtbLength) { param.dtb = null } else { - this.dtbLength = File(param.dtb).length() + this.dtbLength = File(param.dtb).length().toUInt() } //refresh image hash val imageId = when (this.headerVersion) { - 0 -> { + 0U -> { hashFileAndSize(param.kernel, param.ramdisk, param.second) } - 1 -> { + 1U -> { hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo) } - 2 -> { + 2U -> { hashFileAndSize(param.kernel, param.ramdisk, param.second, param.dtbo, param.dtb) } else -> { @@ -231,8 +230,8 @@ open class BootImgHeader( fun encode(): ByteArray { this.refresh() - val ret = Struct(FORMAT_STRING).pack( - "ANDROID!".toByteArray(), + val ret = Struct3(FORMAT_STRING).pack( + "ANDROID!", //10I this.kernelLength, this.kernelOffset, @@ -245,28 +244,28 @@ open class BootImgHeader( this.headerVersion, (packOsVersion(this.osVersion) shl 11) or packOsPatchLevel(this.osPatchLevel), //16s - this.board.toByteArray(), + this.board, //512s - this.cmdline.substring(0, minOf(512, this.cmdline.length)).toByteArray(), - //32s + this.cmdline.substring(0, minOf(512, this.cmdline.length)), + //32b this.hash!!, //1024s - if (this.cmdline.length > 512) this.cmdline.substring(512).toByteArray() else byteArrayOf(0), + if (this.cmdline.length > 512) this.cmdline.substring(512) else "", //I this.recoveryDtboLength, //Q - if (this.headerVersion > 0) this.recoveryDtboOffset else 0, + if (this.headerVersion > 0U) this.recoveryDtboOffset else 0, //I when (this.headerVersion) { - 0 -> 0 - 1 -> BOOT_IMAGE_HEADER_V1_SIZE - 2 -> BOOT_IMAGE_HEADER_V2_SIZE + 0U -> 0 + 1U -> BOOT_IMAGE_HEADER_V1_SIZE + 2U -> BOOT_IMAGE_HEADER_V2_SIZE else -> java.lang.IllegalArgumentException("headerVersion ${this.headerVersion} illegal") }, //I this.dtbLength, //Q - if (this.headerVersion > 1) this.dtbOffset else 0 + if (this.headerVersion > 1U) this.dtbOffset else 0 ) return ret } @@ -278,7 +277,7 @@ open class BootImgHeader( "10I" + "16s" + //board name "512s" + //cmdline part 1 - "32s" + //hash digest + "32b" + //hash digest "1024s" + //cmdline part 2 "I" + //dtbo length [v1] "Q" + //dtbo offset [v1] @@ -289,7 +288,7 @@ open class BootImgHeader( const val BOOT_IMAGE_HEADER_V1_SIZE = 1648 init { - Assert.assertEquals(BOOT_IMAGE_HEADER_V2_SIZE, Struct(FORMAT_STRING).calcSize()) + Assert.assertEquals(BOOT_IMAGE_HEADER_V2_SIZE, Struct3(FORMAT_STRING).calcSize()) } } } diff --git a/bbootimg/src/main/kotlin/bootimg/BootImgInfo.kt b/bbootimg/src/main/kotlin/bootimg/BootImgInfo.kt index a63c2ec..561ba58 100644 --- a/bbootimg/src/main/kotlin/bootimg/BootImgInfo.kt +++ b/bbootimg/src/main/kotlin/bootimg/BootImgInfo.kt @@ -7,33 +7,33 @@ import java.io.InputStream class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) { constructor() : this(null) - val kernelPosition: Int + val kernelPosition: UInt get() { return getHeaderSize(this.pageSize) } - val ramdiskPosition: Int + val ramdiskPosition: UInt get() { return (kernelPosition + this.kernelLength + - getPaddingSize(this.kernelLength.toInt(), this.pageSize)).toInt() + getPaddingSize(this.kernelLength, this.pageSize)) } - val secondBootloaderPosition: Int + val secondBootloaderPosition: UInt get() { - return (ramdiskPosition + ramdiskLength + - getPaddingSize(ramdiskLength.toInt(), pageSize)).toInt() + return ramdiskPosition + ramdiskLength + + getPaddingSize(ramdiskLength, pageSize) } - val recoveryDtboPosition: Int + val recoveryDtboPosition: ULong get() { - return (secondBootloaderPosition + secondBootloaderLength + - getPaddingSize(secondBootloaderLength.toInt(), pageSize)).toInt() + return secondBootloaderPosition.toULong() + secondBootloaderLength + + getPaddingSize(secondBootloaderLength, pageSize) } - val dtbPosition: Int + val dtbPosition: ULong get() { - return (recoveryDtboPosition + dtbLength + - getPaddingSize(dtbLength.toInt(), pageSize)).toInt() + return recoveryDtboPosition + recoveryDtboLength + + getPaddingSize(recoveryDtboLength, pageSize) } var signatureType: BootImgInfo.VerifyType? = null @@ -45,6 +45,15 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) { return pad + 1648 } + private fun getHeaderSize(pageSize: UInt): UInt { + val pad = (pageSize - (1648U and (pageSize - 1U))) and (pageSize - 1U) + return pad + 1648U + } + + private fun getPaddingSize(position: UInt, pageSize: UInt): UInt { + return (pageSize - (position and pageSize - 1U)) and (pageSize - 1U) + } + private fun getPaddingSize(position: Int, pageSize: Int): Int { return (pageSize - (position and pageSize - 1)) and (pageSize - 1) } @@ -59,39 +68,39 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) { ret.addArgument(" --kernel ") ret.addArgument(param.kernel) ret.addArgument(" --kernel_offset ") - ret.addArgument("0x" + java.lang.Long.toHexString(kernelOffset)) - if (this.ramdiskLength > 0) { + ret.addArgument("0x" + Integer.toHexString(kernelOffset.toInt())) + if (this.ramdiskLength > 0U) { ret.addArgument(" --ramdisk ") ret.addArgument(param.ramdisk) } ret.addArgument(" --ramdisk_offset ") - ret.addArgument("0x" + java.lang.Long.toHexString(ramdiskOffset)) - if (this.secondBootloaderLength > 0) { + ret.addArgument("0x" + Integer.toHexString(ramdiskOffset.toInt())) + if (this.secondBootloaderLength > 0U) { ret.addArgument(" --second ") ret.addArgument(param.second) } ret.addArgument(" --second_offset ") - ret.addArgument("0x" + java.lang.Long.toHexString(this.secondBootloaderOffset)) + ret.addArgument("0x" + Integer.toHexString(this.secondBootloaderOffset.toInt())) if (!board.isBlank()) { ret.addArgument(" --board ") ret.addArgument(board) } - if (headerVersion > 0) { - if (this.recoveryDtboLength > 0) { + if (headerVersion > 0U) { + if (this.recoveryDtboLength > 0U) { ret.addArgument(" --recovery_dtbo ") ret.addArgument(param.dtbo) } } - if (headerVersion > 1) { - if (this.dtbLength > 0) { + if (headerVersion > 1U) { + if (this.dtbLength > 0U) { ret.addArgument("--dtb ") ret.addArgument(param.dtb) } ret.addArgument("--dtb_offset ") - ret.addArgument("0x" + java.lang.Long.toHexString(this.dtbOffset)) + ret.addArgument("0x" + java.lang.Long.toHexString(this.dtbOffset.toLong())) } ret.addArgument(" --pagesize ") - ret.addArgument(Integer.toString(pageSize)) + ret.addArgument(Integer.toString(pageSize.toInt())) ret.addArgument(" --cmdline ") ret.addArgument(cmdline, false) if (!osVersion.isNullOrBlank()) { @@ -103,7 +112,7 @@ class BootImgInfo(iS: InputStream?) : BootImgHeader(iS) { ret.addArgument(osPatchLevel) } ret.addArgument(" --tags_offset ") - ret.addArgument("0x" + java.lang.Long.toHexString(tagsOffset)) + ret.addArgument("0x" + Integer.toHexString(tagsOffset.toInt())) ret.addArgument(" --id ") ret.addArgument(" --output ") //ret.addArgument("boot.img" + ".google") diff --git a/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt b/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt new file mode 100644 index 0000000..e0efdd5 --- /dev/null +++ b/bbootimg/src/main/kotlin/bootloader_message/BootloaderMsg.kt @@ -0,0 +1,54 @@ +package cfig.bootloader_message + +import cfig.io.Struct3 +import org.junit.Assert +import org.slf4j.LoggerFactory +import java.lang.IllegalStateException + +data class BootloaderMsg( + var command: String = "", + var status: String = "", + var recovery: String = "", + var stage: String = "", + var reserved: ByteArray = byteArrayOf() +) { + companion object { + private const val FORMAT_STRING = "32s32s768s32s1184b" + const val SIZE = 2048 + private val log = LoggerFactory.getLogger("BootloaderMsg") + + init { + Assert.assertEquals(SIZE, Struct3(FORMAT_STRING).calcSize()) + } + + fun writeBootloaderMessage(options: Array) { + val boot = BootloaderMsg() + boot.updateBootloaderMessageInStruct(options) + boot.writeBootloaderMessage() + } + } + + fun writeRebootBootloader() { + if (this.command.isNotBlank()) { + throw IllegalStateException("Bootloader command pending.") + } + this.command = "bootonce-bootloader" + writeBootloaderMessage() + } + + fun writeBootloaderMessage() { + log.info("writing ... $this") + } + + fun updateBootloaderMessageInStruct(options: Array) { + this.command = "boot-recovery" + this.recovery = "recovery" + options.forEach { + this.recovery += if (it.endsWith("\n")) { + it + } else { + it + "\n" + } + } + } +} diff --git a/bbootimg/src/main/kotlin/cfig/io/Struct3.kt b/bbootimg/src/main/kotlin/cfig/io/Struct3.kt new file mode 100644 index 0000000..3f726c9 --- /dev/null +++ b/bbootimg/src/main/kotlin/cfig/io/Struct3.kt @@ -0,0 +1,508 @@ +package cfig.io + +import cfig.Helper +import org.junit.Assert +import org.slf4j.LoggerFactory +import java.io.IOException +import java.io.InputStream +import java.net.URLStreamHandler +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.util.* +import java.util.regex.Pattern + +class Struct3 { + private val log = LoggerFactory.getLogger(Struct3::class.java) + private val formatString: String + private var byteOrder = ByteOrder.LITTLE_ENDIAN + private val formats = ArrayList>() + + enum class Type { + Padding, + } + + constructor(inFormatString: String) { + formatString = inFormatString + val m = Pattern.compile("(\\d*)([a-zA-Z])").matcher(formatString) + + if (formatString.startsWith(">") || formatString.startsWith("!")) { + this.byteOrder = ByteOrder.BIG_ENDIAN + log.debug("Parsing BIG_ENDIAN format: $formatString") + } else if (formatString.startsWith("@") || formatString.startsWith("=")) { + this.byteOrder = ByteOrder.nativeOrder() + log.debug("Parsing native ENDIAN format: $formatString") + } else { + log.debug("Parsing LITTLE_ENDIAN format: $formatString") + } + + while (m.find()) { + var bExploded = false + val multiple = if (m.group(1).isEmpty()) 1 else Integer.decode(m.group(1)) + //item[0]: Type, item[1]: multiple + // if need to expand format items, explode it + // eg: "4L" will be exploded to "1L 1L 1L 1L" + // eg: "10x" won't be exploded, it's still "10x" + val item = arrayOfNulls(2) + + when (m.group(2)) { + //exploded types + "x" -> {//byte 1 + item[0] = Type.Padding + bExploded = true + } + "b" -> {//byte 1 + item[0] = Byte + bExploded = true + } + "B" -> {//UByte 1 + item[0] = UByte + bExploded = true + } + "s" -> {//string + item[0] = String + bExploded = true + } + //combo types, which need to be exploded with multiple=1 + "c" -> {//char 1 + item[0] = Char + bExploded = false + } + "h" -> {//2 + item[0] = Short + } + "H" -> {//2 + item[0] = UShort + } + "i", "l" -> {//4 + item[0] = Int + } + "I", "L" -> {//4 + item[0] = UInt + } + "q" -> {//8 + item[0] = Long + } + "Q" -> {//8 + item[0] = ULong + } + else -> { + throw IllegalArgumentException("type [" + m.group(2) + "] not supported") + } + } + if (bExploded) { + item[1] = multiple + formats.add(item) + } else { + item[1] = 1 + for (i in 0 until multiple) { + formats.add(item) + } + } + } + } + + private fun getFormatInfo(inCursor: Int): String { + return ("type=" + formats.get(inCursor)[0] + ", value=" + formats.get(inCursor)[1]) + } + + fun calcSize(): Int? { + var ret = 0 + for (format in formats) { + when (format[0]) { + Byte, UByte, Char, String, Type.Padding -> { + ret += format[1] as Int + } + Short, UShort -> { + ret += 2 * format[1] as Int + } + Int, UInt -> { + ret += 4 * format[1] as Int + } + Long, ULong -> { + ret += 8 * format[1] as Int + } + else -> { + throw IllegalArgumentException("Class [" + format[0] + "] not supported") + } + } + } + return ret + } + + fun pack(vararg args: Any?): ByteArray { + if (args.size != this.formats.size) { + throw IllegalArgumentException("argument size " + args.size + + " doesn't match format size " + this.formats.size) + } else { + log.debug("byte buffer size: " + this.calcSize()!!) + } + val bf = ByteBuffer.allocate(this.calcSize()!!) + bf.order(this.byteOrder) + var formatCursor = -1 //which format item to handle + for (i in args.indices) { + formatCursor++ + val arg = args[i] + val format2 = formats[i][0] + val size = formats[i][1] as Int + + //x: padding: + // arg == null: + // arg is Byte.class + // arg is Integer.class + if (Type.Padding == format2) { + val b = ByteArray(size) + when (arg) { + null -> Arrays.fill(b, 0.toByte()) + is Byte -> Arrays.fill(b, arg) + is Int -> Arrays.fill(b, arg.toByte()) + else -> throw IllegalArgumentException("Index[" + i + "] Unsupported arg [" + + arg + "] with type [" + formats[i][0] + "]") + } + bf.put(b) + continue + } + + //c: character + if (Char == format2) { + Assert.assertEquals(1, size.toLong()) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Character.class", + arg is Char) + bf.put(getLowerByte(arg as Char)) + continue + } + + //b: byte array + if (Byte == format2) { + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of ByteArray/IntArray", + arg is ByteArray || arg is IntArray) + val argInternal = if (arg is IntArray) { + val arg2: MutableList = mutableListOf() + for (item in arg) { + Assert.assertTrue("$item is not valid Byte", + item in Byte.MIN_VALUE..Byte.MAX_VALUE) + arg2.add(item.toByte()) + } + arg2.toByteArray() + } else { + arg as ByteArray + } + + val paddingSize = size - argInternal.size + Assert.assertTrue("argument size overflow: " + argInternal.size + " > " + size, + paddingSize >= 0) + bf.put(argInternal) + if (paddingSize > 0) { + val padBytes = ByteArray(paddingSize) + Arrays.fill(padBytes, 0.toByte()) + bf.put(padBytes) + log.debug("paddingSize $paddingSize") + } else { + log.debug("paddingSize is zero, perfect match") + } + continue + } + + //B: UByte array + if (UByte == format2) { + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of ByteArray/IntArray", + arg is ByteArray || arg is IntArray || arg is UByteArray) + val argInternal = if (arg is IntArray) { + var arg2: MutableList = mutableListOf() + for (item in arg) { + Assert.assertTrue("$item is not valid UByte", + item in UByte.MIN_VALUE.toInt()..UByte.MAX_VALUE.toInt()) + arg2.add(item.toByte()) + } + arg2.toByteArray() + } else if (arg is UByteArray) { + arg as ByteArray + } else { + arg as ByteArray + } + + val paddingSize = size - argInternal.size + Assert.assertTrue("argument size overflow: " + argInternal.size + " > " + size, + paddingSize >= 0) + bf.put(argInternal) + if (paddingSize > 0) { + val padBytes = ByteArray(paddingSize) + Arrays.fill(padBytes, 0.toByte()) + bf.put(padBytes) + log.debug("paddingSize $paddingSize") + } else { + log.debug("paddingSize is zero, perfect match") + } + continue + } + + //h: Short + if (Short == format2) { + Assert.assertEquals(1, size.toLong()) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Short/Int", + arg is Short || arg is Int) + when (arg) { + is Int -> { + Assert.assertTrue("[$arg] is truncated as type Short.class", + arg in java.lang.Short.MIN_VALUE..java.lang.Short.MAX_VALUE) + bf.putShort(arg.toShort()) + } + is Short -> //instance Short + bf.putShort(arg) + } + continue + } + + //H: UShort + if (UShort == format2) { + Assert.assertEquals(1, size.toLong()) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of UShort/UInt/Int", + arg is UShort || arg is UInt || arg is Int) + when (arg) { + is Int -> { + Assert.assertFalse("[$arg] is truncated as type UShort", + arg < UShort.MIN_VALUE.toInt() || arg > UShort.MAX_VALUE.toInt()) + bf.putShort(arg.toShort()) + } + is UInt -> { + Assert.assertFalse("[$arg] is truncated as type UShort", + arg < UShort.MIN_VALUE || arg > UShort.MAX_VALUE) + bf.putShort(arg.toShort()) + } + is UShort -> bf.putShort(arg.toShort()) + } + continue + } + + //i, l: Int + if (Int == format2) { + Assert.assertEquals(1, size.toLong()) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Int", arg is Int) + bf.putInt(arg as Int) + continue + } + + //I, L: UInt + if (UInt == format2) { + Assert.assertEquals(1, size.toLong()) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of UInt/Int/Long", + arg is UInt || arg is Int || arg is Long) + when (arg) { + is Int -> { + Assert.assertTrue("[$arg] is invalid as type UInt", arg >= 0) + bf.putInt(arg) + } + is UInt -> bf.putInt(arg.toInt()) + is Long -> { + Assert.assertTrue("[$arg] is invalid as type UInt", arg >= 0) + bf.putInt(arg.toInt()) + } + else -> { + Assert.fail("program bug") + } + } + continue + } + + //q: Long + if (Long == format2) { + Assert.assertEquals(1, size.toLong()) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Long/Int", + arg is Long || arg is Int) + when (arg) { + is Long -> bf.putLong(arg) + is Int -> bf.putLong(arg.toLong()) + } + continue + } + + //Q: ULong + if (ULong == format2) { + Assert.assertEquals(1, size.toLong()) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of Int/Long/ULong", + arg is Int || arg is Long || arg is ULong) + when (arg) { + is Int -> { + Assert.assertTrue("[$arg] is invalid as type ULong", arg >= 0) + bf.putLong(arg.toLong()) + } + is Long -> { + Assert.assertTrue("[$arg] is invalid as type ULong", arg >= 0) + bf.putLong(arg) + } + is ULong -> { + bf.putLong(arg.toLong()) + } + } + continue + } + + //s: String + if (String == format2) { + Assert.assertNotNull("arg can not be NULL for String, formatString=$formatString, ${getFormatInfo(formatCursor)}", arg) + Assert.assertTrue("[$arg](${arg!!::class.java}) is NOT instance of String.class, ${getFormatInfo(formatCursor)}", + arg is String) + val paddingSize = size - (arg as String).length + Assert.assertTrue("argument size overflow: " + arg.length + " > " + size, + paddingSize >= 0) + bf.put(arg.toByteArray()) + if (paddingSize > 0) { + val padBytes = ByteArray(paddingSize) + Arrays.fill(padBytes, 0.toByte()) + bf.put(padBytes) + log.debug("paddingSize $paddingSize") + } else { + log.debug("paddingSize is zero, perfect match") + } + continue + } + + throw java.lang.IllegalArgumentException("unrecognized format $format2") + } + log.debug("Pack Result:" + Helper.toHexString(bf.array())) + return bf.array() + } + + @Throws(IOException::class) + fun unpack(iS: InputStream): List<*> { + val ret = ArrayList() + for (format in this.formats) { + //x: padding + //return padding byte + if (format[0] === Type.Padding) { + val multip = format[1] as Int + val data = ByteArray(1) + iS.read(data)//sample the 1st byte + val skipped = iS.skip(multip.toLong() - 1)//skip remaining + Assert.assertEquals(multip.toLong() - 1, skipped) + ret.add(data[0]) + continue + } + + //b: byte array + if (format[0] === Byte) { + val data = ByteArray(format[1] as Int) + Assert.assertEquals(format[1] as Int, iS.read(data)) + ret.add(data) + continue + } + + //B: ubyte array + if (format[0] === UByte) { + val data = ByteArray(format[1] as Int) + Assert.assertEquals(format[1] as Int, iS.read(data)) + val innerData = UByteArray(data.size) + for (i in 0 until data.size) { + innerData[i] = data[i].toUByte() + } + ret.add(innerData) + continue + } + + //char: 1 + if (format[0] === Char) { + val data = ByteArray(format[1] as Int)//now its size is fixed at 1 + Assert.assertEquals(format[1] as Int, iS.read(data)) + ret.add(data[0].toChar()) + continue + } + + //string + if (format[0] === String) { + val data = ByteArray(format[1] as Int) + Assert.assertEquals(format[1] as Int, iS.read(data)) + ret.add(Helper.toCString(data)) + continue + } + + //h: short + if (format[0] === Short) { + val data = ByteArray(2) + Assert.assertEquals(2, iS.read(data).toLong()) + ByteBuffer.allocate(2).let { + it.order(this.byteOrder) + it.put(data) + it.flip() + ret.add(it.short) + } + continue + } + + //H: UShort + if (format[0] === UShort) { + val data = ByteArray(2) + Assert.assertEquals(2, iS.read(data).toLong()) + ByteBuffer.allocate(2).let { + it.order(this.byteOrder) + it.put(data) + it.flip() + ret.add(it.short.toUShort()) + } + continue + } + + //i, l: Int + if (format[0] === Int) { + val data = ByteArray(4) + Assert.assertEquals(4, iS.read(data).toLong()) + ByteBuffer.allocate(4).let { + it.order(this.byteOrder) + it.put(data) + it.flip() + ret.add(it.int) + } + continue + } + + //I, L: UInt + if (format[0] === UInt) { + val data = ByteArray(4) + Assert.assertEquals(4, iS.read(data).toLong()) + ByteBuffer.allocate(4).let { + it.order(this.byteOrder) + it.put(data) + it.flip() + ret.add(it.int.toUInt()) + } + continue + } + + //q: Long + if (format[0] === Long) { + val data = ByteArray(8) + Assert.assertEquals(8, iS.read(data).toLong()) + ByteBuffer.allocate(8).let { + it.order(this.byteOrder) + it.put(data) + it.flip() + ret.add(it.long) + } + continue + } + + //Q: ULong + if (format[0] === ULong) { + val data = ByteArray(8) + Assert.assertEquals(8, iS.read(data).toLong()) + ByteBuffer.allocate(8).let { + it.order(this.byteOrder) + it.put(data) + it.flip() + ret.add(it.long.toULong()) + } + continue + } + + throw IllegalArgumentException("Class [" + format[0] + "] not supported") + } + return ret + } + + //get lower 1 byte + private fun getLowerByte(obj: Char?): Byte { + val bf2 = ByteBuffer.allocate(Character.SIZE / 8) //aka. 16/8 + bf2.putChar(obj!!) + bf2.flip() + bf2.get() + return bf2.get() + } +} diff --git a/bbootimg/src/main/kotlin/init/Reboot.kt b/bbootimg/src/main/kotlin/init/Reboot.kt new file mode 100644 index 0000000..eac2535 --- /dev/null +++ b/bbootimg/src/main/kotlin/init/Reboot.kt @@ -0,0 +1,113 @@ +package cfig.init + +import cfig.bootloader_message.BootloaderMsg +import org.slf4j.LoggerFactory +import java.util.* + +class Reboot { + enum class RB_TYPE { + ANDROID_RB_RESTART, + ANDROID_RB_POWEROFF, + ANDROID_RB_RESTART2, + ANDROID_RB_THERMOFF + } + + companion object { + private val log = LoggerFactory.getLogger(Reboot::class.java) + const val dynamicPartitionKey = "ro.boot.dynamic_partitions" + const val lastRebootReasonKey = "persist.sys.boot.reason" + + private fun doReboot(cmd: RB_TYPE, reason: String, rebootTarget: String) { + val reasons = reason.split(",").toTypedArray() + val props = Properties() + props.setProperty(lastRebootReasonKey, reason) + if (reasons.size > 1 && reasons[0] == "reboot") { + if (reasons[1] in setOf("recovery", "bootloader", "cold", "hard", "warn")) { + props.setProperty(lastRebootReasonKey, reason.substring(7)) + } else { + //pass + } + } + } + + private fun doReboot(cmd: String, rebootTarget: String) { + log.info("cmd=[$cmd], rebootTarget=[$rebootTarget]") + } + + // setprop sys.powerctl + fun handlePowerctlMessage(inValue: String, props: Properties? = null) { + log.info("handlePowerctlMessage($inValue)") + val args = inValue.split(",").toTypedArray() + var cmd: String + var rebootTarget = "" + if (args.size > 3) { + throw java.lang.IllegalArgumentException("powerctl: unrecognized command $args") + } + when (args[0]) { + "shutdown" -> { + cmd = "ANDROID_RB_POWEROFF" + if (args.size == 2) { + when (args[1]) { + "userrequested" -> { + log.info("need to run_fsck") + } + "thermal" -> { + cmd = "ANDROID_RB_THERMOFF" + log.info("TurnOffBacklight()") + } + else -> { + //pass + } + } + } else { + //pass + } + } + "reboot" -> { + cmd = "ANDROID_RB_RESTART2" + if (args.size >= 2) { + rebootTarget = args[1] + val bDynamicPartition: Boolean? = props?.let { propsNotNull -> + propsNotNull.get(dynamicPartitionKey)?.let { if (it == "true") true else null } + } + when (rebootTarget) { + "fastboot" -> { + if (bDynamicPartition == null || bDynamicPartition == false) { + log.warn("$dynamicPartitionKey=false, using 'bootloader fastboot' instead of 'fastbootd'") + rebootTarget = "bootloader" + BootloaderMsg().writeRebootBootloader() + } else { + log.info("$dynamicPartitionKey=true, using fastbootd") + BootloaderMsg.writeBootloaderMessage(arrayOf("--fastboot")) + rebootTarget = "recovery" + } + } + "bootloader" -> { + BootloaderMsg().writeRebootBootloader() + } + "sideload", "sideload-auto-reboot" -> { + BootloaderMsg.writeBootloaderMessage( + arrayOf("--" + rebootTarget.replace("-", "_"))) + rebootTarget = "recovery" + } + else -> { + } + }//end-of-when-rebootTarget + if (args.size == 3 && args[2].isNotBlank()) { + log.info("rebootTarget: append " + args[2]) + rebootTarget += ("," + args[2]) + } + }//end-of-cmd + }//end-of-cmd=reboot + else -> {//not shutdown/reboot + throw java.lang.IllegalArgumentException("powerctl: unrecognized command $args") + } + }//end-of-args[0] + + log.debug("ActionManager::GetInstance().ClearQueue()") + log.debug("ActionManager::GetInstance().QueueEventTrigger(\"shutdown\")") + log.debug("ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, \"shutdown_done\")") + doReboot(cmd, rebootTarget) + }//end-of-handlePowerctlMessage + }//end-of-companion +} diff --git a/bbootimg/src/test/kotlin/avb/FooterTest.kt b/bbootimg/src/test/kotlin/avb/FooterTest.kt index a5b0d1a..0781284 100644 --- a/bbootimg/src/test/kotlin/avb/FooterTest.kt +++ b/bbootimg/src/test/kotlin/avb/FooterTest.kt @@ -15,11 +15,11 @@ class FooterTest { it.skip(footerBytes.size - 64L) val footer = Footer(it) println(footer.toString()) - assertEquals(1, footer.versionMajor) - assertEquals(0, footer.versionMinor) - assertEquals(512, footer.vbMetaSize) - assertEquals(28983296, footer.vbMetaOffset) - assertEquals(28983296, footer.originalImageSize) + assertEquals(1U, footer.versionMajor) + assertEquals(0U, footer.versionMinor) + assertEquals(512U.toULong(), footer.vbMetaSize) + assertEquals(28983296U.toULong(), footer.vbMetaOffset) + assertEquals(28983296U.toULong(), footer.originalImageSize) } } diff --git a/bbootimg/src/test/kotlin/avb/HeaderTest.kt b/bbootimg/src/test/kotlin/avb/HeaderTest.kt index aaaf76f..a92d993 100644 --- a/bbootimg/src/test/kotlin/avb/HeaderTest.kt +++ b/bbootimg/src/test/kotlin/avb/HeaderTest.kt @@ -8,7 +8,7 @@ class HeaderTest { @Test fun readHeader() { - val vbmetaHeaderStr = "4156423000000001000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000c80000000000000000000000000000000000000000000000c800000000000000000000000000000000617662746f6f6c20312e312e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + val vbmetaHeaderStr = "4156423000000001000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000c80000000000000000000000000000000000000000000000c800000000000000000000000000000000617662746f6f6c20312e312e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" val header2 = Header(ByteArrayInputStream(Hex.decodeHex(vbmetaHeaderStr))) println(header2.toString()) } diff --git a/bbootimg/src/test/kotlin/avb/alg/AlgorithmTest.kt b/bbootimg/src/test/kotlin/avb/alg/AlgorithmTest.kt index 1e533ee..060d0f6 100644 --- a/bbootimg/src/test/kotlin/avb/alg/AlgorithmTest.kt +++ b/bbootimg/src/test/kotlin/avb/alg/AlgorithmTest.kt @@ -1,6 +1,5 @@ package avb.alg -import cfig.io.Struct import org.junit.Test import org.junit.Assert.* diff --git a/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt b/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt index 13735cd..f9a3f65 100644 --- a/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt +++ b/bbootimg/src/test/kotlin/avb/alg/AlgorithmsTest.kt @@ -7,7 +7,7 @@ import org.junit.Test class AlgorithmsTest { @Test - fun test() { + fun test1() { val alg = Algorithms.get("NONE")!! Assert.assertEquals(Helper.toHexString(Algorithms.get("SHA256_RSA4096")!!.padding), diff --git a/bbootimg/src/test/kotlin/cfig/io/Struct3Test.kt b/bbootimg/src/test/kotlin/cfig/io/Struct3Test.kt new file mode 100644 index 0000000..1a67036 --- /dev/null +++ b/bbootimg/src/test/kotlin/cfig/io/Struct3Test.kt @@ -0,0 +1,433 @@ +import cfig.Helper +import cfig.io.Struct3 +import com.fasterxml.jackson.databind.ObjectMapper +import org.junit.Assert +import org.junit.Assert.* +import org.junit.Test +import java.io.ByteArrayInputStream + +class Struct3Test { + private fun getConvertedFormats(inStruct: Struct3): ArrayList> { + val f = inStruct.javaClass.getDeclaredField("formats") + f.isAccessible = true + val formatDumps = arrayListOf>() + (f.get(inStruct) as ArrayList<*>).apply { + this.forEach { + val format = it as Array + val k = if (format[0].toString().indexOf(" ") > 0) { + format[0].toString().split(" ")[1] + } else { + format[0].toString() + } + formatDumps.add(mapOf(k to (format[1] as Int))) + } + } + return formatDumps + } + + private fun constructorTestFun1(inFormatString: String) { + println(ObjectMapper().writeValueAsString(getConvertedFormats(Struct3(inFormatString)))) + } + + @Test + fun constructorTest() { + constructorTestFun1("3s") + constructorTestFun1("5b") + constructorTestFun1("5x") + constructorTestFun1("2c") + } + + @Test + fun calcSizeTest() { + Assert.assertEquals(3, Struct3("3s").calcSize()) + Assert.assertEquals(5, Struct3("5b").calcSize()) + Assert.assertEquals(5, Struct3("5x").calcSize()) + Assert.assertEquals(9, Struct3("9c").calcSize()) + } + + //x + @Test + fun paddingTest() { + Assert.assertEquals("0000000000", Helper.toHexString(Struct3("5x").pack(null))) + Assert.assertEquals("0000000000", Helper.toHexString(Struct3("5x").pack(0))) + Assert.assertEquals("0101010101", Helper.toHexString(Struct3("5x").pack(1))) + Assert.assertEquals("1212121212", Helper.toHexString(Struct3("5x").pack(0x12))) + //Integer高位被截掉 + Assert.assertEquals("2323232323", Helper.toHexString(Struct3("5x").pack(0x123))) + // minus 0001_0011 -> 补码 1110 1101,ie. 0xed + Assert.assertEquals("ededededed", Helper.toHexString(Struct3("5x").pack(-0x13))) + //0xff + Assert.assertEquals("ffffffffff", Helper.toHexString(Struct3("5x").pack(-1))) + + try { + Struct3("5x").pack("bad") + Assert.assertTrue("should throw exception here", false) + } catch (e: IllegalArgumentException) { + } + + //unpack + Struct3("3x").unpack(ByteArrayInputStream(Helper.fromHexString("000000"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(0.toByte(), it[0]) + } + Struct3("x2xx").unpack(ByteArrayInputStream(Helper.fromHexString("01121210"))).let { + Assert.assertEquals(3, it.size) + Assert.assertEquals(0x1.toByte(), it[0]) + Assert.assertEquals(0x12.toByte(), it[1]) + Assert.assertEquals(0x10.toByte(), it[2]) + } + } + + //c + @Test + fun characterTest() { + //constructor + Struct3("c") + + //calcSize + Assert.assertEquals(3, Struct3("3c").calcSize()) + + //pack illegal + try { + Struct3("c").pack("a") + Assert.fail("should throw exception here") + } catch (e: Throwable) { + Assert.assertTrue(e is AssertionError || e is IllegalArgumentException) + } + + //pack legal + Assert.assertEquals("61", + Helper.toHexString(Struct3("!c").pack(Character('a')))) + Assert.assertEquals("61", + Helper.toHexString(Struct3("c").pack(Character('a')))) + Assert.assertEquals("616263", + Helper.toHexString(Struct3("3c").pack('a', 'b', 'c'))) + + //unpack + Struct3("3c").unpack(ByteArrayInputStream(Helper.fromHexString("616263"))).let { + Assert.assertEquals(3, it.size) + Assert.assertEquals('a', it[0]) + Assert.assertEquals('b', it[1]) + Assert.assertEquals('c', it[2]) + } + } + + //b + @Test + fun bytesTest() { + //constructor + Struct3("b") + + //calcSize + Assert.assertEquals(3, Struct3("3b").calcSize()) + + //pack + Assert.assertEquals("123456", Helper.toHexString( + Struct3("3b").pack(byteArrayOf(0x12, 0x34, 0x56)))) + Assert.assertEquals("123456", Helper.toHexString( + Struct3("!3b").pack(byteArrayOf(0x12, 0x34, 0x56)))) + Assert.assertEquals("123400", Helper.toHexString( + Struct3("3b").pack(byteArrayOf(0x12, 0x34)))) + + //unpack + Struct3("3b").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals("123400", Helper.toHexString(it[0] as ByteArray)) + } + Struct3("bbb").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let { + Assert.assertEquals(3, it.size) + Assert.assertEquals("12", Helper.toHexString(it[0] as ByteArray)) + Assert.assertEquals("34", Helper.toHexString(it[1] as ByteArray)) + Assert.assertEquals("00", Helper.toHexString(it[2] as ByteArray)) + } + } + + //B: UByte array + @Test + fun uBytesTest() { + //constructor + Struct3("B") + + //calcSize + Assert.assertEquals(3, Struct3("3B").calcSize()) + + //pack + Assert.assertEquals("123456", Helper.toHexString( + Struct3("3B").pack(byteArrayOf(0x12, 0x34, 0x56)))) + Assert.assertEquals("123456", Helper.toHexString( + Struct3("!3B").pack(byteArrayOf(0x12, 0x34, 0x56)))) + Assert.assertEquals("123400", Helper.toHexString( + Struct3("3B").pack(byteArrayOf(0x12, 0x34)))) + + //unpack + Struct3("3B").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals("123400", Helper.toHexString(it[0] as UByteArray)) + } + Struct3("BBB").unpack(ByteArrayInputStream(Helper.fromHexString("123400"))).let { + Assert.assertEquals(3, it.size) + Assert.assertEquals("12", Helper.toHexString(it[0] as UByteArray)) + Assert.assertEquals("34", Helper.toHexString(it[1] as UByteArray)) + Assert.assertEquals("00", Helper.toHexString(it[2] as UByteArray)) + } + } + + //s + @Test + fun stringTest() { + //constructor + Struct3("s") + + //calcSize + Assert.assertEquals(3, Struct3("3s").calcSize()) + + //pack + Struct3("3s").pack("a") + Struct3("3s").pack("abc") + try { + Struct3("3s").pack("abcd") + Assert.fail("should throw exception here") + } catch (e: Throwable) { + Assert.assertTrue(e is AssertionError || e is IllegalArgumentException) + } + + //unpack + Struct3("3s").unpack(ByteArrayInputStream(Helper.fromHexString("616263"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals("abc", it[0]) + } + Struct3("3s").unpack(ByteArrayInputStream(Helper.fromHexString("610000"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals("a", it[0]) + } + } + + //h + @Test + fun shortTest() { + //constructor + Struct3("h") + + //calcSize + Assert.assertEquals(6, Struct3("3h").calcSize()) + + //pack + Assert.assertEquals("ff7f", Helper.toHexString(Struct3("h").pack(0x7fff))) + Assert.assertEquals("0080", Helper.toHexString(Struct3("h").pack(-0x8000))) + Assert.assertEquals("7fff0000", Helper.toHexString(Struct3(">2h").pack(0x7fff, 0))) + + //unpack + Struct3(">2h").unpack(ByteArrayInputStream(Helper.fromHexString("7fff0000"))).let { + Assert.assertEquals(2, it.size) + Assert.assertEquals(0x7fff.toShort(), it[0]) + Assert.assertEquals(0.toShort(), it[1]) + } + } + + //H + @Test + fun uShortTest() { + //constructor + Struct3("H") + + //calcSize + Assert.assertEquals(6, Struct3("3H").calcSize()) + + //pack + Assert.assertEquals("0100", Helper.toHexString(Struct3("H").pack((1U).toUShort()))) + Assert.assertEquals("0100", Helper.toHexString(Struct3("H").pack(1U))) + Assert.assertEquals("ffff", Helper.toHexString(Struct3("H").pack(65535U))) + Assert.assertEquals("ffff", Helper.toHexString(Struct3("H").pack(65535))) + try { + Struct3("H").pack(-1) + Assert.fail("should throw exception here") + } catch (e: Throwable) { + Assert.assertTrue(e is AssertionError || e is IllegalArgumentException) + } + //unpack + Struct3("H").unpack(ByteArrayInputStream(Helper.fromHexString("ffff"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(65535U.toUShort(), it[0]) + } + } + + //i, l + @Test + fun intTest() { + //constructor + Struct3("i") + Struct3("l") + + //calcSize + Assert.assertEquals(12, Struct3("3i").calcSize()) + Assert.assertEquals(12, Struct3("3l").calcSize()) + + //pack + Struct3("i").pack(65535 + 1) + Struct3("i").pack(-1) + //unpack + Struct3("i").unpack(ByteArrayInputStream(Helper.fromHexString("00000100"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(65536, it[0]) + } + Struct3("i").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffff"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(-1, it[0]) + } + } + + //I, L + @Test + fun uIntTest() { + //constructor + Struct3("I") + Struct3("L") + + //calcSize + Assert.assertEquals(12, Struct3("3I").calcSize()) + Assert.assertEquals(12, Struct3("3L").calcSize()) + + //pack + Assert.assertEquals("01000000", Helper.toHexString( + Struct3("I").pack(1U))) + Assert.assertEquals("80000000", Helper.toHexString( + Struct3(">I").pack(Int.MAX_VALUE.toUInt() + 1U))) + //unpack + Struct3("I").unpack(ByteArrayInputStream(Helper.fromHexString("01000000"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(1U, it[0]) + } + Struct3(">I").unpack(ByteArrayInputStream(Helper.fromHexString("80000000"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(Int.MAX_VALUE.toUInt() + 1U, it[0]) + } + } + + //q: Long + @Test + fun longTest() { + //constructor + Struct3("q") + + //calcSize + Assert.assertEquals(24, Struct3("3q").calcSize()) + + //pack + Assert.assertEquals("8000000000000000", Helper.toHexString( + Struct3(">q").pack(Long.MIN_VALUE))) + Assert.assertEquals("7fffffffffffffff", Helper.toHexString( + Struct3(">q").pack(Long.MAX_VALUE))) + Assert.assertEquals("ffffffffffffffff", Helper.toHexString( + Struct3(">q").pack(-1L))) + //unpack + Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("8000000000000000"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(Long.MIN_VALUE, it[0]) + } + Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("7fffffffffffffff"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(Long.MAX_VALUE, it[0]) + } + Struct3(">q").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffffffffffff"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(-1L, it[0]) + } + } + + //Q: ULong + @Test + fun uLongTest() { + //constructor + Struct3("Q") + + //calcSize + Assert.assertEquals(24, Struct3("3Q").calcSize()) + + //pack + Assert.assertEquals("7fffffffffffffff", Helper.toHexString( + Struct3(">Q").pack(Long.MAX_VALUE))) + Assert.assertEquals("0000000000000000", Helper.toHexString( + Struct3(">Q").pack(ULong.MIN_VALUE))) + Assert.assertEquals("ffffffffffffffff", Helper.toHexString( + Struct3(">Q").pack(ULong.MAX_VALUE))) + try { + Struct3(">Q").pack(-1L) + } catch (e: Throwable) { + Assert.assertTrue(e is AssertionError || e is IllegalArgumentException) + } + //unpack + Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("7fffffffffffffff"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(Long.MAX_VALUE.toULong(), it[0]) + } + Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("0000000000000000"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(ULong.MIN_VALUE, it[0]) + } + Struct3(">Q").unpack(ByteArrayInputStream(Helper.fromHexString("ffffffffffffffff"))).let { + Assert.assertEquals(1, it.size) + Assert.assertEquals(ULong.MAX_VALUE, it[0]) + } + } + + @Test + fun legacyTest() { + Assert.assertTrue(Struct3("<2i4b4b").pack( + 1, 7321, byteArrayOf(1, 2, 3, 4), byteArrayOf(200.toByte(), 201.toByte(), 202.toByte(), 203.toByte()))!! + .contentEquals(Helper.fromHexString("01000000991c000001020304c8c9cacb"))) + Assert.assertTrue(Struct3("<2i4b4B").pack( + 1, 7321, byteArrayOf(1, 2, 3, 4), intArrayOf(200, 201, 202, 203))!! + .contentEquals(Helper.fromHexString("01000000991c000001020304c8c9cacb"))) + + Assert.assertTrue(Struct3("b2x").pack(byteArrayOf(0x13), null).contentEquals(Helper.fromHexString("130000"))) + Assert.assertTrue(Struct3("b2xi").pack(byteArrayOf(0x13), null, 55).contentEquals(Helper.fromHexString("13000037000000"))) + + Struct3("5s").pack("Good").contentEquals(Helper.fromHexString("476f6f6400")) + Struct3("5s1b").pack("Good", byteArrayOf(13)).contentEquals(Helper.fromHexString("476f6f64000d")) + } + + + @Test + fun legacyIntegerLE() { + //int (4B) + assertTrue(Struct3("<2i").pack(1, 7321).contentEquals(Helper.fromHexString("01000000991c0000"))) + val ret = Struct3("<2i").unpack(ByteArrayInputStream(Helper.fromHexString("01000000991c0000"))) + assertEquals(2, ret.size) + assertTrue(ret[0] is Int) + assertTrue(ret[1] is Int) + assertEquals(1, ret[0] as Int) + assertEquals(7321, ret[1] as Int) + + //unsigned int (4B) + assertTrue(Struct3("2i").pack(1, 7321).contentEquals(Helper.fromHexString("0000000100001c99"))) + val ret = Struct3(">2i").unpack(ByteArrayInputStream(Helper.fromHexString("0000000100001c99"))) + assertEquals(1, ret[0] as Int) + assertEquals(7321, ret[1] as Int) + } + + run { + assertTrue(Struct3("!i").pack(-333).contentEquals(Helper.fromHexString("fffffeb3"))) + val ret2 = Struct3("!i").unpack(ByteArrayInputStream(Helper.fromHexString("fffffeb3"))) + assertEquals(-333, ret2[0] as Int) + } + } +} diff --git a/bbootimg/src/test/kotlin/cfig/io/StructTest.kt b/bbootimg/src/test/kotlin/cfig/io/StructTest.kt index d545a37..b513e07 100644 --- a/bbootimg/src/test/kotlin/cfig/io/StructTest.kt +++ b/bbootimg/src/test/kotlin/cfig/io/StructTest.kt @@ -1,13 +1,39 @@ import cfig.Helper import cfig.io.Struct +import com.fasterxml.jackson.databind.ObjectMapper import org.junit.Test import org.junit.Assert.* import java.io.ByteArrayInputStream +import kotlin.reflect.jvm.jvmName class StructTest { + private fun getConvertedFormats(inStruct: Struct): ArrayList> { + val f = inStruct.javaClass.getDeclaredField("formats") + f.isAccessible = true + val formatDumps = arrayListOf>() + (f.get(inStruct) as ArrayList<*>).apply { + this.forEach { + val format = it as Array + formatDumps.add(mapOf(format[0].toString().split(" ")[1] to (format[1] as Int))) + } + } + return formatDumps + } + + private fun constructorTestFun1(inFormatString: String) { + println(ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(getConvertedFormats(Struct(inFormatString)))) + } + + @Test + fun constructorTest() { + constructorTestFun1("2s") + constructorTestFun1("2b") + constructorTestFun1("2bs") + } + @Test - fun constructTest() { + fun calcSizeTest() { assertEquals(16, Struct("<2i4b4b").calcSize()) assertEquals(16, Struct("h").calcSize()) diff --git a/bbootimg/src/test/kotlin/init/RebootTest.kt b/bbootimg/src/test/kotlin/init/RebootTest.kt new file mode 100644 index 0000000..f77e918 --- /dev/null +++ b/bbootimg/src/test/kotlin/init/RebootTest.kt @@ -0,0 +1,39 @@ +package init + +import cfig.init.Reboot +import org.junit.Assert.* +import org.junit.Test +import java.util.* + +class RebootTest { + @Test + fun testDifferentModes() { + Reboot.handlePowerctlMessage("reboot,recovery") + Reboot.handlePowerctlMessage("reboot") + Reboot.handlePowerctlMessage("reboot,safemode") + Reboot.handlePowerctlMessage("reboot,dynsystem") + Reboot.handlePowerctlMessage("reboot,quiescent") + } + + @Test + fun shutdown() { + Reboot.handlePowerctlMessage("shutdown,userrequested") + Reboot.handlePowerctlMessage("shutdown,thermal") + Reboot.handlePowerctlMessage("shutdown,thermal,battery") + } + + @Test + fun fastbootd() { + Reboot.handlePowerctlMessage("reboot,bootloader") + val props = Properties() + Reboot.handlePowerctlMessage("reboot,fastboot", props) + props.put(Reboot.dynamicPartitionKey, "true") + Reboot.handlePowerctlMessage("reboot,fastboot", props) + } + + @Test + fun sideload() { + Reboot.handlePowerctlMessage("reboot,sideload-auto-reboot") + Reboot.handlePowerctlMessage("reboot,sideload") + } +} diff --git a/doc/misc_image_layout.md b/doc/misc_image_layout.md new file mode 100644 index 0000000..7567bc1 --- /dev/null +++ b/doc/misc_image_layout.md @@ -0,0 +1,20 @@ +# /misc partition layout + +| - | - | - | size | description | +| :---- | :------ | :---- | :---- | :---- | +| bootloader_message_ab | | | 4096 | | +| | bootloader_message | | 2048 | | +| | |command | 32 |updated by linux/bootloader | +| | |status | 32 |deprecated | +| | |recovery |768 |talking channel between normal/recovery modes | +| | |stage | 32 |format "#/#", eg, "1/3" | +| | |reserved | 1184| | +| |slot_suffix | | 32| | +| |update_channel | |128 | | +| |reserved | | 1888| | +| | | | | | +| vendor bootloader msg | | | 12 KB | offset 4kB | +| wipe_package info | | | 48K | offset 16KB, Used by uncrypt and recovery to store wipe_package for A/B devices | +|- |- |- |- |- | +| | | | | | + diff --git a/integrationTest.py b/integrationTest.py index 16e1681..e74f78f 100755 --- a/integrationTest.py +++ b/integrationTest.py @@ -3,6 +3,12 @@ import shutil, os.path, json, subprocess, hashlib, glob import unittest +successLogo = """ + +----------------------------------+ + | All test cases have PASSED | + +----------------------------------+ +""" + def hashFile(fileName): hasher = hashlib.md5() with open(fileName, 'rb') as afile: @@ -69,3 +75,5 @@ verifySingleJson(resDir, "9.0.0_blueline_pq1a.181105.017.a1", "%s/9.0.0_blueline # from volunteers verifySingleDir(resDir, "recovery_image_from_s-trace") + +print(successLogo)