mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-30 03:33:50 -04:00
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user