treat disabiling bluetooth as loss of connection

This commit is contained in:
geeksville 2020-02-04 17:27:10 -08:00
parent 9e83bfd790
commit 8ce5a13cf8
4 changed files with 59 additions and 16 deletions

View file

@ -1,9 +1,8 @@
# High priority # High priority
* fix startup race conditions in services, allow reads to block as needed
* if radio disconnects, we need to requeue a new connect attempt in RadioService * if radio disconnects, we need to requeue a new connect attempt in RadioService
* when notified phone should download messages * when notified phone should download messages
* have phone use our local node number as its node number (instead of hardwired) * fix startup race conditions in services, allow reads to block as needed
* investigate the Signal SMS message flow path, see if I could just make Mesh a third peer to signal & sms? * investigate the Signal SMS message flow path, see if I could just make Mesh a third peer to signal & sms?
* make signal work when there is no internet up * make signal work when there is no internet up
* make Signal rx path work * make Signal rx path work
@ -69,3 +68,4 @@ Don't leave device discoverable. Don't let unpaired users do things with device
* investigate a 16 bit node number. If possible it would make collisions super rare. Much easier to just pick a nodenum and go. * investigate a 16 bit node number. If possible it would make collisions super rare. Much easier to just pick a nodenum and go.
* remove example code boilerplate from the service * remove example code boilerplate from the service
* switch from protobuf-java to protobuf-javalite - much faster and smaller, just no JSON debug printing * switch from protobuf-java to protobuf-javalite - much faster and smaller, just no JSON debug printing
* have phone use our local node number as its node number (instead of hardwired)

View file

@ -132,11 +132,12 @@ class RadioInterfaceService : Service(), Logging {
private lateinit var device: BluetoothDevice private lateinit var device: BluetoothDevice
private lateinit var safe: SafeBluetooth private lateinit var safe: SafeBluetooth
val service get() = safe.gatt.services.find { it.uuid == BTM_SERVICE_UUID }!! val service get() = safe.gatt!!.services.find { it.uuid == BTM_SERVICE_UUID }!!
private lateinit var fromRadio: BluetoothGattCharacteristic private lateinit var fromRadio: BluetoothGattCharacteristic
private lateinit var fromNum: BluetoothGattCharacteristic private lateinit var fromNum: BluetoothGattCharacteristic
private val logSends = false
lateinit var sentPacketsLog: BinaryLogFile // inited in onCreate lateinit var sentPacketsLog: BinaryLogFile // inited in onCreate
private var isConnected = false private var isConnected = false
@ -160,8 +161,10 @@ class RadioInterfaceService : Service(), Logging {
debug("sending to radio") debug("sending to radio")
doWrite(BTM_TORADIO_CHARACTER, p) doWrite(BTM_TORADIO_CHARACTER, p)
sentPacketsLog.write(p) if (logSends) {
sentPacketsLog.flush() sentPacketsLog.write(p)
sentPacketsLog.flush()
}
} }
// Handle an incoming packet from the radio, broadcasts it as an android intent // Handle an incoming packet from the radio, broadcasts it as an android intent
@ -239,12 +242,14 @@ class RadioInterfaceService : Service(), Logging {
} }
} }
sentPacketsLog = BinaryLogFile(this, "sent_log.pb") if (logSends)
sentPacketsLog = BinaryLogFile(this, "sent_log.pb")
} }
override fun onDestroy() { override fun onDestroy() {
info("Destroying radio interface service") info("Destroying radio interface service")
sentPacketsLog.close() if (logSends)
sentPacketsLog.close()
safe.disconnect() safe.disconnect()
super.onDestroy() super.onDestroy()
} }

View file

