dash ble: handle disconnects
This commit is contained in:
parent
547454de6c
commit
5b10ad13ec
7 changed files with 80 additions and 45 deletions
|
@ -87,6 +87,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
|
|
||||||
|
|
||||||
override fun getStatus(): ConnectionStatus {
|
override fun getStatus(): ConnectionStatus {
|
||||||
|
// TODO is this used?
|
||||||
var s: ConnectionStatus
|
var s: ConnectionStatus
|
||||||
synchronized(status) {
|
synchronized(status) {
|
||||||
s = status
|
s = status
|
||||||
|
@ -113,7 +114,6 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
emitter.onNext(PodEvent.EstablishingSession)
|
emitter.onNext(PodEvent.EstablishingSession)
|
||||||
establishSession(1.toByte())
|
establishSession(1.toByte())
|
||||||
emitter.onNext(PodEvent.Connected)
|
emitter.onNext(PodEvent.Connected)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
emitter.onNext(PodEvent.AlreadyConnected(podAddress))
|
emitter.onNext(PodEvent.AlreadyConnected(podAddress))
|
||||||
}
|
}
|
||||||
|
@ -217,10 +217,8 @@ class OmnipodDashBleManagerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun disconnect() {
|
override fun disconnect() {
|
||||||
if (connection == null) {
|
|
||||||
aapsLogger.info(LTag.PUMPBTCOMM, "Trying to disconnect a null connection")
|
|
||||||
}
|
|
||||||
connection?.disconnect()
|
connection?.disconnect()
|
||||||
|
?: aapsLogger.info(LTag.PUMPBTCOMM, "Trying to disconnect a null connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
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.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.io.IncomingPackets
|
||||||
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.DisconnectHandler
|
||||||
import info.nightscout.androidaps.utils.extensions.toHex
|
import info.nightscout.androidaps.utils.extensions.toHex
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.BlockingQueue
|
import java.util.concurrent.BlockingQueue
|
||||||
|
@ -19,11 +20,19 @@ import java.util.concurrent.TimeUnit
|
||||||
class BleCommCallbacks(
|
class BleCommCallbacks(
|
||||||
private val aapsLogger: AAPSLogger,
|
private val aapsLogger: AAPSLogger,
|
||||||
private val incomingPackets: IncomingPackets,
|
private val incomingPackets: IncomingPackets,
|
||||||
|
private val disconnectHandler: DisconnectHandler,
|
||||||
) : BluetoothGattCallback() {
|
) : BluetoothGattCallback() {
|
||||||
|
|
||||||
|
// Synchronized because they can be:
|
||||||
|
// - read from various callbacks
|
||||||
|
// - written from resetConnection that is called onConnectionLost
|
||||||
private var serviceDiscoveryComplete: CountDownLatch = CountDownLatch(1)
|
private var serviceDiscoveryComplete: CountDownLatch = CountDownLatch(1)
|
||||||
|
@Synchronized get
|
||||||
|
@Synchronized set
|
||||||
private var connected: CountDownLatch = CountDownLatch(1)
|
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) {
|
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "OnConnectionStateChange with status/state: $status/$newState")
|
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) {
|
if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
connected.countDown()
|
connected.countDown()
|
||||||
}
|
}
|
||||||
|
if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||||
|
disconnectHandler.onConnectionLost(status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.bluetooth.BluetoothGatt
|
||||||
import android.bluetooth.BluetoothManager
|
import android.bluetooth.BluetoothManager
|
||||||
import android.bluetooth.BluetoothProfile
|
import android.bluetooth.BluetoothProfile
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.provider.ContactsContract
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
|
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.callbacks.BleCommCallbacks
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt
|
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.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.BleSendSuccess
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CharacteristicType
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CharacteristicType
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CmdBleIO
|
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CmdBleIO
|
||||||
|
@ -27,29 +27,36 @@ sealed class ConnectionState
|
||||||
object Connected : ConnectionState()
|
object Connected : ConnectionState()
|
||||||
object NotConnected : 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 incomingPackets = IncomingPackets()
|
||||||
private val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
|
private val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets, this)
|
||||||
private val gattConnection: BluetoothGatt
|
private val gattConnection: BluetoothGatt
|
||||||
|
|
||||||
private val bluetoothManager: BluetoothManager =
|
private val bluetoothManager: BluetoothManager =
|
||||||
context.getSystemService(Context.BLUETOOTH_SERVICE) as 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 {
|
init {
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}")
|
||||||
|
|
||||||
val autoConnect = false
|
val autoConnect = false
|
||||||
|
|
||||||
gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
|
gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
|
||||||
|
// OnDisconnect can be called after this point!!!
|
||||||
val state = waitForConnection()
|
val state = waitForConnection()
|
||||||
if (state !is Connected) {
|
if (state !is Connected) {
|
||||||
throw FailedToConnectException(podDevice.address)
|
throw FailedToConnectException(podDevice.address)
|
||||||
}
|
}
|
||||||
}
|
val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
|
||||||
|
val discoveredCharacteristics = discoverer.discoverServices()
|
||||||
private val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
|
cmdBleIO = CmdBleIO(
|
||||||
private val discoveredCharacteristics = discoverer.discoverServices()
|
|
||||||
private val cmdBleIO = CmdBleIO(
|
|
||||||
aapsLogger,
|
aapsLogger,
|
||||||
discoveredCharacteristics[CharacteristicType.CMD]!!,
|
discoveredCharacteristics[CharacteristicType.CMD]!!,
|
||||||
incomingPackets
|
incomingPackets
|
||||||
|
@ -57,7 +64,7 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
|
||||||
gattConnection,
|
gattConnection,
|
||||||
bleCommCallbacks
|
bleCommCallbacks
|
||||||
)
|
)
|
||||||
private val dataBleIO = DataBleIO(
|
dataBleIO = DataBleIO(
|
||||||
aapsLogger,
|
aapsLogger,
|
||||||
discoveredCharacteristics[CharacteristicType.DATA]!!,
|
discoveredCharacteristics[CharacteristicType.DATA]!!,
|
||||||
incomingPackets
|
incomingPackets
|
||||||
|
@ -65,10 +72,6 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
|
||||||
gattConnection,
|
gattConnection,
|
||||||
bleCommCallbacks
|
bleCommCallbacks
|
||||||
)
|
)
|
||||||
val msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
|
|
||||||
var session: Session? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
val sendResult = cmdBleIO.hello()
|
val sendResult = cmdBleIO.hello()
|
||||||
if (sendResult !is BleSendSuccess) {
|
if (sendResult !is BleSendSuccess) {
|
||||||
throw FailedToConnectException("Could not send HELLO command to ${podDevice.address}")
|
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()
|
dataBleIO.readyToRead()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val msgIO = MessageIO(aapsLogger, cmdBleIO, dataBleIO)
|
||||||
|
|
||||||
fun connect() {
|
fun connect() {
|
||||||
// forces reconnection
|
|
||||||
disconnect()
|
disconnect()
|
||||||
|
|
||||||
if (!gattConnection.connect()) {
|
if (!gattConnection.connect()) {
|
||||||
|
@ -88,9 +92,12 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
|
||||||
if (waitForConnection() is NotConnected) {
|
if (waitForConnection() is NotConnected) {
|
||||||
throw FailedToConnectException(podDevice.address)
|
throw FailedToConnectException(podDevice.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
|
||||||
val discovered = discoverer.discoverServices()
|
val discovered = discoverer.discoverServices()
|
||||||
dataBleIO.characteristic = discovered[CharacteristicType.DATA]!!
|
dataBleIO.characteristic = discovered[CharacteristicType.DATA]!!
|
||||||
cmdBleIO.characteristic = discovered[CharacteristicType.CMD]!!
|
cmdBleIO.characteristic = discovered[CharacteristicType.CMD]!!
|
||||||
|
|
||||||
cmdBleIO.hello()
|
cmdBleIO.hello()
|
||||||
cmdBleIO.readyToRead()
|
cmdBleIO.readyToRead()
|
||||||
dataBleIO.readyToRead()
|
dataBleIO.readyToRead()
|
||||||
|
@ -98,8 +105,8 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
|
||||||
|
|
||||||
fun disconnect() {
|
fun disconnect() {
|
||||||
aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting")
|
aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting")
|
||||||
bleCommCallbacks.resetConnection()
|
|
||||||
gattConnection.disconnect()
|
gattConnection.disconnect()
|
||||||
|
bleCommCallbacks.resetConnection()
|
||||||
session = null
|
session = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +115,7 @@ class Connection(val podDevice: BluetoothDevice, private val aapsLogger: AAPSLog
|
||||||
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
|
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
// We are still going to check if connection was successful
|
// 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()
|
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? {
|
fun establishSession(ltk: ByteArray, msgSeq: Byte, myId: Id, podID: Id, eapSqn: ByteArray): EapSqn? {
|
||||||
var eapAkaExchanger = SessionEstablisher(aapsLogger, msgIO, ltk, eapSqn, myId, podID, msgSeq)
|
val eapAkaExchanger = SessionEstablisher(aapsLogger, msgIO, ltk, eapSqn, myId, podID, msgSeq)
|
||||||
var keys = eapAkaExchanger.negotiateSessionKeys()
|
return when (val keys = eapAkaExchanger.negotiateSessionKeys()) {
|
||||||
return when (keys) {
|
is SessionNegotiationResynchronization -> {
|
||||||
is SessionNegotiationResynchronization ->
|
if (BuildConfig.DEBUG) {
|
||||||
keys.syncronizedEapSqn
|
aapsLogger.info(LTag.PUMPCOMM, "EAP AKA resynchronization: ${keys.synchronizedEapSqn}")
|
||||||
|
}
|
||||||
|
keys.synchronizedEapSqn
|
||||||
|
}
|
||||||
is SessionKeys -> {
|
is SessionKeys -> {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
aapsLogger.info(LTag.PUMPCOMM, "CK: ${keys.ck.toHex()}")
|
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 {
|
companion object {
|
||||||
|
|
||||||
private const val CONNECT_TIMEOUT_MS = 7000
|
private const val CONNECT_TIMEOUT_MS = 7000
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
|
||||||
|
|
||||||
|
interface DisconnectHandler {
|
||||||
|
fun onConnectionLost(status: Int)
|
||||||
|
}
|
|
@ -21,6 +21,10 @@ class EapSqn(val value: ByteArray) {
|
||||||
).long
|
).long
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "EapSqn(value=${toLong()})"
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SIZE = 6
|
private const val SIZE = 6
|
||||||
private fun fromLong(v: Long): ByteArray {
|
private fun fromLong(v: Long): ByteArray {
|
||||||
|
|
|
@ -50,7 +50,7 @@ class SessionEstablisher(
|
||||||
val newSqn = processChallengeResponse(challengeResponse)
|
val newSqn = processChallengeResponse(challengeResponse)
|
||||||
if (newSqn != null) {
|
if (newSqn != null) {
|
||||||
return SessionNegotiationResynchronization(
|
return SessionNegotiationResynchronization(
|
||||||
syncronizedEapSqn = newSqn,
|
synchronizedEapSqn = newSqn,
|
||||||
msgSequenceNumber = msgSeq
|
msgSequenceNumber = msgSeq
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,5 @@ data class SessionKeys(val ck: ByteArray, val nonce: Nonce, var msgSequenceNumbe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SessionNegotiationResynchronization(val syncronizedEapSqn: EapSqn?, val msgSequenceNumber: Byte)
|
data class SessionNegotiationResynchronization(val synchronizedEapSqn: EapSqn, val msgSequenceNumber: Byte)
|
||||||
:SessionNegotiationResponse()
|
: SessionNegotiationResponse()
|
Loading…
Reference in a new issue