dash ble: handle disconnects

This commit is contained in:
Andrei Vereha 2021-04-04 12:40:28 +02:00
parent 547454de6c
commit 5b10ad13ec
7 changed files with 80 additions and 45 deletions

View file

@ -87,6 +87,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
override fun getStatus(): ConnectionStatus {
// TODO is this used?
var s: ConnectionStatus
synchronized(status) {
s = status
@ -113,7 +114,6 @@ class OmnipodDashBleManagerImpl @Inject constructor(
emitter.onNext(PodEvent.EstablishingSession)
establishSession(1.toByte())
emitter.onNext(PodEvent.Connected)
} else {
emitter.onNext(PodEvent.AlreadyConnected(podAddress))
}
@ -217,10 +217,8 @@ class OmnipodDashBleManagerImpl @Inject constructor(
}
override fun disconnect() {
if (connection == null) {
aapsLogger.info(LTag.PUMPBTCOMM, "Trying to disconnect a null connection")
}
connection?.disconnect()
?: aapsLogger.info(LTag.PUMPBTCOMM, "Trying to disconnect a null connection")
}
companion object {

View file

@ -9,6 +9,7 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CharacteristicType.Companion.byValue
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.IncomingPackets
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.DisconnectHandler
import info.nightscout.androidaps.utils.extensions.toHex
import java.util.*
import java.util.concurrent.BlockingQueue
@ -19,11 +20,19 @@ import java.util.concurrent.TimeUnit
class BleCommCallbacks(
private val aapsLogger: AAPSLogger,
private val incomingPackets: IncomingPackets,
private val disconnectHandler: DisconnectHandler,
) : BluetoothGattCallback() {
// Synchronized because they can be:
// - read from various callbacks
// - written from resetConnection that is called onConnectionLost
private var serviceDiscoveryComplete: CountDownLatch = CountDownLatch(1)
@Synchronized get
@Synchronized set
private var connected: CountDownLatch = CountDownLatch(1)
private val writeQueue: BlockingQueue<WriteConfirmation> = LinkedBlockingQueue(1)
@Synchronized get
@Synchronized set
private val writeQueue: BlockingQueue<WriteConfirmation> = LinkedBlockingQueue()
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
aapsLogger.debug(LTag.PUMPBTCOMM, "OnConnectionStateChange with status/state: $status/$newState")
@ -31,6 +40,9 @@ class BleCommCallbacks(
if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
connected.countDown()
}
if (newState == BluetoothProfile.STATE_DISCONNECTED) {
disconnectHandler.onConnectionLost(status)
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {

View file

@ -5,6 +5,7 @@ import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.provider.ContactsContract
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
@ -13,7 +14,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.ServiceD
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.FailedToConnectException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.SessionEstablishmentException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleSendSuccess
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CharacteristicType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CmdBleIO
@ -27,29 +27,36 @@ sealed class ConnectionState
object Connected : ConnectionState()
object NotConnected : ConnectionState()
class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLogger, context: Context) {
class Connection(private val podDevice: BluetoothDevice, private val aapsLogger: AAPSLogger, context: Context)
: DisconnectHandler {
private val incomingPackets = IncomingPackets()
private val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
private val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets, this)
private val gattConnection: BluetoothGatt
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
// The session is Synchronized because we can lose the connection right when establishing it
var session: Session? = null
@Synchronized get
@Synchronized set
private val cmdBleIO: CmdBleIO
private val dataBleIO: DataBleIO
init {
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}")
val autoConnect = false
gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
// OnDisconnect can be called after this point!!!
val state = waitForConnection()
if (state !is Connected) {
throw FailedToConnectException(podDevice.address)
}
}
private val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
private val discoveredCharacteristics = discoverer.discoverServices()
private val cmdBleIO = CmdBleIO(
val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
val discoveredCharacteristics = discoverer.discoverServices()
cmdBleIO = CmdBleIO(
aapsLogger,
discoveredCharacteristics[CharacteristicType.CMD]!!,
incomingPackets
@ -57,7 +64,7 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
gattConnection,
bleCommCallbacks
)
private val dataBleIO = DataBleIO(
dataBleIO = DataBleIO(
aapsLogger,
discoveredCharacteristics[CharacteristicType.DATA]!!,
incomingPackets
@ -65,10 +72,6 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
gattConnection,
bleCommCallbacks
)
val msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
var session: Session? = null
init {
val sendResult = cmdBleIO.hello()
if (sendResult !is BleSendSuccess) {
throw FailedToConnectException("Could not send HELLO command to ${podDevice.address}")
@ -77,8 +80,9 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
dataBleIO.readyToRead()
}
val msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
fun connect() {
// forces reconnection
disconnect()
if (!gattConnection.connect()) {
@ -88,9 +92,12 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
if (waitForConnection() is NotConnected) {
throw FailedToConnectException(podDevice.address)
}
val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
val discovered = discoverer.discoverServices()
dataBleIO.characteristic = discovered[CharacteristicType.DATA]!!
cmdBleIO.characteristic = discovered[CharacteristicType.CMD]!!
cmdBleIO.hello()
cmdBleIO.readyToRead()
dataBleIO.readyToRead()
@ -98,8 +105,8 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
fun disconnect() {
aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting")
bleCommCallbacks.resetConnection()
gattConnection.disconnect()
bleCommCallbacks.resetConnection()
session = null
}
@ -108,7 +115,7 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
} catch (e: InterruptedException) {
// We are still going to check if connection was successful
aapsLogger.info(LTag.PUMPBTCOMM, "Interruped while waiting for connection")
aapsLogger.info(LTag.PUMPBTCOMM, "Interrupted while waiting for connection")
}
return connectionState()
}
@ -123,11 +130,14 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
}
fun establishSession(ltk: ByteArray, msgSeq: Byte, myId: Id, podID: Id, eapSqn: ByteArray): EapSqn? {
var eapAkaExchanger = SessionEstablisher(aapsLogger, msgIO, ltk, eapSqn, myId, podID, msgSeq)
var keys = eapAkaExchanger.negotiateSessionKeys()
return when (keys) {
is SessionNegotiationResynchronization ->
keys.syncronizedEapSqn
val eapAkaExchanger = SessionEstablisher(aapsLogger, msgIO, ltk, eapSqn, myId, podID, msgSeq)
return when (val keys = eapAkaExchanger.negotiateSessionKeys()) {
is SessionNegotiationResynchronization -> {
if (BuildConfig.DEBUG) {
aapsLogger.info(LTag.PUMPCOMM, "EAP AKA resynchronization: ${keys.synchronizedEapSqn}")
}
keys.synchronizedEapSqn
}
is SessionKeys -> {
if (BuildConfig.DEBUG) {
aapsLogger.info(LTag.PUMPCOMM, "CK: ${keys.ck.toHex()}")
@ -145,6 +155,12 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
}
}
// This will be called from a different thread !!!
override fun onConnectionLost(status: Int) {
aapsLogger.info(LTag.PUMPBTCOMM, "Lost connection with status: $status")
disconnect()
}
companion object {
private const val CONNECT_TIMEOUT_MS = 7000

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
interface DisconnectHandler {
fun onConnectionLost(status: Int)
}

View file

@ -21,6 +21,10 @@ class EapSqn(val value: ByteArray) {
).long
}
override fun toString(): String {
return "EapSqn(value=${toLong()})"
}
companion object {
private const val SIZE = 6
private fun fromLong(v: Long): ByteArray {

View file

@ -50,7 +50,7 @@ class SessionEstablisher(
val newSqn = processChallengeResponse(challengeResponse)
if (newSqn != null) {
return SessionNegotiationResynchronization(
syncronizedEapSqn = newSqn,
synchronizedEapSqn = newSqn,
msgSequenceNumber = msgSeq
)
}

View file

@ -10,5 +10,5 @@ data class SessionKeys(val ck: ByteArray, val nonce: Nonce, var msgSequenceNumbe
}
}
data class SessionNegotiationResynchronization(val syncronizedEapSqn: EapSqn?, val msgSequenceNumber: Byte)
:SessionNegotiationResponse()
data class SessionNegotiationResynchronization(val synchronizedEapSqn: EapSqn, val msgSequenceNumber: Byte)
: SessionNegotiationResponse()