sforkowany z mirror/meshtastic-android
work around for software update vs scan race condition
rodzic
d536900808
commit
dcd1fd3b55
2
TODO.md
2
TODO.md
|
@ -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).
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Ładowanie…
Reference in New Issue