work around for software update vs scan race condition

1.2-legacy
geeksville 2020-01-23 09:04:06 -08:00
rodzic d536900808
commit dcd1fd3b55
3 zmienionych plików z 123 dodań i 94 usunięć

Wyświetl plik

@ -4,6 +4,7 @@
* get signal running under debugger * get signal running under debugger
* DONE add broadcasters for use by signal (node changes and packet received) * DONE add broadcasters for use by signal (node changes and packet received)
* make test implementation of server (doesn't use bluetooth) * make test implementation of server (doesn't use bluetooth)
* make compose based access show mesh state
* make a test client of the android service * make a test client of the android service
* add real messaging code/protobufs * add real messaging code/protobufs
* use https://codelabs.developers.google.com/codelabs/jetpack-compose-basics/#4 to show service state * use https://codelabs.developers.google.com/codelabs/jetpack-compose-basics/#4 to show service state
@ -21,6 +22,7 @@ nanopb binaries available here: https://jpa.kapsi.fi/nanopb/download/ use nanopb
# Medium priority # Medium priority
* change info() log strings to debug()
* use platform theme (dark or light) * use platform theme (dark or light)
* remove mixpanel analytics * remove mixpanel analytics
* require user auth to pair with the device (i.e. press button on device to allow a new phone to pair with it). * require user auth to pair with the device (i.e. press button on device to allow a new phone to pair with it).

Wyświetl plik