@ -1,11 +1,15 @@
package com.geeksville.mesh package com.geeksville.mesh
import android.bluetooth.* import android.bluetooth.*
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.geeksville.android.Logging import com.geeksville.android.Logging
import com.geeksville.concurrent.CallbackContinuation import com.geeksville.concurrent.CallbackContinuation
import com.geeksville.concurrent.Continuation import com.geeksville.concurrent.Continuation
import com.geeksville.concurrent.SyncContinuation import com.geeksville.concurrent.SyncContinuation
import com.geeksville.util.exceptionReporter
import java.io.IOException import java.io.IOException
@ -25,12 +29,39 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
var timeoutMsec = 5 * 1000L var timeoutMsec = 5 * 1000L
/// Users can access the GATT directly as needed /// Users can access the GATT directly as needed
lateinit var gatt: BluetoothGatt var gatt: BluetoothGatt? = null
var state = BluetoothProfile.STATE_DISCONNECTED var state = BluetoothProfile.STATE_DISCONNECTED
private var currentWork: BluetoothContinuation? = null private var currentWork: BluetoothContinuation? = null
private val workQueue = mutableListOf<BluetoothContinuation>() private val workQueue = mutableListOf<BluetoothContinuation>()
/// When we see the BT stack getting disabled/renabled we handle that as a connect/disconnect event
private val btStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
if (intent.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
val newstate = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)
when (newstate) {
// Simulate a disconnection if the user disables bluetooth entirely
BluetoothAdapter.STATE_OFF -> if (gatt != null) gattCallback.onConnectionStateChange(
gatt!!,
0,
BluetoothProfile.STATE_DISCONNECTED
)
BluetoothAdapter.STATE_ON -> {
warn("FIXME - requeue a connect anytime bluetooth is reenabled")
}
}
}
}
}
init {
context.registerReceiver(
btStateReceiver,
IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
)
}
/** /**
* a schedulable bit of bluetooth work, includes both the closure to call to start the operation * a schedulable bit of bluetooth work, includes both the closure to call to start the operation
* and the completion (either async or sync) to call when it completes * and the completion (either async or sync) to call when it completes
@ -48,10 +79,11 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
} }
} }
private val gattCallback = object : BluetoothGattCallback() { private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange( override fun onConnectionStateChange(
gatt: BluetoothGatt, g: BluetoothGatt,
status: Int, status: Int,
newState: Int newState: Int
) { ) {
@ -66,6 +98,8 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
BluetoothProfile.STATE_DISCONNECTED -> { BluetoothProfile.STATE_DISCONNECTED -> {
// cancel any queued ops? for now I think it is best to keep them around // cancel any queued ops? for now I think it is best to keep them around
// failAllWork(IOException("Lost connection")) // failAllWork(IOException("Lost connection"))
gatt = null;
} }
} }
} }
@ -167,6 +201,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
// more info. // more info.
// Otherwise if you pass in false, it will try to connect now and will timeout and fail in 30 seconds. // Otherwise if you pass in false, it will try to connect now and will timeout and fail in 30 seconds.
private fun queueConnect(autoConnect: Boolean = false, cont: Continuation<Unit>) { private fun queueConnect(autoConnect: Boolean = false, cont: Continuation<Unit>) {
assert(gatt == null);
queueWork("connect", cont) { queueWork("connect", cont) {
val g = device.connectGatt(context, autoConnect, gattCallback) val g = device.connectGatt(context, autoConnect, gattCallback)
if (g != null) if (g != null)
@ -185,7 +220,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private fun queueReadCharacteristic( private fun queueReadCharacteristic(
c: BluetoothGattCharacteristic, c: BluetoothGattCharacteristic,
cont: Continuation<BluetoothGattCharacteristic> cont: Continuation<BluetoothGattCharacteristic>
) = queueWork("readc", cont) { gatt.readCharacteristic(c) } ) = queueWork("readc", cont) { gatt!!.readCharacteristic(c) }
fun asyncReadCharacteristic( fun asyncReadCharacteristic(
c: BluetoothGattCharacteristic, c: BluetoothGattCharacteristic,
@ -197,7 +232,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private fun queueDiscoverServices(cont: Continuation<Unit>) { private fun queueDiscoverServices(cont: Continuation<Unit>) {
queueWork("discover", cont) { queueWork("discover", cont) {
gatt.discoverServices() gatt!!.discoverServices()
} }
} }
@ -211,7 +246,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private fun queueRequestMtu( private fun queueRequestMtu(
len: Int, len: Int,
cont: Continuation<Int> cont: Continuation<Int>
) = queueWork("reqMtu", cont) { gatt.requestMtu(len) } ) = queueWork("reqMtu", cont) { gatt!!.requestMtu(len) }
fun asyncRequestMtu( fun asyncRequestMtu(
len: Int, len: Int,
@ -227,7 +262,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private fun queueWriteCharacteristic( private fun queueWriteCharacteristic(
c: BluetoothGattCharacteristic, c: BluetoothGattCharacteristic,
cont: Continuation<BluetoothGattCharacteristic> cont: Continuation<BluetoothGattCharacteristic>
) = queueWork("writec", cont) { gatt.writeCharacteristic(c) } ) = queueWork("writec", cont) { gatt!!.writeCharacteristic(c) }
fun asyncWriteCharacteristic( fun asyncWriteCharacteristic(
c: BluetoothGattCharacteristic, c: BluetoothGattCharacteristic,
@ -238,7 +273,10 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
makeSync { queueWriteCharacteristic(c, it) } makeSync { queueWriteCharacteristic(c, it) }
fun disconnect() { fun disconnect() {
gatt.disconnect() if (gatt != null)
gatt!!.disconnect()
context.unregisterReceiver(btStateReceiver)
failAllWork(Exception("SafeBluetooth disconnected")) failAllWork(Exception("SafeBluetooth disconnected"))
} }
} }

View file

@ -53,8 +53,8 @@ class SoftwareUpdateService : JobIntentService(), Logging {
// we begin by setting our MTU size as high as it can go // we begin by setting our MTU size as high as it can go
sync.requestMtu(512) sync.requestMtu(512)
val service = sync.gatt.services.find { it.uuid == SW_UPDATE_UUID }!! val service = sync.gatt!!.services.find { it.uuid == SW_UPDATE_UUID }!!
val totalSizeDesc = service.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER) val totalSizeDesc = service.getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)
val dataDesc = service.getCharacteristic(SW_UPDATE_DATA_CHARACTER) val dataDesc = service.getCharacteristic(SW_UPDATE_DATA_CHARACTER)