Merge pull request #45 from geeksville/dev

Dev
This commit is contained in:
Kevin Hester
2020-06-13 17:03:29 -07:00
committed by GitHub
6 changed files with 109 additions and 42 deletions

View File

@@ -9,6 +9,9 @@ import com.google.zxing.MultiFormatWriter
import com.journeyapps.barcodescanner.BarcodeEncoder
import java.net.MalformedURLException
/** Utility function to make it easy to declare byte arrays - FIXME move someplace better */
fun byteArrayOfInts(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }
data class Channel(
val settings: MeshProtos.ChannelSettings = MeshProtos.ChannelSettings.getDefaultInstance()
@@ -17,6 +20,12 @@ data class Channel(
// Note: this string _SHOULD NOT BE LOCALIZED_ because it directly hashes to values used on the device for the default channel name.
val defaultChannelName = "Default"
// These bytes must math the well known and not secret bytes used the default channel AES128 key device code
val channelDefaultKey = byteArrayOfInts(
0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf
)
// Placeholder when emulating
val emulated = Channel(
MeshProtos.ChannelSettings.newBuilder().setName(defaultChannelName)

View File

@@ -217,9 +217,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
// Note: we generate a new characteristic each time, because we are about to
// change the data and we want the data stored in the closure
val toRadio = getCharacteristic(uuid)
toRadio.value = a
s.asyncWriteCharacteristic(toRadio) { r ->
s.asyncWriteCharacteristic(toRadio, a) { r ->
try {
r.getOrThrow()
debug("write of ${a.size} bytes completed")
@@ -378,15 +377,16 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
}
}
private var needForceRefresh = true
private fun onConnect(connRes: Result<Unit>) {
// This callback is invoked after we are connected
connRes.getOrThrow()
info("Connected to radio!")
if (!hasForcedRefresh) {
// FIXME - for some reason we need to refresh _everytime_. It is almost as if we've cached wrong descriptor fieldnums forever
// hasForcedRefresh = true
if (needForceRefresh) { // Our ESP32 code doesn't properly generate "service changed" indications. Therefore we need to force a refresh on initial start
//needForceRefresh = false // In fact, because of tearing down BLE in sleep on the ESP32, our handle # assignments are not stable across sleep - so we much refetch every time
forceServiceRefresh()
}

View File

@@ -893,6 +893,7 @@ class MeshService : Service(), Logging {
val packet = toMeshPacket(p)
p.status = MessageStatus.ENROUTE
p.time = System.currentTimeMillis() // update time to the actual time we started sending
debug("SENDING TO RADIO: $packet")
sendToRadio(packet)
}
@@ -934,7 +935,7 @@ class MeshService : Service(), Logging {
// decided to pass through to us (except for broadcast packets)
//val toNum = packet.to
debug("Recieved: $packet")
// debug("Recieved: $packet")
val p = packet.decoded
// If the rxTime was not set by the device (because device software was old), guess at a time
@@ -1370,24 +1371,34 @@ class MeshService : Service(), Logging {
* Set our owner with either the new or old API
*/
fun setOwner(myId: String?, longName: String, shortName: String) {
debug("SetOwner $myId : ${longName.anonymize} : $shortName")
val myNode = myNodeInfo
if (myNode != null) {
val user = MeshProtos.User.newBuilder().also {
if (myId != null) // Only set the id if it was provided
it.id = myId
it.longName = longName
it.shortName = shortName
}.build()
// Also update our own map for our nodenum, by handling the packet just like packets from other users
if (myNodeInfo != null) {
handleReceivedUser(myNodeInfo!!.myNodeNum, user)
}
val myInfo = toNodeInfo(myNode.myNodeNum)
if (longName == myInfo.user?.longName && shortName == myInfo.user?.shortName)
debug("Ignoring nop owner change")
else {
debug("SetOwner $myId : ${longName.anonymize} : $shortName")
// set my owner info
sendToRadio(ToRadio.newBuilder().apply {
this.setOwner = user
})
val user = MeshProtos.User.newBuilder().also {
if (myId != null) // Only set the id if it was provided
it.id = myId
it.longName = longName
it.shortName = shortName
}.build()
// Also update our own map for our nodenum, by handling the packet just like packets from other users
handleReceivedUser(myNode.myNodeNum, user)
// set my owner info
sendToRadio(ToRadio.newBuilder().apply {
this.setOwner = user
})
}
} else
throw Exception("Can't set user without a node info") // this shouldn't happen
}
/// Do not use directly, instead call generatePacketId()

View File

@@ -622,19 +622,25 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private fun queueWriteCharacteristic(
c: BluetoothGattCharacteristic,
v: ByteArray,
cont: Continuation<BluetoothGattCharacteristic>
) = queueWork("writeC ${c.uuid}", cont) {
currentReliableWrite = null
c.value = v
gatt!!.writeCharacteristic(c)
}
fun asyncWriteCharacteristic(
c: BluetoothGattCharacteristic,
v: ByteArray,
cb: (Result<BluetoothGattCharacteristic>) -> Unit
) = queueWriteCharacteristic(c, CallbackContinuation(cb))
) = queueWriteCharacteristic(c, v, CallbackContinuation(cb))
fun writeCharacteristic(c: BluetoothGattCharacteristic): BluetoothGattCharacteristic =
makeSync { queueWriteCharacteristic(c, it) }
fun writeCharacteristic(
c: BluetoothGattCharacteristic,
v: ByteArray
): BluetoothGattCharacteristic =
makeSync { queueWriteCharacteristic(c, v, it) }
/** Like write, but we use the extra reliable flow documented here:
* https://stackoverflow.com/questions/24485536/what-is-reliable-write-in-ble

View File

@@ -13,6 +13,48 @@ import com.geeksville.util.exceptionReporter
import java.util.*
import java.util.zip.CRC32
/**
* Move this somewhere as a generic network byte order function
*/
fun toNetworkByteArray(value: Int, formatType: Int): ByteArray {
val len: Int = 4 // getTypeLen(formatType)
val mValue = ByteArray(len)
when (formatType) {
/* BluetoothGattCharacteristic.FORMAT_SINT8 -> {
value = intToSignedBits(value, 8)
mValue.get(offset) = (value and 0xFF).toByte()
}
BluetoothGattCharacteristic.FORMAT_UINT8 -> mValue.get(offset) =
(value and 0xFF).toByte()
BluetoothGattCharacteristic.FORMAT_SINT16 -> {
value = intToSignedBits(value, 16)
mValue.get(offset++) = (value and 0xFF).toByte()
mValue.get(offset) = (value shr 8 and 0xFF).toByte()
}
BluetoothGattCharacteristic.FORMAT_UINT16 -> {
mValue.get(offset++) = (value and 0xFF).toByte()
mValue.get(offset) = (value shr 8 and 0xFF).toByte()
}
BluetoothGattCharacteristic.FORMAT_SINT32 -> {
value = intToSignedBits(value, 32)
mValue.get(offset++) = (value and 0xFF).toByte()
mValue.get(offset++) = (value shr 8 and 0xFF).toByte()
mValue.get(offset++) = (value shr 16 and 0xFF).toByte()
mValue.get(offset) = (value shr 24 and 0xFF).toByte()
} */
BluetoothGattCharacteristic.FORMAT_UINT32 -> {
mValue[0] = (value and 0xFF).toByte()
mValue[1] = (value shr 8 and 0xFF).toByte()
mValue[2] = (value shr 16 and 0xFF).toByte()
mValue[3] = (value shr 24 and 0xFF).toByte()
}
else -> TODO()
}
return mValue
}
/**
* typical flow
*
@@ -211,14 +253,10 @@ class SoftwareUpdateService : JobIntentService(), Logging {
val firmwareSize = firmwareStream.available()
// Start the update by writing the # of bytes in the image
logAssert(
totalSizeDesc.setValue(
firmwareSize,
BluetoothGattCharacteristic.FORMAT_UINT32,
0
)
sync.writeCharacteristic(
totalSizeDesc,
toNetworkByteArray(firmwareSize, BluetoothGattCharacteristic.FORMAT_UINT32)
)
sync.writeCharacteristic(totalSizeDesc)
// Our write completed, queue up a readback
val totalSizeReadback = sync.readCharacteristic(totalSizeDesc)
@@ -239,23 +277,18 @@ class SoftwareUpdateService : JobIntentService(), Logging {
// slightly expensive to keep reallocing this buffer, but whatever
logAssert(firmwareStream.read(buffer) == blockSize)
firmwareCrc.update(buffer)
dataDesc.value = buffer
sync.writeCharacteristic(dataDesc)
sync.writeCharacteristic(dataDesc, buffer)
firmwareNumSent += blockSize
}
// We have finished sending all our blocks, so post the CRC so our state machine can advance
val c = firmwareCrc.value
info("Sent all blocks, crc is $c")
logAssert(
crc32Desc.setValue(
c.toInt(),
BluetoothGattCharacteristic.FORMAT_UINT32,
0
)
sync.writeCharacteristic(
crc32Desc,
toNetworkByteArray(c.toInt(), BluetoothGattCharacteristic.FORMAT_UINT32)
)
sync.writeCharacteristic(crc32Desc)
// we just read the update result if !0 we have an error
val updateResult =

View File

@@ -147,12 +147,20 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
newSettings.name = channelNameEdit.text.toString().trim()
// Generate a new AES256 key (for any channel not named Default)
if (newSettings.name != Channel.defaultChannelName) {
if (!newSettings.name.equals(
Channel.defaultChannelName,
ignoreCase = true
)
) {
debug("ASSIGNING NEW AES256 KEY")
val random = SecureRandom()
val bytes = ByteArray(32)
random.nextBytes(bytes)
newSettings.psk = ByteString.copyFrom(bytes)
} else {
debug("ASSIGNING NEW default AES128 KEY")
newSettings.name = Channel.defaultChannelName // Fix any case errors
newSettings.psk = ByteString.copyFrom(Channel.channelDefaultKey)
}
// Try to change the radio, if it fails, tell the user why and throw away their redits