dash ble: detekt

This commit is contained in:
Andrei Vereha 2021-04-04 16:06:27 +02:00
parent c1426941cf
commit 6d2d5a7e76
7 changed files with 98 additions and 106 deletions

View file

@ -148,6 +148,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
it == mapProfileToBasalProgram(profile) it == mapProfileToBasalProgram(profile)
} ?: true } ?: true
override fun lastDataTime(): Long { override fun lastDataTime(): Long {
return podStateManager.lastConnection return podStateManager.lastConnection
} }

View file

@ -46,9 +46,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
throw BusyException() throw BusyException()
} }
try { try {
val conn = connection ?: throw NotConnectedException("Not connected") val session = assertSessionEstablished()
val session = conn.session ?: throw NotConnectedException("Missing session")
emitter.onNext(PodEvent.CommandSending(cmd)) emitter.onNext(PodEvent.CommandSending(cmd))
when (session.sendCommand(cmd)) { when (session.sendCommand(cmd)) {
@ -84,6 +82,12 @@ class OmnipodDashBleManagerImpl @Inject constructor(
} }
} }
private fun assertSessionEstablished(): Session {
val conn = assertConnected()
return conn.session
?: throw NotConnectedException("Missing session")
}
override fun getStatus(): ConnectionStatus { override fun getStatus(): ConnectionStatus {
// TODO is this used? // TODO is this used?
var s: ConnectionStatus var s: ConnectionStatus
@ -135,8 +139,9 @@ class OmnipodDashBleManagerImpl @Inject constructor(
} }
private fun establishSession(msgSeq: Byte) { private fun establishSession(msgSeq: Byte) {
val conn = connection ?: throw FailedToConnectException("connection lost") val conn = assertConnected()
val ltk: ByteArray = podState.ltk ?: throw FailedToConnectException("Missing LTK, activate the pod first")
val ltk = assertPaired()
val uniqueId = podState.uniqueId val uniqueId = podState.uniqueId
val podId = uniqueId?.let { Id.fromLong(uniqueId) } val podId = uniqueId?.let { Id.fromLong(uniqueId) }
@ -149,7 +154,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
if (newSqn != null) { if (newSqn != null) {
aapsLogger.info(LTag.PUMPBTCOMM, "Updating EAP SQN to: $newSqn") aapsLogger.info(LTag.PUMPBTCOMM, "Updating EAP SQN to: $newSqn")
podState.eapAkaSequenceNumber = newSqn.toLong() podState.eapAkaSequenceNumber = newSqn.toLong()
var newSqn = conn.establishSession(ltk, msgSeq, myId, podId, podState.increaseEapAkaSequenceNumber()) newSqn = conn.establishSession(ltk, msgSeq, myId, podId, podState.increaseEapAkaSequenceNumber())
if (newSqn != null) { if (newSqn != null) {
throw SessionEstablishmentException("Received resynchronization SQN for the second time") throw SessionEstablishmentException("Received resynchronization SQN for the second time")
} }
@ -158,6 +163,16 @@ class OmnipodDashBleManagerImpl @Inject constructor(
podState.commitEapAkaSequenceNumber() podState.commitEapAkaSequenceNumber()
} }
private fun assertPaired(): ByteArray {
return podState.ltk
?: throw FailedToConnectException("Missing LTK, activate the pod first")
}
private fun assertConnected(): Connection {
return connection
?: throw FailedToConnectException("connection lost")
}
override fun pairNewPod(): Observable<PodEvent> = Observable.create { emitter -> override fun pairNewPod(): Observable<PodEvent> = Observable.create { emitter ->
if (!busy.compareAndSet(false, true)) { if (!busy.compareAndSet(false, true)) {
throw BusyException() throw BusyException()

View file

@ -15,7 +15,7 @@ data class MessagePacket(
val sequenceNumber: Byte, val sequenceNumber: Byte,
val ack: Boolean = false, val ack: Boolean = false,
val ackNumber: Byte = 0.toByte(), val ackNumber: Byte = 0.toByte(),
val eqos: Short = 0.toShort(), // TODO: understand. Seems to be set to 1 for commands val eqos: Short = 0.toShort(),
val priority: Boolean = false, val priority: Boolean = false,
val lastMessage: Boolean = false, val lastMessage: Boolean = false,
val gateway: Boolean = false, val gateway: Boolean = false,

View file

@ -42,7 +42,7 @@ class PayloadJoiner(private val firstPacket: ByteArray) {
oneExtraPacket = lastPacket.oneExtraPacket oneExtraPacket = lastPacket.oneExtraPacket
} }
idx > fullFragments && oneExtraPacket -> { idx == fullFragments+1 && oneExtraPacket -> {
fragments.add(LastOptionalPlusOneBlePacket.parse(packet)) fragments.add(LastOptionalPlusOneBlePacket.parse(packet))
} }

View file

@ -6,29 +6,11 @@ import java.util.zip.CRC32
internal class PayloadSplitter(private val payload: ByteArray) { internal class PayloadSplitter(private val payload: ByteArray) {
fun splitInPackets(): List<BlePacket> { fun splitInPackets(): List<BlePacket> {
if (payload.size <= FirstBlePacket.CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET) {
return splitInOnePacket()
}
val ret = ArrayList<BlePacket>() val ret = ArrayList<BlePacket>()
val crc32 = payload.crc32() val crc32 = payload.crc32()
if (payload.size <= FirstBlePacket.CAPACITY_WITH_THE_OPTIONAL_PLUS_ONE_PACKET) {
val end = min(FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS, payload.size)
ret.add(
FirstBlePacket(
fullFragments = 0,
payload = payload.copyOfRange(0, end),
size = payload.size.toByte(),
crc32 = crc32
)
)
if (payload.size > FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS) {
ret.add(
LastOptionalPlusOneBlePacket(
index = 1,
payload = payload.copyOfRange(end, payload.size),
size = (payload.size - end).toByte()
)
)
}
return ret
}
val middleFragments = (payload.size - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS) / MiddleBlePacket.CAPACITY val middleFragments = (payload.size - FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS) / MiddleBlePacket.CAPACITY
val rest = val rest =
( (
@ -42,17 +24,10 @@ internal class PayloadSplitter(private val payload: ByteArray) {
) )
) )
for (i in 1..middleFragments) { for (i in 1..middleFragments) {
val p = if (i == 1) { val p = payload.copyOfRange(
payload.copyOfRange(
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS,
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + MiddleBlePacket.CAPACITY
)
} else {
payload.copyOfRange(
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + (i - 1) * MiddleBlePacket.CAPACITY, FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + (i - 1) * MiddleBlePacket.CAPACITY,
FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + i * MiddleBlePacket.CAPACITY FirstBlePacket.CAPACITY_WITH_MIDDLE_PACKETS + i * MiddleBlePacket.CAPACITY
) )
}
ret.add( ret.add(
MiddleBlePacket( MiddleBlePacket(
index = i.toByte(), index = i.toByte(),
@ -88,6 +63,30 @@ internal class PayloadSplitter(private val payload: ByteArray) {
} }
return ret return ret
} }
private fun splitInOnePacket(): List<BlePacket> {
val ret = ArrayList<BlePacket>()
val crc32 = payload.crc32()
val end = min(FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS, payload.size)
ret.add(
FirstBlePacket(
fullFragments = 0,
payload = payload.copyOfRange(0, end),
size = payload.size.toByte(),
crc32 = crc32
)
)
if (payload.size > FirstBlePacket.CAPACITY_WITHOUT_MIDDLE_PACKETS) {
ret.add(
LastOptionalPlusOneBlePacket(
index = 1,
payload = payload.copyOfRange(end, payload.size),
size = (payload.size - end).toByte()
)
)
}
return ret
}
} }
internal fun ByteArray.crc32(): Long { internal fun ByteArray.crc32(): Long {

View file

@ -29,29 +29,57 @@ internal class LTKExchanger(
@Throws(PairingException::class) @Throws(PairingException::class)
fun negotiateLTK(): PairResult { fun negotiateLTK(): PairResult {
val sp1sp2 = sp1sp2(podId.address, sp2()) val sp1sp2 = PairMessage(
throwOnSendError(sp1sp2.messagePacket, "SP1SP2") sequenceNumber = seq,
source = myId,
destination = podAddress,
keys = arrayOf(SP1, SP2),
payloads = arrayOf(podId.address, sp2())
)
throwOnSendError(sp1sp2.messagePacket, SP1+SP2)
seq++ seq++
val sps1 = sps1() val sps1 = PairMessage(
throwOnSendError(sps1.messagePacket, "SP1") sequenceNumber = seq,
source = myId,
destination = podAddress,
keys = arrayOf(SPS1),
payloads = arrayOf(keyExchange.pdmPublic + keyExchange.pdmNonce)
)
throwOnSendError(sps1.messagePacket, SPS1)
val podSps1 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS1") val podSps1 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS1")
processSps1FromPod(podSps1) processSps1FromPod(podSps1)
// now we have all the data to generate: confPod, confPdm, ltk and noncePrefix // now we have all the data to generate: confPod, confPdm, ltk and noncePrefix
seq++ seq++
val sps2 = sps2() val sps2 = PairMessage(
throwOnSendError(sps2.messagePacket, "SPS2") sequenceNumber = seq,
source = myId,
destination = podAddress,
keys = arrayOf(SPS2),
payloads = arrayOf(keyExchange.pdmConf)
)
throwOnSendError(sps2.messagePacket, SPS2)
val podSps2 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS2") val podSps2 = msgIO.receiveMessage() ?: throw PairingException("Could not read SPS2")
validatePodSps2(podSps2) validatePodSps2(podSps2)
// No exception throwing after this point. It is possible that the pod saved the LTK
seq++ seq++
// send SP0GP0 // send SP0GP0
throwOnSendErrorSending(sp0gp0().messagePacket, "SP0GP0") val sp0gp0 = PairMessage (
sequenceNumber = seq,
source = myId,
destination = podAddress,
keys = arrayOf(SP0GP0),
payloads = arrayOf(ByteArray(0))
)
val result = msgIO.sendMessage(sp0gp0.messagePacket)
if (result !is MessageSendSuccess) {
aapsLogger.warn(LTag.PUMPBTCOMM,"Error sending SP0GP0: $result")
}
// No exception throwing after this point. It is possible that the pod saved the LTK
msgIO.receiveMessage() msgIO.receiveMessage()
?.let { validateP0(it) } ?.let { validateP0(it) }
?: aapsLogger.warn(LTag.PUMPBTCOMM, "Could not read P0") ?: aapsLogger.warn(LTag.PUMPBTCOMM, "Could not read P0")
@ -70,39 +98,6 @@ internal class LTKExchanger(
} }
} }
@Throws(PairingException::class)
private fun throwOnSendErrorSending(msg: MessagePacket, msgType: String) {
val result = msgIO.sendMessage(msg)
if (result is MessageSendErrorSending) {
throw PairingException("Could not send $msgType: $result")
}
}
private fun sp1sp2(sp1: ByteArray, sp2: ByteArray): PairMessage {
val payload = StringLengthPrefixEncoding.formatKeys(
arrayOf(SP1, SP2),
arrayOf(sp1, sp2)
)
return PairMessage(
sequenceNumber = seq,
source = myId,
destination = podAddress,
payload = payload
)
}
private fun sps1(): PairMessage {
val payload = StringLengthPrefixEncoding.formatKeys(
arrayOf("SPS1="),
arrayOf(keyExchange.pdmPublic + keyExchange.pdmNonce)
)
return PairMessage(
sequenceNumber = seq,
source = myId,
destination = podAddress,
payload = payload
)
}
private fun processSps1FromPod(msg: MessagePacket) { private fun processSps1FromPod(msg: MessagePacket) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Received SPS1 from pod: ${msg.payload.toHex()}") aapsLogger.debug(LTag.PUMPBTCOMM, "Received SPS1 from pod: ${msg.payload.toHex()}")
@ -111,19 +106,6 @@ internal class LTKExchanger(
keyExchange.updatePodPublicData(payload) keyExchange.updatePodPublicData(payload)
} }
private fun sps2(): PairMessage {
val payload = StringLengthPrefixEncoding.formatKeys(
arrayOf(SPS2),
arrayOf(keyExchange.pdmConf)
)
return PairMessage(
sequenceNumber = seq,
source = myId,
destination = podAddress,
payload = payload
)
}
private fun validatePodSps2(msg: MessagePacket) { private fun validatePodSps2(msg: MessagePacket) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Received SPS2 from pod: ${msg.payload.toHex()}") aapsLogger.debug(LTag.PUMPBTCOMM, "Received SPS2 from pod: ${msg.payload.toHex()}")
@ -142,16 +124,6 @@ internal class LTKExchanger(
return GET_POD_STATUS_HEX_COMMAND.hexStringToByteArray() return GET_POD_STATUS_HEX_COMMAND.hexStringToByteArray()
} }
private fun sp0gp0(): PairMessage {
val payload = SP0GP0.toByteArray()
return PairMessage(
sequenceNumber = seq,
source = myId,
destination = podAddress,
payload = payload
)
}
private fun validateP0(msg: MessagePacket) { private fun validateP0(msg: MessagePacket) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Received P0 from pod: ${msg.payload.toHex()}") aapsLogger.debug(LTag.PUMPBTCOMM, "Received P0 from pod: ${msg.payload.toHex()}")

View file

@ -3,17 +3,22 @@ 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.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.message.StringLengthPrefixEncoding
data class PairMessage( data class PairMessage(
val sequenceNumber: Byte, val sequenceNumber: Byte,
val source: Id, val source: Id,
val destination: Id, val destination: Id,
val payload: ByteArray, private val keys: Array<String>,
private val payloads: Array<ByteArray>,
val messagePacket: MessagePacket = MessagePacket( val messagePacket: MessagePacket = MessagePacket(
type = MessageType.PAIRING, type = MessageType.PAIRING,
source = source, source = source,
destination = destination, destination = destination,
payload = payload, payload = StringLengthPrefixEncoding.formatKeys(
keys,
payloads,
),
sequenceNumber = sequenceNumber, sequenceNumber = sequenceNumber,
sas = true // TODO: understand why this is true for PairMessages sas = true // TODO: understand why this is true for PairMessages
) )