@ -155,12 +155,16 @@ class MainActivity : AppCompatActivity(), Logging {
} }
private fun bindMeshService() { private fun bindMeshService() {
info("Binding to mesh service!") debug("Binding to mesh service!")
// we bind using the well known name, to make sure 3rd party apps could also // we bind using the well known name, to make sure 3rd party apps could also
logAssert(meshService == null) logAssert(meshService == null)
// FIXME - finding by string does work
val intent = Intent(this, MeshService::class.java) val intent = Intent(this, MeshService::class.java)
intent.action = IMeshService::class.java.name intent.action = IMeshService::class.java.name
intent.setPackage("com.geeksville.mesh");
// This is the remote version that does not work! FIXME
//val intent = Intent(IMeshService::class.java.name)
//intent.setPackage("com.geeksville.mesh");
isBound = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) isBound = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
logAssert(isBound) logAssert(isBound)
} }
@ -170,7 +174,7 @@ class MainActivity : AppCompatActivity(), Logging {
// it, then now is the time to unregister. // it, then now is the time to unregister.
// if we never connected, do nothing // if we never connected, do nothing
if(isBound) { if(isBound) {
info("Unbinding from mesh service!") debug("Unbinding from mesh service!")
unbindService(serviceConnection) unbindService(serviceConnection)
meshService = null meshService = null
} }

Wyświetl plik

@ -36,6 +36,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
} }
fun startUpdate() { fun startUpdate() {
info("starting update")
firmwareStream = assets.open("firmware.bin") firmwareStream = assets.open("firmware.bin")
// Start the update by writing the # of bytes in the image // Start the update by writing the # of bytes in the image
@ -46,6 +47,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
// Send the next block of our file to the device // Send the next block of our file to the device
fun sendNextBlock() { fun sendNextBlock() {
info("sending next block")
if (firmwareStream.available() > 0) { if (firmwareStream.available() > 0) {
var blockSize = 512 var blockSize = 512
@ -64,6 +66,110 @@ class SoftwareUpdateService : JobIntentService(), Logging {
} }
} }
fun connectToDevice(device: BluetoothDevice) {
debug("Connect to $device")
lateinit var bluetoothGatt: BluetoothGatt // late init so we can declare our callback and use this there
//var connectionState = STATE_DISCONNECTED
// Various callback methods defined by the BLE API.
val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt,
status: Int,
newState: Int
) {
info("new bluetooth connection state $newState")
//val intentAction: String
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
//intentAction = ACTION_GATT_CONNECTED
//connectionState = STATE_CONNECTED
// broadcastUpdate(intentAction)
logAssert(bluetoothGatt.discoverServices())
}
BluetoothProfile.STATE_DISCONNECTED -> {
//intentAction = ACTION_GATT_DISCONNECTED
//connectionState = STATE_DISCONNECTED
// broadcastUpdate(intentAction)
}
}
}
// New services discovered
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
info("onServicesDiscovered")
logAssert(status == BluetoothGatt.GATT_SUCCESS)
// broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED)
val service = gatt.services.find { it.uuid == SW_UPDATE_UUID }
logAssert(service != null)
// FIXME instead of slamming in the target device here, instead make it a param for startUpdate
updateService = service!!
totalSizeDesc = service.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)
dataDesc = service.getCharacteristic(SW_UPDATE_DATA_CHARACTER)
crc32Desc = service.getCharacteristic(SW_UPDATE_CRC32_CHARACTER)
updateResultDesc = service.getCharacteristic(SW_UPDATE_RESULT_CHARACTER)
// FIXME instead of keeping the connection open, make start update just reconnect (needed once user can choose devices)
updateGatt = bluetoothGatt
enqueueWork(this@SoftwareUpdateService, startUpdateIntent)
}
// Result of a characteristic read operation
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
debug("onCharacteristicRead")
logAssert(status == BluetoothGatt.GATT_SUCCESS)
if (characteristic == totalSizeDesc) {
// Our read of this has completed, either fail or continue updating
val readvalue =
characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
logAssert(readvalue != 0) // FIXME - handle this case
enqueueWork(this@SoftwareUpdateService, sendNextBlockIntent)
}
else {
warn("Unexpected read: $characteristic")
}
// broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic)
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?,
status: Int
) {
debug("onCharacteristicWrite")
logAssert(status == BluetoothGatt.GATT_SUCCESS)
if(characteristic == totalSizeDesc) {
// Our write completed, queue up a readback
logAssert(updateGatt.readCharacteristic(totalSizeDesc))
} else if (characteristic == dataDesc) {
enqueueWork(this@SoftwareUpdateService, sendNextBlockIntent)
}
else {
warn("Unexpected write: $characteristic")
}
}
}
bluetoothGatt =
device.connectGatt(this@SoftwareUpdateService.applicationContext, true, gattCallback)!!
toast("Connected to $device")
// too early to do this here
// logAssert(bluetoothGatt.discoverServices())
}
private val scanCallback = object : ScanCallback() { private val scanCallback = object : ScanCallback() {
override fun onScanFailed(errorCode: Int) { override fun onScanFailed(errorCode: Int) {
throw NotImplementedError() throw NotImplementedError()
@ -78,102 +184,19 @@ class SoftwareUpdateService : JobIntentService(), Logging {
// if that device later disconnects remove it as a candidate // if that device later disconnects remove it as a candidate
override fun onScanResult(callbackType: Int, result: ScanResult) { override fun onScanResult(callbackType: Int, result: ScanResult) {
info("onScanResult")
// We don't need any more results now // We don't need any more results now
bluetoothAdapter.bluetoothLeScanner.stopScan(this) bluetoothAdapter.bluetoothLeScanner.stopScan(this)
lateinit var bluetoothGatt: BluetoothGatt // late init so we can declare our callback and use this there connectToDevice(result.device)
//var connectionState = STATE_DISCONNECTED
// Various callback methods defined by the BLE API.
val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt,
status: Int,
newState: Int
) {
info("new bluetooth connection state $newState")
//val intentAction: String
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
//intentAction = ACTION_GATT_CONNECTED
//connectionState = STATE_CONNECTED
// broadcastUpdate(intentAction)
logAssert(bluetoothGatt.discoverServices())
}
BluetoothProfile.STATE_DISCONNECTED -> {
//intentAction = ACTION_GATT_DISCONNECTED
//connectionState = STATE_DISCONNECTED
// broadcastUpdate(intentAction)
}
}
}
// New services discovered
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
logAssert(status == BluetoothGatt.GATT_SUCCESS)
// broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED)
val service = gatt.services.find { it.uuid == SW_UPDATE_UUID }
if (service != null) {
// FIXME instead of slamming in the target device here, instead make it a param for startUpdate
updateService = service
totalSizeDesc = service.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)
dataDesc = service.getCharacteristic(SW_UPDATE_DATA_CHARACTER)
crc32Desc = service.getCharacteristic(SW_UPDATE_CRC32_CHARACTER)
updateResultDesc = service.getCharacteristic(SW_UPDATE_RESULT_CHARACTER)
// FIXME instead of keeping the connection open, make start update just reconnect (needed once user can choose devices)
updateGatt = bluetoothGatt
enqueueWork(this@SoftwareUpdateService, startUpdateIntent)
}
}
// Result of a characteristic read operation
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
logAssert(status == BluetoothGatt.GATT_SUCCESS)
if (characteristic == totalSizeDesc) {
// Our read of this has completed, either fail or continue updating
val readvalue =
characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
logAssert(readvalue != 0) // FIXME - handle this case
enqueueWork(this@SoftwareUpdateService, sendNextBlockIntent)
}
// broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic)
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?,
status: Int
) {
logAssert(status == BluetoothGatt.GATT_SUCCESS)
if(characteristic == totalSizeDesc) {
// Our write completed, queue up a readback
logAssert(updateGatt.readCharacteristic(totalSizeDesc))
} else if (characteristic == dataDesc) {
enqueueWork(this@SoftwareUpdateService, sendNextBlockIntent)
}
}
}
bluetoothGatt =
result.device.connectGatt(this@SoftwareUpdateService.applicationContext, true, gattCallback)!!
toast("FISH " + bluetoothGatt)
// too early to do this here
// logAssert(bluetoothGatt.discoverServices())
} }
} }
// Until my race condition with scanning is fixed
fun connectToTestDevice() {
connectToDevice(bluetoothAdapter.getRemoteDevice("B4:E6:2D:EA:32:B7"))
}
private fun scanLeDevice(enable: Boolean) { private fun scanLeDevice(enable: Boolean) {
when (enable) { when (enable) {
@ -217,7 +240,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
toast("Executing: $label") toast("Executing: $label")
when (intent.action) { when (intent.action) {
scanDevicesIntent.action -> scanLeDevice(true) scanDevicesIntent.action -> connectToTestDevice() // FIXME scanLeDevice(true)
startUpdateIntent.action -> startUpdate() startUpdateIntent.action -> startUpdate()
sendNextBlockIntent.action -> sendNextBlock() sendNextBlockIntent.action -> sendNextBlock()
else -> logAssert(false) else -> logAssert(false)