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:
parent
4046828567
commit
e7a9e24093
11 changed files with 153 additions and 75 deletions
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,50 +140,77 @@ 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 {
|
||||||
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")
|
if (podState.bluetoothAddress == null) {
|
||||||
|
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")
|
||||||
|
|
||||||
val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
|
val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
|
||||||
emitter.onNext(PodEvent.Scanning)
|
emitter.onNext(PodEvent.Scanning)
|
||||||
|
|
||||||
val podAddress = podScanner.scanForPod(
|
val podAddress = podScanner.scanForPod(
|
||||||
PodScanner.SCAN_FOR_SERVICE_UUID,
|
PodScanner.SCAN_FOR_SERVICE_UUID,
|
||||||
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, myId, podId, Id.fromLong(PodScanner.POD_ID_NOT_ACTIVATED))
|
||||||
|
if (podState.ltk == null) {
|
||||||
|
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 ltkExchanger = LTKExchanger(aapsLogger, mIO)
|
val ltk: ByteArray = podState.ltk!!
|
||||||
|
|
||||||
emitter.onNext(PodEvent.Pairing)
|
|
||||||
|
|
||||||
val ltk = ltkExchanger.negotiateLTK()
|
|
||||||
|
|
||||||
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()
|
||||||
aapsLogger.info(LTag.PUMPCOMM, "CK: ${keys.ck.toHex()}")
|
podState.commitEapAkaSequenceNumber()
|
||||||
aapsLogger.info(LTag.PUMPCOMM, "msgSequenceNumber: ${keys.msgSequenceNumber}")
|
|
||||||
aapsLogger.info(LTag.PUMPCOMM, "Nonce: ${keys.nonce}")
|
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
aapsLogger.info(LTag.PUMPCOMM, "CK: ${keys.ck.toHex()}")
|
||||||
|
aapsLogger.info(LTag.PUMPCOMM, "msgSequenceNumber: ${keys.msgSequenceNumber}")
|
||||||
|
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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()}" }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue