using newly added kotlin.(UInt|ULong|UByte) since 1.3
add 'Reboot' function tests add "Struct3" to replace "Struct" with new kotlin typespull/31/head
parent
fe14b49178
commit
a7be076e77
@ -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)
|
@ -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
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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<String>) {
|
||||
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<String>) {
|
||||
this.command = "boot-recovery"
|
||||
this.recovery = "recovery"
|
||||
options.forEach {
|
||||
this.recovery += if (it.endsWith("\n")) {
|
||||
it
|
||||
} else {
|
||||
it + "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Array<Any?>>()
|
||||
|
||||
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<Any?>(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<Byte> = 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<Byte> = 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<Any>()
|
||||
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()
|
||||
}
|
||||
}
|
@ -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 <value>
|
||||
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
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
@ -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 |
|
||||
|- |- |- |- |- |
|
||||
| | | | | |
|
||||
|
Loading…
Reference in New Issue