diff --git a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt index feb2af321..4a77b50f9 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt @@ -226,12 +226,27 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD completeWork(status, characteristic) } + override fun onReliableWriteCompleted(gatt: BluetoothGatt, status: Int) { + completeWork(status, Unit) + } + override fun onCharacteristicWrite( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { - completeWork(status, characteristic) + val reliable = currentReliableWrite + if (reliable != null) + if (!characteristic.value.contentEquals(reliable)) { + errormsg("A reliable write failed!") + gatt.abortReliableWrite(); + completeWork(42, characteristic) // skanky code to indicate failure + } else { + logAssert(gatt.executeReliableWrite()) + // After this execute reliable completes - we can continue with normal operations (see onReliableWriteCompleted) + } + else // Just a standard write - do the normal flow + completeWork(status, characteristic) } override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { @@ -481,10 +496,15 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD fun requestMtu(len: Int): Unit = makeSync { queueRequestMtu(len, it) } + private var currentReliableWrite: ByteArray? = null + private fun queueWriteCharacteristic( c: BluetoothGattCharacteristic, cont: Continuation - ) = queueWork("writeC ${c.uuid}", cont) { gatt!!.writeCharacteristic(c) } + ) = queueWork("writeC ${c.uuid}", cont) { + currentReliableWrite = null + gatt!!.writeCharacteristic(c) + } fun asyncWriteCharacteristic( c: BluetoothGattCharacteristic, @@ -494,6 +514,26 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD fun writeCharacteristic(c: BluetoothGattCharacteristic): BluetoothGattCharacteristic = makeSync { queueWriteCharacteristic(c, it) } + /** Like write, but we use the extra reliable flow documented here: + * https://stackoverflow.com/questions/24485536/what-is-reliable-write-in-ble + */ + private fun queueWriteReliable( + c: BluetoothGattCharacteristic, + cont: Continuation + ) = queueWork("rwriteC ${c.uuid}", cont) { + logAssert(gatt!!.beginReliableWrite()) + currentReliableWrite = c.value.clone() + gatt!!.writeCharacteristic(c) + } + + /* fun asyncWriteReliable( + c: BluetoothGattCharacteristic, + cb: (Result) -> Unit + ) = queueWriteCharacteristic(c, CallbackContinuation(cb)) */ + + fun writeReliable(c: BluetoothGattCharacteristic): Unit = + makeSync { queueWriteReliable(c, it) } + private fun queueWriteDescriptor( c: BluetoothGattDescriptor, cont: Continuation diff --git a/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt b/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt index ab334bc3a..c10c3914e 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SoftwareUpdateService.kt @@ -145,7 +145,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { // FIXME, instead compare version strings carefully to see if less than val needsUpdate = (curver != swVer) - return needsUpdate && !isDevBuild + return needsUpdate && !isDevBuild && false // temporarily disabled because it fails occasionally } /** Return the filename this device needs to use as an update (or null if no update needed) @@ -199,6 +199,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { info("Starting firmware update for $assetName") + progress = 0 val totalSizeDesc = service.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER) val dataDesc = service.getCharacteristic(SW_UPDATE_DATA_CHARACTER) val crc32Desc = service.getCharacteristic(SW_UPDATE_CRC32_CHARACTER) @@ -219,7 +220,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { ) sync.writeCharacteristic(totalSizeDesc) -// Our write completed, queue up a readback + // Our write completed, queue up a readback val totalSizeReadback = sync.readCharacteristic(totalSizeDesc) .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0) if (totalSizeReadback == 0) // FIXME - handle this case @@ -238,8 +239,7 @@ class SoftwareUpdateService : JobIntentService(), Logging { // slightly expensive to keep reallocing this buffer, but whatever logAssert(firmwareStream.read(buffer) == blockSize) firmwareCrc.update(buffer) - - // updateGatt.beginReliableWrite() + dataDesc.value = buffer sync.writeCharacteristic(dataDesc) firmwareNumSent += blockSize diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index e6a3b092e..5b1850692 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -320,6 +320,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { debug("User started firmware update") updateFirmwareButton.isEnabled = false // Disable until things complete updateProgressBar.visibility = View.VISIBLE + updateProgressBar.progress = 0 // start from scratch scanStatusText.text = "Updating firmware, wait up to eight minutes..." service.startFirmwareUpdate() @@ -342,6 +343,32 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { return inflater.inflate(R.layout.settings_fragment, container, false) } + private fun initNodeInfo() { + val connected = model.isConnected.value + + // If actively connected possibly let the user update firmware + val info = model.myNodeInfo.value + if (connected == MeshService.ConnectionState.CONNECTED && info != null && info.shouldUpdate) { + updateFirmwareButton.visibility = View.VISIBLE + updateFirmwareButton.text = + getString(R.string.update_to).format(getString(R.string.cur_firmware_version)) + } else { + updateFirmwareButton.visibility = View.GONE + updateProgressBar.visibility = View.GONE + } + + when (connected) { + MeshService.ConnectionState.CONNECTED -> { + val fwStr = info?.firmwareString ?: "" + scanStatusText.text = getString(R.string.connected_to).format(fwStr) + } + MeshService.ConnectionState.DISCONNECTED -> + scanStatusText.text = getString(R.string.not_connected) + MeshService.ConnectionState.DEVICE_SLEEP -> + scanStatusText.text = getString(R.string.connected_sleeping) + } + } + /// Setup the ui widgets unrelated to BLE scanning private fun initCommonUI() { model.ownerName.observe(viewLifecycleOwner, Observer { name -> @@ -351,29 +378,12 @@ class SettingsFragment : ScreenFragment("Settings"), Logging { // Only let user edit their name or set software update while connected to a radio model.isConnected.observe(viewLifecycleOwner, Observer { connected -> usernameView.isEnabled = connected == MeshService.ConnectionState.CONNECTED + initNodeInfo() + }) - // If actively connected possibly let the user update firmware - val info = model.myNodeInfo.value - if (connected == MeshService.ConnectionState.CONNECTED && info != null && info.couldUpdate) { - updateFirmwareButton.visibility = View.VISIBLE - updateFirmwareButton.text = - getString(R.string.update_to).format(getString(R.string.cur_firmware_version)) - } else { - updateFirmwareButton.visibility = View.GONE - updateProgressBar.visibility = View.GONE - } - - when (connected) { - MeshService.ConnectionState.CONNECTED -> { - val fwStr = info?.firmwareString ?: "" - scanStatusText.text = getString(R.string.connected_to).format(fwStr) - } - MeshService.ConnectionState.DISCONNECTED -> - scanStatusText.text = getString(R.string.not_connected) - MeshService.ConnectionState.DEVICE_SLEEP -> - scanStatusText.text = getString(R.string.connected_sleeping) - } - + // Also watch myNodeInfo because it might change later + model.myNodeInfo.observe(viewLifecycleOwner, Observer { + initNodeInfo() }) updateFirmwareButton.setOnClickListener { diff --git a/geeksville-androidlib b/geeksville-androidlib index b53b7f90b..8ccbd0b68 160000 --- a/geeksville-androidlib +++ b/geeksville-androidlib @@ -1 +1 @@ -Subproject commit b53b7f90b326ef6b1dedca32a2472784e63d10e6 +Subproject commit 8ccbd0b688e9ed1e2ccd836414d535f7a6d5366e