dash: start using podState for BLE

implement disconnect()
various fixes after testing with a real pod(cmd is 2 bytes, message
joining, etc)
This commit is contained in:
Andrei Vereha 2021-03-06 21:12:20 +01:00
parent 4046828567
commit e7a9e24093
11 changed files with 153 additions and 75 deletions

View file

@ -36,5 +36,8 @@ data class Id(val address: ByteArray) {
fun fromInt(v: Int): Id { fun fromInt(v: Int): Id {
return Id(ByteBuffer.allocate(4).putInt(v).array()) return Id(ByteBuffer.allocate(4).putInt(v).array())
} }
fun fromLong(v: Long): Id {
return Id(ByteBuffer.allocate(8).putLong(v).array().copyOfRange(4,8))
}
} }
} }

View file

@ -2,11 +2,13 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
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 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.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.command.BleCommandHello import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommandHello
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.EnDecrypt
@ -16,13 +18,13 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.Chara
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.LTKExchanger import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.LTKExchanger
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.EapSqn
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.Session import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.Session
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.SessionEstablisher import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.SessionEstablisher
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.SessionKeys import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.SessionKeys
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status.ConnectionStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status.ConnectionStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.utils.extensions.toHex import info.nightscout.androidaps.utils.extensions.toHex
import io.reactivex.Observable import io.reactivex.Observable
import java.util.concurrent.BlockingQueue import java.util.concurrent.BlockingQueue
@ -34,7 +36,8 @@ import javax.inject.Singleton
@Singleton @Singleton
class OmnipodDashBleManagerImpl @Inject constructor( class OmnipodDashBleManagerImpl @Inject constructor(
private val context: Context, private val context: Context,
private val aapsLogger: AAPSLogger private val aapsLogger: AAPSLogger,
private val podState: OmnipodDashPodStateManager
) : OmnipodDashBleManager { ) : OmnipodDashBleManager {
private val bluetoothManager: BluetoothManager = private val bluetoothManager: BluetoothManager =
@ -42,6 +45,8 @@ class OmnipodDashBleManagerImpl @Inject constructor(
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
private var sessionKeys: SessionKeys? = null private var sessionKeys: SessionKeys? = null
private var msgIO: MessageIO? = null private var msgIO: MessageIO? = null
private var gatt: BluetoothGatt? = null
private var status: ConnectionStatus = ConnectionStatus.IDLE
@Throws( @Throws(
FailedToConnectException::class, FailedToConnectException::class,
@ -54,30 +59,29 @@ class OmnipodDashBleManagerImpl @Inject constructor(
DescriptorNotFoundException::class, DescriptorNotFoundException::class,
CouldNotConfirmDescriptorWriteException::class CouldNotConfirmDescriptorWriteException::class
) )
private fun connect(podAddress: String): BleIO { private fun connect(podDevice: BluetoothDevice): BleIO {
// TODO: locking?
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> = val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
mapOf( mapOf(
CharacteristicType.CMD to LinkedBlockingDeque(), CharacteristicType.CMD to LinkedBlockingDeque(),
CharacteristicType.DATA to LinkedBlockingDeque() CharacteristicType.DATA to LinkedBlockingDeque()
) )
val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets) val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to $podAddress") aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}")
val autoConnect = false // TODO: check what to use here val autoConnect = false // TODO: check what to use here
val gatt = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE) val gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS) bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
val connectionState = bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT) val connectionState = bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT)
aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: $connectionState") aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: $connectionState")
if (connectionState != BluetoothProfile.STATE_CONNECTED) { if (connectionState != BluetoothProfile.STATE_CONNECTED) {
throw FailedToConnectException(podAddress) throw FailedToConnectException(podDevice.address)
} }
val discoverer = ServiceDiscoverer(aapsLogger, gatt, bleCommCallbacks) val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
val chars = discoverer.discoverServices() val chars = discoverer.discoverServices()
val bleIO = BleIO(aapsLogger, chars, incomingPackets, gatt, bleCommCallbacks) val bleIO = BleIO(aapsLogger, chars, incomingPackets, gattConnection, bleCommCallbacks)
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandHello(CONTROLLER_ID).data) bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandHello(CONTROLLER_ID).data)
bleIO.readyToRead() bleIO.readyToRead()
gatt = gattConnection
return bleIO return bleIO
} }
@ -117,7 +121,11 @@ class OmnipodDashBleManagerImpl @Inject constructor(
} }
override fun getStatus(): ConnectionStatus { override fun getStatus(): ConnectionStatus {
TODO("not implemented") var s: ConnectionStatus
synchronized(status) {
s = status
}
return s
} }
@Throws( @Throws(
@ -132,11 +140,12 @@ class OmnipodDashBleManagerImpl @Inject constructor(
DescriptorNotFoundException::class, DescriptorNotFoundException::class,
CouldNotConfirmDescriptorWriteException::class CouldNotConfirmDescriptorWriteException::class
) )
override fun connect(): Observable<PodEvent> = Observable.create { emitter -> override fun connect(): Observable<PodEvent> = Observable.create { emitter ->
// TODO: when we are already connected, // TODO: when we are already connected,
// emit PodEvent.AlreadyConnected, complete the observable and return from this method // emit PodEvent.AlreadyConnected, complete the observable and return from this method
try { try {
if (podState.bluetoothAddress == null) {
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation") aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")
val podScanner = PodScanner(aapsLogger, bluetoothAdapter) val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
@ -147,35 +156,61 @@ class OmnipodDashBleManagerImpl @Inject constructor(
PodScanner.POD_ID_NOT_ACTIVATED PodScanner.POD_ID_NOT_ACTIVATED
).scanResult.device.address ).scanResult.device.address
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB"; // For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
podState.bluetoothAddress = podAddress
}
emitter.onNext(PodEvent.BluetoothConnecting) emitter.onNext(PodEvent.BluetoothConnecting)
val podAddress = podState.bluetoothAddress ?: throw FailedToConnectException("Lost connection")
// check if already connected
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
val connectionState = bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT)
aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: $connectionState")
val bleIO = connect(podAddress)
emitter.onNext(PodEvent.BluetoothConnected(podAddress)) emitter.onNext(PodEvent.BluetoothConnected(podAddress))
if (connectionState == BluetoothProfile.STATE_CONNECTED) {
podState.uniqueId ?: throw FailedToConnectException("Already connection and uniqueId is missing")
emitter.onNext(PodEvent.AlreadyConnected(podAddress, podState.uniqueId ?: 0))
emitter.onComplete()
return@create
}
if (msgIO != null) {
disconnect()
}
val bleIO = connect(podDevice)
val mIO = MessageIO(aapsLogger, bleIO) val mIO = MessageIO(aapsLogger, bleIO)
val myId = Id.fromInt(CONTROLLER_ID) val myId = Id.fromInt(CONTROLLER_ID)
val podId = myId.increment() val podId = myId.increment()
var msgSeq = 1.toByte()
val ltkExchanger = LTKExchanger(aapsLogger, mIO) val ltkExchanger = LTKExchanger(aapsLogger, mIO, myId, podId, Id.fromLong(PodScanner.POD_ID_NOT_ACTIVATED))
if (podState.ltk == null) {
emitter.onNext(PodEvent.Pairing) emitter.onNext(PodEvent.Pairing)
val pairResult = ltkExchanger.negotiateLTK()
podState.ltk = pairResult.ltk
podState.uniqueId = podId.toLong()
msgSeq = pairResult.msgSeq
podState.eapAkaSequenceNumber = 1
if (BuildConfig.DEBUG) {
aapsLogger.info(LTag.PUMPCOMM, "Got LTK: ${pairResult.ltk.toHex()}")
}
}
val ltk = ltkExchanger.negotiateLTK() val ltk: ByteArray = podState.ltk!!
emitter.onNext(PodEvent.EstablishingSession) emitter.onNext(PodEvent.EstablishingSession)
val eapSqn = podState.increaseEapAkaSequenceNumber()
val eapSqn = EapSqn(1) val eapAkaExchanger = SessionEstablisher(aapsLogger, mIO, ltk, eapSqn, myId, podId, msgSeq)
aapsLogger.info(LTag.PUMPCOMM, "Got LTK: ${ltk.ltk.toHex()}")
val eapAkaExchanger = SessionEstablisher(aapsLogger, mIO, ltk, eapSqn)
val keys = eapAkaExchanger.negotiateSessionKeys() val keys = eapAkaExchanger.negotiateSessionKeys()
podState.commitEapAkaSequenceNumber()
if (BuildConfig.DEBUG) {
aapsLogger.info(LTag.PUMPCOMM, "CK: ${keys.ck.toHex()}") aapsLogger.info(LTag.PUMPCOMM, "CK: ${keys.ck.toHex()}")
aapsLogger.info(LTag.PUMPCOMM, "msgSequenceNumber: ${keys.msgSequenceNumber}") aapsLogger.info(LTag.PUMPCOMM, "msgSequenceNumber: ${keys.msgSequenceNumber}")
aapsLogger.info(LTag.PUMPCOMM, "Nonce: ${keys.nonce}") aapsLogger.info(LTag.PUMPCOMM, "Nonce: ${keys.nonce}")
}
sessionKeys = keys sessionKeys = keys
msgIO = mIO msgIO = mIO
emitter.onNext(PodEvent.Connected(ltk.podId.toLong())) emitter.onNext(PodEvent.Connected(podId.toLong()))
emitter.onComplete() emitter.onComplete()
} catch (ex: Exception) { } catch (ex: Exception) {
@ -184,7 +219,11 @@ class OmnipodDashBleManagerImpl @Inject constructor(
} }
override fun disconnect() { override fun disconnect() {
TODO("not implemented") val localGatt = gatt
localGatt?.close()
gatt = null
msgIO = null
sessionKeys = null
} }
companion object { companion object {

View file

@ -11,9 +11,10 @@ class BleCommandAbort : BleCommand(BleCommandType.ABORT)
class BleCommandSuccess : BleCommand(BleCommandType.SUCCESS) class BleCommandSuccess : BleCommand(BleCommandType.SUCCESS)
class BleCommandFail : BleCommand(BleCommandType.FAIL) class BleCommandFail : BleCommand(BleCommandType.FAIL)
open class BleCommand(val data: ByteArray) { open class BleCommand(val data: ByteArray) {
constructor(type: BleCommandType) : this(byteArrayOf(type.value)) constructor(type: BleCommandType) : this(byteArrayOf(type.value, 0))
constructor(type: BleCommandType, payload: ByteArray) : this( constructor(type: BleCommandType, payload: ByteArray) : this(
byteArrayOf(type.value) + payload byteArrayOf(type.value) + payload

View file

@ -32,7 +32,7 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
fullFragments == 0 -> { fullFragments == 0 -> {
crc = ByteBuffer.wrap(firstPacket.copyOfRange(2, 6)).int.toUnsignedLong() crc = ByteBuffer.wrap(firstPacket.copyOfRange(2, 6)).int.toUnsignedLong()
val rest = firstPacket[6] val rest = firstPacket[6]
val end = min(rest + FirstBlePacket.HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, BlePacket.MAX_SIZE) val end = min(rest + FirstBlePacket.HEADER_SIZE_WITHOUT_MIDDLE_PACKETS, firstPacket.size)
oneExtraPacket = rest + FirstBlePacket.HEADER_SIZE_WITHOUT_MIDDLE_PACKETS > end oneExtraPacket = rest + FirstBlePacket.HEADER_SIZE_WITHOUT_MIDDLE_PACKETS > end
if (end > firstPacket.size) { if (end > firstPacket.size) {
throw IncorrectPacketException(0, firstPacket) throw IncorrectPacketException(0, firstPacket)
@ -78,12 +78,12 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
} }
crc = ByteBuffer.wrap(packet.copyOfRange(2, 6)).int.toUnsignedLong() crc = ByteBuffer.wrap(packet.copyOfRange(2, 6)).int.toUnsignedLong()
val rest = packet[1].toInt() val rest = packet[1].toInt()
val end = min(rest + LastBlePacket.HEADER_SIZE, BlePacket.MAX_SIZE) val end = min(rest + LastBlePacket.HEADER_SIZE, packet.size)
oneExtraPacket = rest + LastBlePacket.HEADER_SIZE > end oneExtraPacket = rest + LastBlePacket.HEADER_SIZE > end
if (packet.size < end) { if (packet.size < end) {
throw IncorrectPacketException(idx.toByte(), packet) throw IncorrectPacketException(idx.toByte(), packet)
} }
fragments.add(packet.copyOfRange(LastBlePacket.HEADER_SIZE, packet.size)) fragments.add(packet.copyOfRange(LastBlePacket.HEADER_SIZE, end))
} }
idx > fullFragments -> { // this is the extra fragment idx > fullFragments -> { // this is the extra fragment

View file

@ -18,7 +18,7 @@ import org.spongycastle.crypto.macs.CMac
import org.spongycastle.crypto.params.KeyParameter import org.spongycastle.crypto.params.KeyParameter
import java.security.SecureRandom import java.security.SecureRandom
internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgIO: MessageIO) { internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgIO: MessageIO, val myId: Id, val podId: Id, val podAddress: Id) {
private val pdmPrivate = X25519.generatePrivateKey() private val pdmPrivate = X25519.generatePrivateKey()
private val pdmPublic = X25519.publicFromPrivate(pdmPrivate) private val pdmPublic = X25519.publicFromPrivate(pdmPrivate)
@ -27,8 +27,6 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
private val pdmNonce = ByteArray(NONCE_SIZE) private val pdmNonce = ByteArray(NONCE_SIZE)
private val pdmConf = ByteArray(CMAC_SIZE) private val pdmConf = ByteArray(CMAC_SIZE)
private val podConf = ByteArray(CMAC_SIZE) private val podConf = ByteArray(CMAC_SIZE)
private val controllerId = Id.fromInt(OmnipodDashBleManagerImpl.CONTROLLER_ID)
val nodeId = controllerId.increment()
private var seq: Byte = 1 private var seq: Byte = 1
private var ltk = ByteArray(CMAC_SIZE) private var ltk = ByteArray(CMAC_SIZE)
@ -39,7 +37,7 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
fun negotiateLTK(): PairResult { fun negotiateLTK(): PairResult {
// send SP1, SP2 // send SP1, SP2
val sp1sp2 = sp1sp2(nodeId.address, sp2()) val sp1sp2 = sp1sp2(podId.address, sp2())
msgIO.sendMessage(sp1sp2.messagePacket) msgIO.sendMessage(sp1sp2.messagePacket)
seq++ seq++
@ -76,7 +74,6 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
return PairResult( return PairResult(
ltk = ltk, ltk = ltk,
podId = nodeId,
msgSeq = seq msgSeq = seq
) )
} }
@ -88,8 +85,8 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
) )
return PairMessage( return PairMessage(
sequenceNumber = seq, sequenceNumber = seq,
source = controllerId, source = myId,
destination = nodeId, destination = podAddress,
payload = payload payload = payload
) )
} }
@ -101,8 +98,8 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
) )
return PairMessage( return PairMessage(
sequenceNumber = seq, sequenceNumber = seq,
source = controllerId, source = myId,
destination = nodeId, destination = podAddress,
payload = payload payload = payload
) )
} }
@ -125,8 +122,8 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
) )
return PairMessage( return PairMessage(
sequenceNumber = seq, sequenceNumber = seq,
source = controllerId, source = myId,
destination = nodeId, destination = podAddress,
payload = payload payload = payload
) )
} }
@ -159,8 +156,8 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
val payload = SP0GP0.toByteArray() val payload = SP0GP0.toByteArray()
return PairMessage( return PairMessage(
sequenceNumber = seq, sequenceNumber = seq,
source = controllerId, source = myId,
destination = nodeId, destination = podAddress,
payload = payload payload = payload
) )
} }

View file

@ -3,7 +3,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
import info.nightscout.androidaps.utils.extensions.toHex import info.nightscout.androidaps.utils.extensions.toHex
data class PairResult(val ltk: ByteArray, val podId: Id, val msgSeq: Byte) { data class PairResult(val ltk: ByteArray, val msgSeq: Byte) {
init { init {
require(ltk.size == 16) { "LTK length must be 16 bytes. Received LTK: ${ltk.toHex()}" } require(ltk.size == 16) { "LTK length must be 16 bytes. Received LTK: ${ltk.toHex()}" }
} }

View file

@ -78,7 +78,7 @@ data class EapMessage(
throw MessageIOException("Invalid eap payload. Expected AKA packet type: ${payload.toHex()}") throw MessageIOException("Invalid eap payload. Expected AKA packet type: ${payload.toHex()}")
} }
val attributesPayload = payload.copyOfRange(8, totalSize) val attributesPayload = payload.copyOfRange(8, totalSize)
aapsLogger.debug(LTag.PUMPBTCOMM, "EAP attributes: ${attributesPayload.toByteString()}") aapsLogger.debug(LTag.PUMPBTCOMM, "parsing EAP payload: ${payload.toHex()}")
return EapMessage( return EapMessage(
code = EapCode.byValue(payload[0]), code = EapCode.byValue(payload[0]),
identifier = payload[1], identifier = payload[1],

View file

@ -3,32 +3,33 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
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.driver.comm.Id import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.Nonce import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt.Nonce
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.SessionEstablishmentException import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.SessionEstablishmentException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.PairResult
import info.nightscout.androidaps.utils.extensions.toHex import info.nightscout.androidaps.utils.extensions.toHex
import java.security.SecureRandom import java.security.SecureRandom
class SessionEstablisher( class SessionEstablisher(
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
private val msgIO: MessageIO, private val msgIO: MessageIO,
private val ltk: PairResult, private val ltk: ByteArray,
private val eapSqn: EapSqn private val eapSqn: ByteArray,
private val myId: Id,
private val podId: Id,
private var msgSeq: Byte
) { ) {
var sequenceNumber = ltk.msgSeq
private val controllerIV = ByteArray(IV_SIZE) private val controllerIV = ByteArray(IV_SIZE)
private var nodeIV = ByteArray(IV_SIZE) private var nodeIV = ByteArray(IV_SIZE)
private val controllerId = Id.fromInt(OmnipodDashBleManagerImpl.CONTROLLER_ID) private val milenage = Milenage(aapsLogger, ltk, eapSqn)
private val milenage = Milenage(aapsLogger, ltk.ltk, eapSqn.increment())
init { init {
require(eapSqn.size == 6) {"EAP-SQN has to be 6 bytes long"}
require(ltk.size == 16) {"LTK has to be 16 bytes long"}
aapsLogger.debug(LTag.PUMPBTCOMM, "Starting EAP-AKA") aapsLogger.debug(LTag.PUMPBTCOMM, "Starting EAP-AKA")
val random = SecureRandom() val random = SecureRandom()
random.nextBytes(controllerIV) random.nextBytes(controllerIV)
@ -36,14 +37,14 @@ class SessionEstablisher(
fun negotiateSessionKeys(): SessionKeys { fun negotiateSessionKeys(): SessionKeys {
// send EAP-AKA challenge // send EAP-AKA challenge
sequenceNumber++ // TODO: get from pod state. This only works for activating a new pod msgSeq++ // TODO: get from pod state. This only works for activating a new pod
var challenge = eapAkaChallenge() var challenge = eapAkaChallenge()
msgIO.sendMessage(challenge) msgIO.sendMessage(challenge)
val challengeResponse = msgIO.receiveMessage() val challengeResponse = msgIO.receiveMessage()
processChallengeResponse(challengeResponse) // TODO: what do we have to answer if challenge response does not validate? processChallengeResponse(challengeResponse) // TODO: what do we have to answer if challenge response does not validate?
sequenceNumber++ msgSeq++
var success = eapSuccess() var success = eapSuccess()
msgIO.sendMessage(success) msgIO.sendMessage(success)
@ -53,7 +54,7 @@ class SessionEstablisher(
prefix = controllerIV + nodeIV, prefix = controllerIV + nodeIV,
sqn = 0 sqn = 0
), ),
msgSequenceNumber = sequenceNumber msgSequenceNumber = msgSeq
) )
} }
@ -66,14 +67,14 @@ class SessionEstablisher(
val eapMsg = EapMessage( val eapMsg = EapMessage(
code = EapCode.REQUEST, code = EapCode.REQUEST,
identifier = 42, // TODO: find what value we need here, it's probably random identifier = 189.toByte(), // TODO: find what value we need here, it's probably random
attributes = attributes attributes = attributes
) )
return MessagePacket( return MessagePacket(
type = MessageType.SESSION_ESTABLISHMENT, type = MessageType.SESSION_ESTABLISHMENT,
sequenceNumber = sequenceNumber, sequenceNumber = msgSeq,
source = controllerId, source = myId,
destination = ltk.podId, destination = podId,
payload = eapMsg.toByteArray() payload = eapMsg.toByteArray()
) )
} }
@ -103,14 +104,14 @@ class SessionEstablisher(
val eapMsg = EapMessage( val eapMsg = EapMessage(
code = EapCode.SUCCESS, code = EapCode.SUCCESS,
attributes = arrayOf(), attributes = arrayOf(),
identifier = 44 // TODO: find what value we need here identifier = 189.toByte() // TODO: find what value we need here
) )
return MessagePacket( return MessagePacket(
type = MessageType.SESSION_ESTABLISHMENT, type = MessageType.SESSION_ESTABLISHMENT,
sequenceNumber = sequenceNumber, sequenceNumber = msgSeq,
source = controllerId, source = myId,
destination = ltk.podId, destination = podId,
payload = eapMsg.toByteArray() payload = eapMsg.toByteArray()
) )
} }

View file

@ -1,6 +1,10 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status
enum class ConnectionStatus { enum class ConnectionStatus {
CONNECTED, IDLE,
NOT_CONNECTED; BUSY,
CONNECTING,
ESTABLISHING_SESSION,
PAIRING,
RUNNING_COMMAND;
} }

View file

@ -23,6 +23,8 @@ interface OmnipodDashPodStateManager {
val activationTime: Long? val activationTime: Long?
var uniqueId: Long? // TODO make Int var uniqueId: Long? // TODO make Int
var bluetoothAddress: String? var bluetoothAddress: String?
var ltk: ByteArray?
var eapAkaSequenceNumber: Long
val bluetoothVersion: SoftwareVersion? val bluetoothVersion: SoftwareVersion?
val firmwareVersion: SoftwareVersion? val firmwareVersion: SoftwareVersion?
@ -46,6 +48,8 @@ interface OmnipodDashPodStateManager {
val basalProgram: BasalProgram? val basalProgram: BasalProgram?
fun increaseMessageSequenceNumber() fun increaseMessageSequenceNumber()
fun increaseEapAkaSequenceNumber():ByteArray
fun commitEapAkaSequenceNumber()
fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) fun updateFromDefaultStatusResponse(response: DefaultStatusResponse)
fun updateFromVersionResponse(response: VersionResponse) fun updateFromVersionResponse(response: VersionResponse)
fun updateFromSetUniqueIdResponse(response: SetUniqueIdResponse) fun updateFromSetUniqueIdResponse(response: SetUniqueIdResponse)

View file

@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.io.Serializable import java.io.Serializable
import java.nio.ByteBuffer
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -150,6 +151,32 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
store() store()
} }
override var eapAkaSequenceNumber: Long
get() = podState.eapAkaSequenceNumber
set(value) {
podState.eapAkaSequenceNumber = value
store()
}
override var ltk: ByteArray?
get() = podState.ltk
set(value) {
podState.ltk = value
store()
}
override fun increaseEapAkaSequenceNumber():ByteArray {
podState.eapAkaSequenceNumber++
return ByteBuffer.allocate(8)
.putLong(podState.eapAkaSequenceNumber)
.array()
.copyOfRange(2, 8)
}
override fun commitEapAkaSequenceNumber() {
store()
}
override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) { override fun updateFromDefaultStatusResponse(response: DefaultStatusResponse) {
podState.deliveryStatus = response.deliveryStatus podState.deliveryStatus = response.deliveryStatus
podState.podStatus = response.podStatus podState.podStatus = response.podStatus
@ -262,6 +289,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var activationTime: Long? = null var activationTime: Long? = null
var uniqueId: Long? = null var uniqueId: Long? = null
var bluetoothAddress: String? = null var bluetoothAddress: String? = null
var ltk: ByteArray? = null
var eapAkaSequenceNumber: Long = 1
var bleVersion: SoftwareVersion? = null var bleVersion: SoftwareVersion? = null
var firmwareVersion: SoftwareVersion? = null var firmwareVersion: SoftwareVersion? = null