From fd19c6bf8ec5e31407a4836e303813513462572a Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sat, 7 Nov 2020 17:18:13 +0100 Subject: [PATCH] Dana-i BLE5 support --- .../nightscout/androidaps/dana/DanaPump.kt | 8 +- .../DanaRS_Packet_Option_Get_User_Option.kt | 68 +++------- .../DanaRS_Packet_Option_Set_User_Option.kt | 12 +- .../danars/encryption/BleEncryption.java | 12 +- .../danars/encryption/EncryptionType.kt | 7 ++ .../androidaps/danars/services/BLEComm.kt | 116 +++++++++++++++--- .../jniLibs/arm64-v8a/libBleEncryption.so | Bin 22680 -> 22776 bytes .../jniLibs/armeabi-v7a/libBleEncryption.so | Bin 26448 -> 26548 bytes .../src/main/jniLibs/x86/libBleEncryption.so | Bin 22296 -> 22396 bytes .../main/jniLibs/x86_64/libBleEncryption.so | Bin 22944 -> 23048 bytes 10 files changed, 147 insertions(+), 76 deletions(-) create mode 100644 danars/src/main/java/info/nightscout/androidaps/danars/encryption/EncryptionType.kt diff --git a/dana/src/main/java/info/nightscout/androidaps/dana/DanaPump.kt b/dana/src/main/java/info/nightscout/androidaps/dana/DanaPump.kt index 2966a25807..a54bf2756e 100644 --- a/dana/src/main/java/info/nightscout/androidaps/dana/DanaPump.kt +++ b/dana/src/main/java/info/nightscout/androidaps/dana/DanaPump.kt @@ -156,7 +156,7 @@ class DanaPump @Inject constructor( // DanaRS specific var rsPassword = "" - var v3RSPump = false + var ignoreUserPassword = false // true if replaced by enhanced encryption // User settings var timeDisplayType24 = false @@ -169,6 +169,7 @@ class DanaPump @Inject constructor( var lowReservoirRate = 0 var cannulaVolume = 0 var refillAmount = 0 + var target = 0 // mgdl 40~400 mmol 2.2~22 => 220~2200 var userOptionsFrompump: ByteArray? = null var initialBolusAmount = 0.0 @@ -274,7 +275,7 @@ class DanaPump @Inject constructor( get() = password == sp.getInt(R.string.key_danar_password, -2) val isRSPasswordOK: Boolean - get() = rsPassword.equals(sp.getString(R.string.key_danars_password, ""), ignoreCase = true) || v3RSPump + get() = rsPassword.equals(sp.getString(R.string.key_danars_password, ""), ignoreCase = true) || ignoreUserPassword fun reset() { aapsLogger.debug(LTag.PUMP, "DanaRPump reset") @@ -293,7 +294,8 @@ class DanaPump @Inject constructor( if (protocol < 10) "DanaRS" else "DanaRS v3" 0x06 -> "DanaRS Korean" - 0x07 -> "Dana-i" + 0x07 -> "Dana-i (BLE4.2)" + 0x09 -> "Dana-i (BLE5)" else -> "Unknown Dana pump" } diff --git a/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Get_User_Option.kt b/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Get_User_Option.kt index d96da176ec..7c2a9fa16d 100644 --- a/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Get_User_Option.kt +++ b/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Get_User_Option.kt @@ -18,54 +18,24 @@ class DanaRS_Packet_Option_Get_User_Option( } override fun handleMessage(data: ByteArray) { - var dataIndex = DATA_START - var dataSize = 1 - danaPump.timeDisplayType24 = byteArrayToInt(getBytes(data, dataIndex, dataSize)) == 0 - dataIndex += dataSize - dataSize = 1 - danaPump.buttonScrollOnOff = byteArrayToInt(getBytes(data, dataIndex, dataSize)) == 1 - dataIndex += dataSize - dataSize = 1 - danaPump.beepAndAlarm = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - danaPump.lcdOnTimeSec = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - danaPump.backlightOnTimeSec = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - danaPump.selectedLanguage = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - danaPump.units = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - danaPump.shutdownHour = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - danaPump.lowReservoirRate = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 2 - danaPump.cannulaVolume = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 2 - danaPump.refillAmount = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - val selectableLanguage1 = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - val selectableLanguage2 = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - val selectableLanguage3 = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - val selectableLanguage4 = byteArrayToInt(getBytes(data, dataIndex, dataSize)) - dataIndex += dataSize - dataSize = 1 - val selectableLanguage5 = byteArrayToInt(getBytes(data, dataIndex, dataSize)) + danaPump.timeDisplayType24 = intFromBuff(data, 0, 1) == 0 + danaPump.buttonScrollOnOff = intFromBuff(data, 1, 1) == 1 + danaPump.beepAndAlarm = intFromBuff(data, 2, 1) + danaPump.lcdOnTimeSec = intFromBuff(data, 3, 1) + danaPump.backlightOnTimeSec = intFromBuff(data, 4, 1) + danaPump.selectedLanguage = intFromBuff(data, 5, 1) + danaPump.units = intFromBuff(data, 6, 1) + danaPump.shutdownHour = intFromBuff(data, 7, 1) + danaPump.lowReservoirRate = intFromBuff(data, 8, 1) + danaPump.cannulaVolume = intFromBuff(data, 9, 2) + danaPump.refillAmount = intFromBuff(data, 11, 2) + val selectableLanguage1 = intFromBuff(data, 13, 1) + val selectableLanguage2 = intFromBuff(data, 14, 1) + val selectableLanguage3 = intFromBuff(data, 15, 1) + val selectableLanguage4 = intFromBuff(data, 16, 1) + val selectableLanguage5 = intFromBuff(data, 17, 1) + if (data.size >= 22) // hw 7+ + danaPump.target = intFromBuff(data, 18, 2) // Pump's screen on time can't be less than 5 failed = danaPump.lcdOnTimeSec < 5 aapsLogger.debug(LTag.PUMPCOMM, "timeDisplayType24: " + danaPump.timeDisplayType24) @@ -77,12 +47,14 @@ class DanaRS_Packet_Option_Get_User_Option( aapsLogger.debug(LTag.PUMPCOMM, "Pump units: " + if (danaPump.units == DanaPump.UNITS_MGDL) "MGDL" else "MMOL") aapsLogger.debug(LTag.PUMPCOMM, "shutdownHour: " + danaPump.shutdownHour) aapsLogger.debug(LTag.PUMPCOMM, "lowReservoirRate: " + danaPump.lowReservoirRate) + aapsLogger.debug(LTag.PUMPCOMM, "cannulaVolume: " + danaPump.cannulaVolume) aapsLogger.debug(LTag.PUMPCOMM, "refillAmount: " + danaPump.refillAmount) aapsLogger.debug(LTag.PUMPCOMM, "selectableLanguage1: $selectableLanguage1") aapsLogger.debug(LTag.PUMPCOMM, "selectableLanguage2: $selectableLanguage2") aapsLogger.debug(LTag.PUMPCOMM, "selectableLanguage3: $selectableLanguage3") aapsLogger.debug(LTag.PUMPCOMM, "selectableLanguage4: $selectableLanguage4") aapsLogger.debug(LTag.PUMPCOMM, "selectableLanguage5: $selectableLanguage5") + aapsLogger.debug(LTag.PUMPCOMM, "target: ${if (danaPump.units == DanaPump.UNITS_MGDL) danaPump.target else danaPump.target / 100}") } override fun getFriendlyName(): String { diff --git a/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Set_User_Option.kt b/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Set_User_Option.kt index 78db5a28c2..ae2e0c03ef 100644 --- a/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Set_User_Option.kt +++ b/danars/src/main/java/info/nightscout/androidaps/danars/comm/DanaRS_Packet_Option_Set_User_Option.kt @@ -26,8 +26,12 @@ class DanaRS_Packet_Option_Set_User_Option( + "\nlcdOnTimeSec:" + danaPump.lcdOnTimeSec + "\nbacklight:" + danaPump.backlightOnTimeSec + "\ndanaRPumpUnits:" + danaPump.units - + "\nlowReservoir:" + danaPump.lowReservoirRate) - val request = ByteArray(13) + + "\nlowReservoir:" + danaPump.lowReservoirRate + + "\ncannulaVolume:" + danaPump.cannulaVolume + + "\nrefillAmount:" + danaPump.refillAmount + + "\ntarget:" + danaPump.target) + val size = if (danaPump.hwModel >= 7) 15 else 13 + val request = ByteArray(size) request[0] = if (danaPump.timeDisplayType24) 0.toByte() else 1.toByte() request[1] = if (danaPump.buttonScrollOnOff) 1.toByte() else 0.toByte() request[2] = (danaPump.beepAndAlarm and 0xff).toByte() @@ -41,6 +45,10 @@ class DanaRS_Packet_Option_Set_User_Option( request[10] = (danaPump.cannulaVolume ushr 8 and 0xff).toByte() request[11] = (danaPump.refillAmount and 0xff).toByte() request[12] = (danaPump.refillAmount ushr 8 and 0xff).toByte() + if (danaPump.hwModel >= 7) { + request[13] = (danaPump.target and 0xff).toByte() + request[14] = (danaPump.target ushr 8 and 0xff).toByte() + } return request } diff --git a/danars/src/main/java/info/nightscout/androidaps/danars/encryption/BleEncryption.java b/danars/src/main/java/info/nightscout/androidaps/danars/encryption/BleEncryption.java index 83e8e3ed3f..a3fb39a647 100644 --- a/danars/src/main/java/info/nightscout/androidaps/danars/encryption/BleEncryption.java +++ b/danars/src/main/java/info/nightscout/androidaps/danars/encryption/BleEncryption.java @@ -126,7 +126,9 @@ public class BleEncryption { private static native void setPairingKeysJni(byte[] pairingKey, byte[] randomPairingKey, byte randomSyncKey); - private static native void setEnhancedEncryptionJni(boolean isSecurityVersion); + private static native void setBle5KeyJni(byte[] ble5Key); + + private static native void setEnhancedEncryptionJni(int securityVersion); private static native byte[] encryptSecondLevelPacketJni(Object context, byte[] bytes); @@ -144,8 +146,12 @@ public class BleEncryption { setPairingKeysJni(pairingKey, randomPairingKey, randomSyncKey); } - public void setEnhancedEncryption(boolean isSecureVersion) { - setEnhancedEncryptionJni(isSecureVersion); + public void setBle5Key(byte[] ble5Key) { + setBle5KeyJni(ble5Key); + } + + public void setEnhancedEncryption(EncryptionType securityVersion) { + setEnhancedEncryptionJni(securityVersion.ordinal()); } public byte[] encryptSecondLevelPacket(byte[] bytes) { diff --git a/danars/src/main/java/info/nightscout/androidaps/danars/encryption/EncryptionType.kt b/danars/src/main/java/info/nightscout/androidaps/danars/encryption/EncryptionType.kt new file mode 100644 index 0000000000..a0e5a77882 --- /dev/null +++ b/danars/src/main/java/info/nightscout/androidaps/danars/encryption/EncryptionType.kt @@ -0,0 +1,7 @@ +package info.nightscout.androidaps.danars.encryption + +enum class EncryptionType(val type: Int) { + ENCRYPTION_DEFAULT(0), + ENCRYPTION_RSv3(1), + ENCRYPTION_BLE5(2) +} \ No newline at end of file diff --git a/danars/src/main/java/info/nightscout/androidaps/danars/services/BLEComm.kt b/danars/src/main/java/info/nightscout/androidaps/danars/services/BLEComm.kt index 2b6eb98495..5f4d68d791 100644 --- a/danars/src/main/java/info/nightscout/androidaps/danars/services/BLEComm.kt +++ b/danars/src/main/java/info/nightscout/androidaps/danars/services/BLEComm.kt @@ -15,6 +15,7 @@ import info.nightscout.androidaps.danars.comm.DanaRSMessageHashTable import info.nightscout.androidaps.danars.comm.DanaRS_Packet import info.nightscout.androidaps.danars.comm.DanaRS_Packet_Etc_Keep_Connection import info.nightscout.androidaps.danars.encryption.BleEncryption +import info.nightscout.androidaps.danars.encryption.EncryptionType import info.nightscout.androidaps.danars.events.EventDanaRSPairingSuccess import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.logging.AAPSLogger @@ -56,9 +57,13 @@ class BLEComm @Inject internal constructor( private const val WRITE_DELAY_MILLIS: Long = 50 private const val UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb" private const val UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb" + private const val UART_BLE5_UUID = "00002902-0000-1000-8000-00805f9b34fb" private const val PACKET_START_BYTE = 0xA5.toByte() private const val PACKET_END_BYTE = 0x5A.toByte() + + private const val BLE5_PACKET_START_BYTE = 0x73.toByte() + private const val BLE5_PACKET_END_BYTE = 0xBF.toByte() } private var scheduledDisconnection: ScheduledFuture<*>? = null @@ -69,7 +74,7 @@ class BLEComm @Inject internal constructor( private var connectDeviceName: String? = null private var bluetoothGatt: BluetoothGatt? = null - private var v3Encryption: Boolean = false + private var encryption: EncryptionType = EncryptionType.ENCRYPTION_DEFAULT set(newValue) { bleEncryption.setEnhancedEncryption(newValue) field = newValue @@ -116,7 +121,7 @@ class BLEComm @Inject internal constructor( return false } isConnected = false - v3Encryption = false + encryption = EncryptionType.ENCRYPTION_DEFAULT encryptedDataRead = false encryptedCommandSent = false isConnecting = true @@ -136,7 +141,7 @@ class BLEComm @Inject internal constructor( fun disconnect(from: String) { aapsLogger.debug(LTag.PUMPBTCOMM, "disconnect from: $from") - if (!encryptedDataRead && encryptedCommandSent && v3Encryption) { + if (!encryptedDataRead && encryptedCommandSent && encryption == EncryptionType.ENCRYPTION_RSv3) { // there was no response from pump after started encryption // assume pairing keys are invalid val lastClearRequest = sp.getLong(R.string.key_rs_last_clear_key_request, 0) @@ -185,8 +190,6 @@ class BLEComm @Inject internal constructor( if (status == BluetoothGatt.GATT_SUCCESS) { findCharacteristic() } - sendConnect() - // 1st message sent to pump after connect } override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { @@ -204,7 +207,7 @@ class BLEComm @Inject internal constructor( override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { // for v3 after initial handshake it's encrypted - useless // aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicWrite: " + DanaRS_Packet.toHexString(characteristic.value)) - Thread(Runnable { + Thread { synchronized(mSendQueue) { // after message sent, check if there is the rest of the message waiting and send it if (mSendQueue.size > 0) { @@ -213,7 +216,14 @@ class BLEComm @Inject internal constructor( writeCharacteristicNoResponse(uartWriteBTGattChar, bytes) } } - }).start() + }.start() + } + + override fun onDescriptorWrite(gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int) { + super.onDescriptorWrite(gatt, descriptor, status) + //aapsLogger.debug(LTag.PUMPBTCOMM, "onDescriptorWrite " + status) + sendConnect() + // 1st message sent to pump after connect } } @@ -229,6 +239,11 @@ class BLEComm @Inject internal constructor( return } bluetoothGatt?.setCharacteristicNotification(characteristic, enabled) + // Dana-i BLE5 specific + characteristic?.getDescriptor(UUID.fromString(UART_BLE5_UUID))?.let { + it.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + bluetoothGatt?.writeDescriptor(it) + } } @Synchronized @@ -299,7 +314,7 @@ class BLEComm @Inject internal constructor( close() isConnected = false isConnecting = false - v3Encryption = false + encryption = EncryptionType.ENCRYPTION_DEFAULT encryptedDataRead = false encryptedCommandSent = false rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) @@ -331,7 +346,7 @@ class BLEComm @Inject internal constructor( var inputBuffer: ByteArray? = null // decrypt 2nd level after successful connection - val incomingBuffer = if (v3Encryption && isConnected) + val incomingBuffer = if (encryption == EncryptionType.ENCRYPTION_RSv3 && isConnected) bleEncryption.decryptSecondLevelPacket(receivedData).also { encryptedDataRead = true sp.putLong(R.string.key_rs_last_clear_key_request, 0L) @@ -342,7 +357,6 @@ class BLEComm @Inject internal constructor( while (isProcessing) { var length = 0 synchronized(readBuffer) { - // Find packet start [A5 A5] if (bufferLength >= 6) { for (idxStartByte in 0 until bufferLength - 2) { @@ -370,6 +384,36 @@ class BLEComm @Inject internal constructor( packetIsValid = true } } + // packet can be BLE5 encrypted too + if (!packetIsValid && encryption == EncryptionType.ENCRYPTION_BLE5) { + var startIndex: Int = -1 + // Find encrypted packet start [73 73] + if (bufferLength >= 6) { + for (idxStartByte in 0 until bufferLength - 2) { + if (readBuffer[idxStartByte] == BLE5_PACKET_START_BYTE && readBuffer[idxStartByte + 1] == BLE5_PACKET_START_BYTE) { + if (idxStartByte > 0) { + // if buffer doesn't start with signature remove the leading trash + aapsLogger.debug(LTag.PUMPBTCOMM, "Shifting the input buffer by $idxStartByte bytes") + System.arraycopy(readBuffer, idxStartByte, readBuffer, 0, bufferLength - idxStartByte) + bufferLength -= idxStartByte + } + startIndex = idxStartByte + break + } + } + } + // 73 73 ENCRYPTED CONTENT BF BF + if (startIndex != -1) { + for (idxEndByte in 5..bufferLength - 2) { + if (readBuffer[idxEndByte] == BLE5_PACKET_END_BYTE && readBuffer[idxEndByte + 1] == BLE5_PACKET_END_BYTE) { + length = idxEndByte - startIndex + 2 - 7 + packetIsValid = true + encryptedDataRead = true + break + } + } + } + } if (packetIsValid) { inputBuffer = ByteArray(length + 7) // copy packet to input buffer @@ -385,7 +429,11 @@ class BLEComm @Inject internal constructor( // now we have encrypted packet in inputBuffer } } + if (packetIsValid && encryptedDataRead && encryption == EncryptionType.ENCRYPTION_BLE5) { + inputBuffer = bleEncryption.decryptSecondLevelPacket(inputBuffer) + } if (packetIsValid) { + // aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< PROCESSING: " + DanaRS_Packet.toHexString(inputBuffer)) // decrypt the packet bleEncryption.getDecryptedPacket(inputBuffer)?.let { decryptedBuffer -> if (decryptedBuffer[0] == BleEncryption.DANAR_PACKET__TYPE_ENCRYPTION_RESPONSE.toByte()) { @@ -453,8 +501,8 @@ class BLEComm @Inject internal constructor( // response OK v1 if (decryptedBuffer.size == 4 && decryptedBuffer[2] == 'O'.toByte() && decryptedBuffer[3] == 'K'.toByte()) { aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (OK)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) - v3Encryption = false - danaPump.v3RSPump = false + encryption = EncryptionType.ENCRYPTION_DEFAULT + danaPump.ignoreUserPassword = false // Grab pairing key from preferences if exists val pairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName, "") aapsLogger.debug(LTag.PUMPBTCOMM, "Using stored pairing key: $pairingKey") @@ -467,8 +515,8 @@ class BLEComm @Inject internal constructor( // response OK v3 } else if (decryptedBuffer.size == 9 && decryptedBuffer[2] == 'O'.toByte() && decryptedBuffer[3] == 'K'.toByte()) { // v3 2nd layer encryption - v3Encryption = true - danaPump.v3RSPump = true + encryption = EncryptionType.ENCRYPTION_RSv3 + danaPump.ignoreUserPassword = true danaPump.hwModel = decryptedBuffer[5].toInt() danaPump.protocol = decryptedBuffer[7].toInt() // grab randomSyncKey @@ -483,6 +531,21 @@ class BLEComm @Inject internal constructor( // Dana RS Easy sendEasyMenuCheck() } + // response OK BLE5 + } else if (decryptedBuffer.size == 14 && decryptedBuffer[2] == 'O'.toByte() && decryptedBuffer[3] == 'K'.toByte()) { + // v3 2nd layer encryption + encryption = EncryptionType.ENCRYPTION_BLE5 + danaPump.ignoreUserPassword = true + danaPump.hwModel = decryptedBuffer[5].toInt() + danaPump.protocol = decryptedBuffer[7].toInt() + val pairingKey = DanaRS_Packet.asciiStringFromBuff(decryptedBuffer, 8, 6) // used while bonding + + if (danaPump.hwModel == 0x09) { + bleEncryption.setBle5Key(pairingKey.encodeToByteArray()) + aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK BLE5 (OK)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) + // Dana-i BLE5 Pump + sendBLE5PairingInformation() + } // response PUMP : error status } else if (decryptedBuffer.size == 6 && decryptedBuffer[2] == 'P'.toByte() && decryptedBuffer[3] == 'U'.toByte() && decryptedBuffer[4] == 'M'.toByte() && decryptedBuffer[5] == 'P'.toByte()) { aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (PUMP)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) @@ -501,8 +564,8 @@ class BLEComm @Inject internal constructor( aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(decryptedBuffer)) mSendQueue.clear() rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, resourceHelper.gs(R.string.connectionerror))) - sp.remove(resourceHelper.gs(R.string.key_danars_pairingkey) + danaRSPlugin.mDeviceName) - val n = Notification(Notification.WRONGSERIALNUMBER, resourceHelper.gs(R.string.wrongpassword), Notification.URGENT) + danaRSPlugin.clearPairing() + val n = Notification(Notification.WRONGSERIALNUMBER, resourceHelper.gs(R.string.password_cleared), Notification.URGENT) rxBus.send(EventNewNotification(n)) } } @@ -544,6 +607,14 @@ class BLEComm @Inject internal constructor( } } + // 2nd packet BLE5 + private fun sendBLE5PairingInformation() { + val params = ByteArray(4) { 0.toByte() } + val bytes: ByteArray = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, params, null) + aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + "ENCRYPTION__TIME_INFORMATION BLE5" + " " + DanaRS_Packet.toHexString(bytes)) + writeCharacteristicNoResponse(uartWriteBTGattChar, bytes) + } + private fun sendV3PairingInformation(requestNewPairing: Int) { val params = byteArrayOf(requestNewPairing.toByte()) val bytes: ByteArray = bleEncryption.getEncryptedPacket(BleEncryption.DANAR_PACKET__OPCODE_ENCRYPTION__TIME_INFORMATION, params, null) @@ -553,8 +624,12 @@ class BLEComm @Inject internal constructor( // 2nd packet response private fun processEncryptionResponse(decryptedBuffer: ByteArray) { - aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__TIME_INFORMATION " + /*message.getMessageName() + " " + */DanaRS_Packet.toHexString(decryptedBuffer)) - if (v3Encryption) { + aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< " + "ENCRYPTION__TIME_INFORMATION " + DanaRS_Packet.toHexString(decryptedBuffer)) + if (encryption == EncryptionType.ENCRYPTION_BLE5) { + isConnected = true + isConnecting = false + aapsLogger.debug(LTag.PUMPBTCOMM, "Connect !!") + } else if (encryption == EncryptionType.ENCRYPTION_RSv3) { // decryptedBuffer[2] : 0x00 OK 0x01 Error, No pairing if (decryptedBuffer[2] == 0x00.toByte()) { val randomPairingKey = sp.getString(resourceHelper.gs(R.string.key_danars_v3_randompairingkey) + danaRSPlugin.mDeviceName, "") @@ -656,7 +731,7 @@ class BLEComm @Inject internal constructor( isUnitUD = decryptedBuffer[3] == 0x01.toByte() // request time information - if (v3Encryption) sendV3PairingInformation() + if (encryption == EncryptionType.ENCRYPTION_RSv3) sendV3PairingInformation() else sendTimeInfo() } @@ -668,7 +743,8 @@ class BLEComm @Inject internal constructor( val params = message.requestParams aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + message.friendlyName + " " + DanaRS_Packet.toHexString(command) + " " + DanaRS_Packet.toHexString(params)) var bytes = bleEncryption.getEncryptedPacket(message.opCode, params, null) - if (v3Encryption) + // aapsLogger.debug(LTag.PUMPBTCOMM, ">>>>> " + DanaRS_Packet.toHexString(bytes)) + if (encryption != EncryptionType.ENCRYPTION_DEFAULT) bytes = bleEncryption.encryptSecondLevelPacket(bytes) // If there is another message not completely sent, add to queue only if (mSendQueue.size > 0) { diff --git a/danars/src/main/jniLibs/arm64-v8a/libBleEncryption.so b/danars/src/main/jniLibs/arm64-v8a/libBleEncryption.so index c5fddf892520595d9446ce1510db2b6ceee93afd..ce5fff9e71687fb48223208e7022670d9f54989d 100644 GIT binary patch delta 7407 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 delta 6012 zcmc&&YjjiBl|EOpgsy~*8(jvt^X+@D zjPXq7@7%Sn_SxUrXPUL zhX(ts%hd9zNy%Npyn>%CRpIIKmEXfG#n0%M8ujS2CyA#d)EoY!V=RSS z2|M|kgiK)re=MPV&A*}7WcDBO>MpdK^1O(03L1A-so>89RaR^G?}}9Yx+>VOG(U_B zhRm$A8WldNwR&r^w$hLpyfR;{eMW0L{xvaN{2IIyIK}pTVj9nEtrr(0;a4hDd@u2Y zXBS?==jM~fHnLM%gGQnR6=~YcyOnyzp4agA94c^9!(|P>KxE?ytAY(r4zIwN89b>a zV~Hl;gcbF2p+Gh7(^~ye3%6&13T)THZHf=isp2Lr+DlN$&&P9BJZ*+5KfIl$po-$x zq&aG@RiRT_O&cURaulh+%Nm}q*+1h_fm0g(T}ZOuUZ?_RG~A)tzf5_Dr$-5N*gK<7 zH$2z;D}i6Jm|FfGMwHd`Y)z!x)-gdFspFdp%=DVxA794PaAdG+rd`P6-4R zgj*UKJrupH7ws0m-{DDTR-e}`Sx(y>k zL!%p`rB=2(;N|C%YlSCxw)M^|wCog?eH7`vCl=|=jYaeuV;uqueXir*vF4!nA6WnS zjwjtxcMFqtG%!gN*c%`yt(En(jq6SJ6!xTSpPcKGl-$>*ikh!3$5^0DKHIu%nIJE8?TD!&(Xi`l@?grN0eV7mur-Rh+j%!q<8LcI+&CiCa4#rermiXq&%I%>LuS;P9|Xjs2G zdI|hp*0WIH1dn9o%y^wigz_^P1*tzlIjyIyRbb9m19KAobY^LA5hkIGN!>cNyRd$9 zOcdNw=|qDx*!zvxxZW1&v};pf2eFvg&Ipry5w~O(+)m#j$aLoPm$@ao$ec=0aINqQ z><>5OXFHGR_WP=vokPGCe$l4&bg9F4q+x$h0TbD70w^Yv$ab%=^t7!wU83sXg35!a z!}fT53o>9_Z;qUW;|PMi3&D2(0>Pr5aLT;SmzXqJx4cp6ddV&Q5cLahBx1q&T@Cvc45TPJ>Ptb22c(!mlAm;tLgg-$Z;@h(1mTxv(4vt-^)AYMg_Q%8 z4O%6!a=Te^2^f)0XPbFmDbV5+SW|ELS z(+JrELSUc}QV&89fIEy(~aN#ng(J7aGC>x@F{7IZ&lMf%40Z*qzfsX0c|oq>g!!f)o-f=Sx) zOVXC#W+q)m_kj~^BmnEc>srSr+#~+i(OM9@J=${iZFWok6Fnoh?d%!q&K`ihva_eS zrTIuaC3s-y-RgtyE3%wP`NCUMXQpg{BkTqC&J1lU&A?V#6vX(Hl@1r7>zKlg!hYY0 z2B#mmveMC&veGv$->+cWLML9PE_6lL7J3)zZcU*%@;&&at-Q6X$=Rx7H54Y<(DobD zg)l|vWHXH7pzffGJvbFRtYYhdsauq`&0*)Db~NSrv~#qnDWPnEv_{&H5~x3~>>Q@Z zHuObyKHx|Reasi&=W=a9BaYu`=^c^3yqFcFF0A?7v z(I|vWjJ;=CQyIby_r218tPGi1&j$l~TWqj5FZPl?FLp7LNtaTYG@$Q@4Ppu|&tOtH ze2mYc@H%29tZ&mR4NCpQEVtx7KLckV;u^OgS2EqwgvBi_MuP#ymW-#WIyvZJtf~a3 zNqE#2#hoqf9ms*vjwtul&zV7oZfQXRAIC{oDp)3a*gj))h9 zj6Di++7wrA;A||?>9+X$$R3Y30@8)R{wZ#4W9}xv_4~ar!PNGi$l|$$_A03B# z(_^)8MuFfjm~*IiU91503_(=I@kH&Ur(lIsPTG!zB<%)4m;o5pb64WzC5EP`(dpMs z=1{-sC^iRe6cmJM&@H{oRGvwX-+FL0rkv|vq1=La$^!c$T!W`T0__F;CQNml+;_FB zWGtplQ;6?eP%DIZWPvRg(;v5sUpdF(b_e;V3u+r-;rEv)8)CTko+xdI;oi*XRcwgV zlZbknXwQzUHakr+beiN>ac}Sl77ww?eGB_+--TOQPs?6EbYql-u;Pw4EL{}B&Hwn; zeT(?4GD)Q6YzBoMetOYdzM}k4A!s4*L#TLl^o8gGf4Ad+VD{Mk^Bt60_JU@vuPDof zS%5l)*^gni-M@p(ECK&Qhvg{uRxDO5Flo~)x;2XzDi#~bN4lGTQIW1Ew_j9bq!NVQ zKf}FeA$t2U&O82mWtMQ1*DoqGTBus&d5g061C@CO(h{NBl#*4!S5+ExrlWkU(q$yF zEQ36IQIRl+=0yg~uvz5iDoe7v9rgl)dRrXbtTH^B7hM5{`9){0n5mvJ=QDYdt2&|O zEBRU8SJuNXESW7_=GLk?@K;tfAHN%`$oHdF%Mjq(RYeqs-O4vtj4S z?V_S}R2yNuv8qrf9_0tBi**?JzgDN~z}ml5FGkhP>Ky!L*HGD8L*+otVzPR%CIiZe z8e8W768oJLd!J)~CoI{dEB!0KR_o$Du0%9=%4MV0KXsKsIp-pW*IeYhtd`22S}H$U zn?pv=)fVas4E&v1r|#rm;NuB`UnczY?ezUIm%C8>@a zKIO{hO?6q+vZF4W+&x_9EL!=O>1R;S2jsMLA_3A%mn_kpdu9^6~j)ph>^_dmGf{;j)q?%lI%JHNNV9t`8}a~fXL z2eSN2$ck}Uz5w|#WCHfAA_8G zQI^j@I=+zQE0AX(XJR!wzLez!kjG-O{5O!D*JOFSjuo>x*Jb(p06vC1204BMfj|!2 zl;z)%p)AW^>Ue!);T=ogmu2OPNc+s(d0re=V_&n6P=Vkd6U)h}LJxP@qT^}TCO3EiejC_c9$pwstfTkqn zgAn0Y@LmM|vWn-En1h$t+WQIjH02pqgOoJBqbWz%^f^D!lxK{7j?pIYpEP9~6aFmA zdlUF?nzD84|H>~n<>{_`#m&um#y&}wPcrUp&d5!`xaWc(Y`&8Zbv*}o8h@-g)vyGm zlT;Fk%x&P#J^V&1c&Zy@ncnyuQmkhr%8v8&P(*Mrk^`rb0N{@^tI=rlc| z>Ds3kJ^NhG-&$E;$uyk4*w9m=eQm;;QJ!(F9_vAQjJ(vlDg`O7Jen~(WZ?(AUSS_U z>$M4GT=E8kor$W+|C1d4YLzGqZ(H-Bz>fG)Q#>*$gC}a-7R;?$!%npJz54c12md8qZ^#_Ti<->)N`Km@A-Is^Iq1w z-u1rkx_lSTe!;bV$(hyMriR2>vSarWa_ruQ>X?At5<*TlY+DzaUrxvn9POA^6})Bj-}h1%S73UY+_}QQ?uVbDi^*^K$)S)Fr9-WRu=bok z$P)wZv@`o$KbhH={$4hbMZZ^&o#^aXA=^IK*C5cX@UJ;iU?t5 zOr8#T#Z*FOi*hpTua6-l1qNZn6igmNhHV8XlxXk}=qebPMR_~gulu$4Kt3>qkTS7- z8U}7FBm}h<22^0cK|lM?(NKf{V#W67(QcgufypLB*a`=y;6Tq5kiUX_2p#n3$mB%O zjhTdu5Hnl@2MI-ls73o}$mD6rN>NThfN6Pz)QPek{npMvz{s5WD~Em6dP1Nkrce?= zI>5Y^N{B)1@DT?1UW;WU%AdgDGf9Lj5#=Wl&=Cxf0Q1q_00%>Jo`v@HMR(*Y=r(aLuZ_=B?UDt%2H>LHQBMh|%b$ z5}^PMIz^tR9ZF+4rcJms_)P=G0+@S}1+Z%ZFcCntgbVA6VDSsrH2h-W5=<>Y@WNF@ zw*@~aiOxx;&fq7f>7W}26bNm;A7mj|$AR7jFc0ivlM*9vzMl+wV0gkzHU_&`oWp=r zCWsfC4R*2d*)<#(34{VTa!5RoNdrRCkW{C{VVM*@X%J z-Noi)JiLU{O{yl^p$ekv&|a>J28WfHGO)eY5fTZXk1~pwjEn{nvWZdDq?XYX95jq# zPPa0O%{dqe zsUvL61WeZ%CSi%3WfYTtj!~r9#V98HJ4O=-u`!xW3&ZD{s_sLkAOGc2z-RTBs{VcQ z@({nwkFW6KOZ<4PAD{2XPx9k4{P+|u+H7NafsUJUZ^Gn=n!v0+1CsSYzqAN zWqy2xA7A3fYyJ3qKYo%Qp8;Mui++RqksgX(6>{;%#U72aQ&Zr2w$|JI35|<+ zgL{R36!U;-k|OnpRiV18 zYt6;1eMoPEo+Q7CmKa^jS_|6DN^kcD_`*$JlhDaLn3bx;nFdC`Puwif!-=O1i%n3> zG;VgqaK6UP;5#^ExKjq!yxw;=*i&(du0XC@S01xn7X;02 zq3weNwJVVH>YAOn$Ls2#-vivTj`18;j`SR2A;z+voJXph4Bg_DP%u#kV-9lyj)$9bst6xKf45Si>g6ivGSyDhOZfv1Xt(e~YzOcivM$ zH^)YstVkyoHWuYGx{6C|G5m@tf2~$&BkfDEk}fU64Fx!}5a%5*y#fajn?NiE1xnJ2 zzOBk?_-oFJS@nKgk}HsBRp$AZbX`@fZSR#pC98eE?x;}v4?MFwN?iw(TRfy=i_F7y zz=)b66CVLR(+uMWZq^U1uUetYuJXFCImUS2M}Q&mNih zNQ{;j=Hmi2#gE~%^o{s?xoFBIyvmi)*Anu$DEe(ey8P%pg!C^Ugv!)flivQi=Q#OK z&)bAK396o0?Q?P*+8}|KDCN~r+NDXD&kV*P*bGJWWFgcjo!4Drzl+ruTPzkE1YQVN zojvz)ZJ0i*+UqW}`!t!iH6@}ZooQr((OmkC`Z<%*PTDsiu20Jexll^TdEisvU7#7b zRf1dFTkCXNtuk{!gLFqIhLxCQ7HK2u-ot}qdwYKv-$as01KY!7m!+69ODr5LdU}XP zf&e*npqaN3{Cc|sEplsZ+ZH@Bz%O+t=5agdgrotZunmeCn7_g>#2p$A<)CNj07Z>agT+s#9oS47RbeY9yz zJU5QMGiDt(pQdTbxlid6nje!-h*1P$WL|yCeWLd*)JR_G?Pl?DdWErtZcZCzsuChw zqcq>(RGAY<+A_!o|Iqiv-u+_V#hx|F&BlEgJX{}bIAsjemm6LcYEROlJ40;+sE-40 zB|iXP0Pg}PfP+92P!F(ZR`*h$M{V~GF`Nd`DbW`gL-n(9zO$fsQV+u*eX++F8>+kI zF6)IpcB`S`hJ-Bk7&Aikx7<3qVr+!zFm89gKAeh6D{$?D{J4+^rx}T)jABy5z&|GP z0pK5HZd*Tc+qxRlm_kpF4UgMmdr;J_6#KC9 z%aHI7&gJyR*wNe~nvfn5UhdZ_OpbFtElwYuRW$Dj?I?Xr1xma#i3g?n7Oo4MrDx6DFoJ+$+eMgwQNdm0|wt?o$LV?&*O#Wu1 zIc402ksk|HP#A|d1@8*;NIEtmLnpMty zL@#H(iigH$CfHY}+N#7tsZl7lb~hVUO(A}Do_SklwNN#N{H_+nC}@^N#jxDjUkLIK zF@N!MnHbA>HUY-$WuvFsa>SwTf=XpE-ymdB7SXVq2a`A^(HF8u#fL%5Hm=GN)F{`g zZg5Jb8Q#g+4kBrKGCPWg&;QOI%Z1Rv?2%&!-M9K{@Dg1OT!4iV)QI;n1NhBA6mS&p zWfyW=GACwnss;UKyg(mkFLc0DlUr&gP2yD6zOu>ahu7XEz?2+%?5^de`o2KhNKb@P z#ttpRp!@kgZ@0n{gxd>gPlXc|a0}hoOgw9qnk5bV4ym}@*=?8;mOF>#9w{7qay@S> zF!IhVJaHD1Fr0iUZRu~Wd%pb5`=^d&Atjr%YJ*&F_V67YMy0cUyplLeh)UbBtP*N& zW5GtwxtSx*nSySwA;2QTQaOPDqJYN9G$*H8wg_zDWcqr}T-ncf{Xd&Sf6JMh^deY> zIxBDH8psY}kq8k;+C+yefBlg8%d^Cz8jX`Y%t?4+CWrt@`9dNMCZsdutIA?Rap(p!0v zK~r!}arl^7H<89q(Zo!5M%ZS#q$EJ2=;YD^Izz?@u^=cJEK$%)Hy zs-eqv4!U!kFOS3_gs(H7MB+0ezE(Ks=_zU1*Bp_*_mdIOr9q!-4v8%xgZqY82ieZs zaqwKPUC;C~Crz1}lhEa0j=ysZx@`{9Zih=HT(ggb9|8Gv*yFy$S!y#LOlH(;$nd)?)q?737Zz|N*HWpQ;gL?7{W5OIkM~K>1IYGzb z4s+CiP7pX+F>Q9_WA@Gd(v`Z|Bie>hexNh3&jWvw{u&qWxPBJ}5O7#PIDQ*0vrJ z;%XaoOYO9FdTMeuJYXv9gYGGKR;?R{XHc}AEA7S_!0^UVWPanlkx6_}eXXo7P zVdeupgKlCUgu#3m-UqrKcnV;W4LOhX&n|b~=AXRD!uj3FH6GV4#Q7 zakP9!g!HkVu5|j!j7a`$4?RAkQhl;VdZmn%UTa)G=st;|Ng{ELy+PDx+jg@^(^_&0 zUgu>4?sNSoH1txTK4HLZ>sRXr+?V1&7%H2A-1MZuB zr2R*XQGgrf-Ab%u@DpfJQSw-I|Mzw-nbR>#b$^;((zGpxYnSI*Z`QgB6jH0a-P$(b z-qhzAO?MROXI$ztzA4%D$eY1)vvf0cUwh={$F&3QANr&gbMlhM3*F$^~pxJJf$n-_^Mv|esR{s`d$`o0g8bl;K5<^ zsgfAQB(M{JOnRtfVp=%3P#^>d28IC&Kmu@p7qJflU(vpjseILkbnM+pCd-Fj6G!m2 z3_1WNx6^Bq*S%bO?VQ(SwMuHW|70|vwppOkS`R2|uRZTIb-{l3cV5$Xpw~cc%+W=! z=_gRtWuKj@$EP=gTCLVvi{0n985CwFvLDPDa6f^ve_d#My{2=ZJ)mp=bDwX3u6~~b zMwy=c;*;f|%ziH@(_39WeKRQ2E3f$Uxu7i2Do_@P9#mdudbxH380g7v&`-g92g(AH zU-bnv8QJ~HD5;+qtMravJSnVCqT{Dy{40(+TXmU4$!TjAA;@#WeIeEvIL^szJMw~SpwHQ zz63Nke3j6H2CxYC)1uN+YA98<>@3~IwUm}O%ITT=l|?V%LHK2$5qJYQ0<-`h0H=X3 zfn*()ATSwV-~ZPG{{+4TdT21*K zx4i4*I6I_}KV)YS=yHJ3=h4pSPLblo736)Qe3-T^AD`&L$u!{guG@qG@R@NP>v0Rz z0@(}7)6RwWm~80$|A_eid+?UQ2PE982RHr46IUNu_fZ!2Q2MQ}|N7V(!|bxbzIQGx zkI*fCWBZ=Ew+Crz&J6dNNe3=pS>H7wqWDV8`na@@wyk@6VY@!tN#4GytySyzO+{SJ#uFYx8lEeu96;jFhRj4g5hoY4@hhp z7*5jbkl17}WYD;(1O+QHLp?33N(f-3-b5d+N)BK}w$KJ}UEsRtJK)%)@1+;P2?eK$ zRmnl;*pdMBQ)$ksc*X61jnn&AB`CD_U{M3-=rfR_7GenlP4vjBPdGjO^Xdcz`>&sV p1^&rI_pDBa?$qirym}>dtWM?gSJJSDQlqL@;s9h*FJ43J{{XN~ybDqyR_7Cpd=bTB&MK!7leLsJQkk21#To>(=D- z-#z+Osi!v~%zwa3Xa1be-FE0k;{R&{X}U*0bdTPCk6ywiSU0t35mlmlo*4E8XZ5&BgEhBumhq`v}?W*7qRZ3$u_gX3qLcSZQhrfteUX z$avBPpCb!acx6=$YAs zXx+LD`~oJh5_T5<65`!Z5z@-mpBWl4U`oN=HTw@Z9zjA&+#UXk4l_`4Pl5}O`goLh zpS%5O=&G4wKr!CKh_?{&!rkph(f{oPLUNG^8}AGFAI1`3{auFn=x_}N&nB6Q5=l`a zb|}a-=OzTW>9yw5(LTki9jixbrx8-*9^gg9+n~K0FA4+BKzz7}2`JEh9PPOnUo`v) z4xeEL7Pmg>o&?xpy^R2#HNJuX8! z;*IlUbPgS=3aHL^J5~6FQ@gLy|EX%jlWVK%R<{NFaeUhf*?W=*LM9`C!VMWX;1t>M=pLbVew!ajg?Rf6awrhqE~|+r)A@wpM5_x z>>h%2?;LX>G0%wrn*!_24r~e%ElRGRsnjPpzJSH|9N4oY$)kXAqKSvr&Evq4KnRct z!~tovDtPKdoKhqfm;tca;S9KQ6wt%L6*JfzQCJe?woh;~n=9%|u#4^-tVZkz19Ip> zE@phNsNby$Q!`jg0nVj62dg1FaFUZLqC`jx7$>-R6uH?$Y(3HuC5&1XO&p=W54nNg z+R(DhsW_o(2#G+%I>yMTp0OVx&okB%(!dzoYBS?BIvg4~4`c3Rnm-|C#_5E-!5Hhh zk1;mcLB=@US{S2Rt&C9{3uDyv7~@&=!LZ~^tV}o4uoKQP#yX#8j2T^EjJ5riaXcYb z#+kG^tkihsH<;8XuU+vuyY0%EUrt{2$X#A?yO(V7l3TpweO|KJOE!7QTfO84muwUV zuJ>v%c*%M%d8L=U%u6o!l1sc~t(TnZC2PE7^)JS|Ggf*vM0v>yFWKKqCSI~|>uwyo zm)z$iTfO899@&VppYtee3B2SkFS*@Iws^@cUh+OK+3Y2oAWO+xR|$E|y4mR*D94p8 z&YAJgG04<5ULB-_!G2uw!2^P{qmQa07IU=i>4-fX|F@HEp;3!C&Ooasyvv=YLD5yg z^j{T%THd49+P5?~dza|K^i0-HL zCM3{;0Iuw`VHHA4Dw^{)S!}wmgk{!lfmj#ViTp@qi0-$rlyq5(c?-Pc#X>Cl?UcW$ zE55?k;oqn>ZID?Xt{~*fQe3=&?uYPHw1klBNP}PnWLSY#^lFu_(-j*9OX}%~33fmJ z30+h4>nQ0lvFX>P{z_==>J~~g?<9s97Ne}KC^kj0zjPNnOe{9Desi?8a)K_a(&;Ed z%}$_ZAF_C{S?fJDJ0a?xnq{`4P@bYO*;TaG*t25MH0zy?q+b=Sl{UsrsieJt}Q;G5|u8bZ~ z&fvo7W#v?=P!+|V=uZ}Iq3=zxZ@{|jb)LSr7gaAAAMPie(TK2m5pD|Q zxIqAwz*3+DxQ(}thT}E5S1kS}zee935g6ac#D1kFf3xpyrz5{#0*P(xgQ1cplE^h; zW9zs*=3?IDW9H#8t)Hm<&N+LfqLvCx(&Oyqlrj?^W+fnJugn}^Y3TSv1|E1b=%s{w z?mV54_}GLi590O#v;s{4%VdisK&PXPi79;5CHmLIxiP7CN|J;@QkaJG+ZS>&oik}R zFSk`C-AIIC~?7adJ-MhhN&;0ZZV#XNzQdnckWJ7$KdH%*SoEv`v?EkPG>ESTheduo7f_od002 zQOw=qUyZ9*NTj_Llf$_Lknr))YsRQ7{PNNE8QtSufI^A~YhEuH`+n?OF zyy5rmOgYx8&pYn+{9#Xz{cg+5JC?mJi^P#TOa*lu7fB;iYPfFtQp!T^D7~2Suf)Ud z%={3USKf9^8#;vQ_^)*KwxEU6%j(~y-%p7#R*568mz!>Kiu6p9@+kB-?&&KZ+U@RJ zA*`2gtlx#v!}Jpjr|LuX3k^HPs*{xHo=_|4+=r_Di0WMe&H{f1jsg3Doxm1=<#WaM z5{qd>LR;Jkz32{0^xFCmeF-iREG?ciz%f8yEY!z_=x#f@ZLr5~H8kFokP4wbHAH{g zaay#UF|fk3K19K%uxpq{=M)_DKSHl&Qxi|TNbIa9N>r|G7KmA|bSbnIlj0y_%@k}{Y`T5ieU|v;fmoSUNL-d#Te(dcZK>lNnw@3 z*Uv;(XU0)JV@c!=R$OeraZN#cLIp|J#sN9kv*cB}A>(UqIlW)=AvcveHOq~z;My?1 zCjUK`h1{-T-RA=7V)rm4hkMv%L5_QvpII($K$$)mr#)|C7bV`Tu$=3-?Fbzpoy+hp zQjICDWWdw!S)dZ{Dr`bKET_tzFFKE?-vXZrMP1`eL5=bqfpnaiqldE=M2=z4D9pjT zwU77-Y4U?&<}cSDpsMVJ+-vly?6){Q4V%^<;Oq@HbDCh&*pKwL_dP;$bJScQU6m6V zecSPeJ~f8hiN|$4I0w8JoS)aWKS#qUE{#a>ko^f>jM{;pX0~0KK8;i4j(m(q?#*b? z1MknENi$X?g^&1IBZcvDUv}ac#vGv|o+~ct%)$GG5(t<@vu3XI9YF6(GwJ@BrM}NW z)X$_p%`8n%f|$VKTEcX^iEG?0NR)31cvb;(B^&$FXo?8CHkfcee3sQ|aE)CJg_u zbp7mnzI2GT&d!mii+zI8CufKb&yENnL!^_#SDm`)bX>kVS~@h|T8NK9K5AJHH`S*n zIL+sRMmOeA4AJuZa9%z{SLf$U{Mn|2O|XqQMr{Z18u;EceDaDQWGY_iex@Jhr(~V5 zMf^HWs?P;A`kb&ytnsPbMP3fNN^dmOGJCg;hR?}~-zx^*kHF0~(s=_RNAXrS z8QcSu(6w{2`9(ImcTN^xXQN-tN#K)g^yfJmzQjh=+F0H?NOf8zFAP$>R#PqvvNUfE zjyY}*S`g=5yo_Z=&zfLapcwH!0eCj9ZA$tEKGNq-1 z#koF6`?PbTFAN48CQ9qdOdZSn!eBKxQvtmbR-d9)dXJI-y5Dq z_ZKAbra^kPASrsg&R|PN9|^X1j5*E@P{Z7$L}7rrj1G)B$RO#g!~6J^FuX@<@VzM% z&x?>p>4$S;cP?w<_E(3lU$HE?_Zcm-gm>H9y!avbtQUQ%;qzo)JnXyhqTU&VFPxAM(9epu!zV)YiGojT-=Sc9X2olH7<@KE zcF<=_-i&V_QR+q=Cr3sdU3mH!!5A9&ESF;+L!M3Vo0mA{$C1nZTq37qopEz;-G!(mj7l^X!w+4OU(Ebs)3+Mn&1kmOMapMm_d>hye>;foYq8As$ zhdc}EcfixYQ$Q`{$_qnl5M~jy1wa`vFMzHnj}EyHVm6QoWB_S&V|jK+Fr*+L5Rd`i z)63CXJ^uh$=q%%$v{?CTZUptK!i=^Sa$UY5u7nm+>*a~Lt zii=Jo`vhzM&S^XkF8SVRyZ|;_aT>n`Z@cR9+t$bWSsHfsyW$-KqnWgVyEuIMv_be7 zmNJ{ucm`|-cZ1DCt^usWt^kag-G9WTE5OXZ1YDWGwQKZsPd#4q zbY1$YRfeYwPj4_hUQ=E7WX<}vtcRNX+q#zjAferl%I|-t0M~oq5bzhE1NaE|61WI_ z4`kz+qyQ)e9so81SAjubGwu%tI&np0NFFYUxKW6oI`LVK)Y1D_WX#k0;<5y7E%?T5 zXH6^4IVto?TsAoH3V?AQ+8OKIjO(qaFLCRS(2f-us%^MvjXIq*Q5XQuNxF(6eu!=sjCW-TiqIG{m!PsvsN_ z1tC_564s!1{U|{=2uSTa05hfw!ru-2m*B8sIXdx+7S3rR`nx9x!k^J5`tN|R>WANf z{;Fg_C;%Pd9|0`O7KC?0^!7Uz!6W1)34&%YIEbZn%oKz)qwOS-a|Pjy(LO>9A_bu* zMrW`V__9Pns5S7X2ME}S{*IA?u-d@Kga39gHw7q_JXirHFa@XugYFqvwIT5cz`RsJ zcp*%mumX&_Qv`^uw;u<+0D;?#c0FJr@P9JezXjfv--mw%?L3zt%z_}K5uvupvQG+| z`N+zNx2=4#HZ$}wF-s7zIw2IrW)vZRHs%n7hyD!U5-?V9MGBH_^62wJo|zdUvF5Ps zs5tb-ql`egfrZ#+S>tj>#;|!d*BEF>7>Y6k#r#E}G8_e_;J*YsZ(!B7qI_aNKVoP{ zAt{ET(6lrG{h${9BPr<@tpomavKzy1VR_*ZY)$x+V{X9W?-PVDSgoC)6&4}r5QNhN zFk_ z^B#etKywLvRd=-96|HzLQr$}^#hRjWlrlTTs&WKsHkGqfp6>ynGVGw@@21MOE_&__ z(DT4)_LoSfWQk$NB1a^V0D&EsHBD}gmNz>DyZ2+rtaCYBR>MM~)_OYluy#CEciS}g z3A-P@Y0#*?Jw#v9R_TmU3)iMMdszP99MQ&}7(8VLmu*8z zv_4Gnw}F|~DmS}ScZ=fgQr)MO=FV>i3qtv3M=PYZdz0bpee3HUJQ18y?W-KbTYj^i zd`Q_wwp8}EvhDVvL%BTC4_qGAPc5aqo*lJkb8!@J3k!)(*G2Q~Vbh|MlW4ThI#?~J zk((s-Kr3(B^|9*j>)EF0dB)fy>{xX2DB?*%N2~5;Q!6y+Dy^{Cm=y5@n-tThhV?O| zhF4;y#Bh20siB#ji*Y0z9>mvuoUfaFv2Wc;v87@o+ZLOO=l)oIDQfi|_Fe2`u*?`T zWnm);P~4~4{b(DSeTHlHIcPQ)8y0RO+Pwj6#2|Z2MS48!_TdSFIKf_VKieGd;>zSp z&3s#N`{3xRpq8-?d!mOc)?aVT60Nu^barz6e(;j)-q~QKx+@OliO>^(^brS^odEWR zd7^wS!8cFjjY6sXd?Uzxg%WSCwa~@hBpKC`GWiRux|dHJ zIn2Iq8==cjaopxI`D=^Mk*iWgI%Lhgo7Y66rtdnBs!|Z30F|AhY)Y{zx2M9;G%K9VF97YscaE@CNS+Olvqq9_>+3AK-mUF~()EK8zSgi&mDTMCvjt z8o1r(Uj!2S zzP$vRuGrv*pK{Iar8(uZR`tNEWMAhbgGM=Ln8hHl_#!8%MjZy2bhp`n4a|lSS*lGd zOix*=Y=9YxQ*6qyz_;hGF+_>Y-~wAXQiyhdEpCT!{cS-5c;5jMn=Kp?$8L#_F!t<= zeM%=9u5_-+=FV~)^Ky}xJF$%k8~TR*(a1YQ7eaSK`3*WgvHPmHvba@w5~Ef( z>OouG%Z-81jZD&TQo&$>72iqPB{xy4o3B5h<14;!T}386U5rRqKQck%SFFl8bQ)hU zygo7o6PrPZG9fw;B5BeAUeEiu0?6wNS&8oX=r5Z&Ewp!9X z<+$Rl0IA)J%n&X_b^pqqd+)nv=|Nqwfk(Q~RR^r#f0Ldi%4bD)E|$BdoB4cdJ}z8p zL9|*DiKEJ2zvNT+!{#n^vrS#fxBL=l^PuAHPzu_Wk~XFEMBokZq`W6!tm;KN6tR~} z14a}!Pd9ZbMzw0Kiu*L=RtrueEY7zo68ZxbU?N|HL2-9sBN9g?`1jw`U%>IEfUELY zQQ~|T9SWePd(&ZS3Nk{0xIFnKbX(A&l<#2&60^iTtUJ+(XH=3~oWY8c(#4}pPFfab zcn^zkrH@SnBjnS1K8B3%*Tjn2{6w`^&}~a^vryNvvA;E&M*8+|$!F$T?fVMO>PKHw zuchgv6n=zyGMiQ@1!sAl)Av}Vy3gWf@I|HjE34>pf7O}FUUX$iO9QMtW%wYcseoa( z)LwAqh?3<@?YQKZMJZ=0dw1mP(qB%4%q6Xw!d^(3m$2|OcU)7W$XUA}xX>wvv5-;A zJXfu)Mqew|K3-p(S`bi6YSh1)2d^~+sgD)ssZDt|&;o~mCwK1t8MNDVTGfpyjQv_l z!#r>RV-91t;@;P9j^CT(f@H=Xy_)Pd*N_=o^J=n(4T&)`S<>E+#~w%>E>^OqQa4E} zPqE(A)#9IOm){g6ibvSV(Q)bDbm+UjphhVtj*wsV(G{vPM)gsL50JMPis)c4*>JEvTwoD5*7ujqDBI zo9FSt!1tdR@>lutrOJGZT3Vwv1PR*|r=HGU2plIu4=!j0pu`QL%HCj;dvPh@w`FSL z{?QA^_y=E$VMVm{OdstQAg~RSxoXjF)!X#S3tH3DQtIl&>zBSB1PGzC)Zm$I-h{Gs z61o1wPcvcZW5+t~*E#635Kp9)t=bN<~=f6Atge?rV;voc@B?XE2tWHupJVfReflGLX9NQIbIE$KqUv^Px8Qhvxh z=5uD*>wIlYOq-pc6D$(dQiNepMCOXeeUZ6pRSUZ>ZH|H7Xi*WR3xIam$DH>$>|o5^ zOq(-8AH6Z851mP9&fS&nh%%!4Y(!)@T&Kk}VGbW+-R=Nw% zrXVIPV?h{M+g_K$TGHdi*5IgCK5AD+>`iUJtL^}QK@#Yc@IIz9n|B$SfTty)4Nauj zaNq61C3q*>GGVgktbRVook$IAXAisEVG(d@pN8%0-^S~0Si$=kY+m=Ey>?xDB56-8 z=u%725gF6eYn4yhxq8udWHp_#mY?bdkMQGqr!Nw^uKIv@>DR;3d?=d6=8hpn1NmV; zq>0PqMsTQU~nA3sj}n~Ulm!m^BLV3!Pgm& z2aQ8I`!S~`MfdL)Sxj0MDVeNk&SR*)PoaHoboV97pZfL^&B)GY|0?Y|vUJ%S1o`4l zyBBXqNzaJ?BHC=w#N^>;?cNN%7ug8B&E8Tsn@m5k7JDD>yw9=6Y<*{E(N^|c0ho)8e zH>3vhu;3}baU^?T($pLH4l2b928ivG`56==uEPI@<9>5hZEzH*uf*ZC7IZ0b~}r(Y&o z=YqM+L^M-Vm)g`3>K{9>St$R-+hs~P@j?x`WPn~9)_0 zpO{oBrxQlyPkWsp2QnURj8Lo5>48cR>A%PjTPrKX+d<_k=WabXxA)CarYu);L9e1z+;it*)4$$G5`;r#q#ePucsMo* zbzn1dX!hSK+Ush#9xovFJxev(>HvD|Gq6pcTgyM^f$yA;W7QhdJM4A!e7vVOb0E%Q z;(EszD3je4tNc065k9i%jJ5m>@m9`-_YUB04RSDGbZpB+cz!!nd$>D$n4iGSCynzD#ka@v$41LJ;1@sucp|i-j zT0kz;PjTQ3bf@<3!3RBl&AfAQp=qeMo&9z;z8NVz1618TaOgSAbQ{NL{+2Q`6`x=`~X}g#?Gtlvao}`s<%Zn*rMP9!)#OcH}zPjd?>t z%D&aKzw=f>-ViC}BD+5?RqDEkMJI-nKy4V&%#^&5Ap#PV7PcF;(Wq6h_w$m{T%agK zYdTI}eAMleb=K3?Etb_>mBeKOXaWaWa(=338A$0{X7fQ!JAv$I>sy+(34L4trD?AM z7PsN02Eq3=?IPfdA8J|(a>Sl?O)CUk@sXyj2ecwz-wId(SPxi?Tgz$cKci{US2S%8 z;AFt9cv-Uoa06aaTaY%t70acsyIEF2_~weX&I-pv zRcHf*frU%J6AVQsK|1*eI*hi{fMcvI>yEVe=fW{W)7G^CVukC76oRkoVNc#Mm$l!K z62BO)I#!`fjB_T4{tXI#UGaCv;fh_!cb zUo`E#+A*J|R}>xJ{*~*}?t30O)-YxM?RULXd#P}}gwK|b79~Z{+Noq&+3$)H!}-#& z>=W!jQBpW5fJ!s_3@ut0mFX<1I4Oiy*Tyo7M~3vE*28{LJTlZl`i)?}DNYV0RgaHg zJByP;YEZ3V$BRcsG-8cZ3Ma8E#fjn3v+x}pWd<8nk`!Ku8kOf*0b0R-X|U3guf-K? p<>I7p`X|MTh2YEli$|hw_u`u*=OXsO;?%gwi(o=hwDB{m{}1n}34Z_p delta 7582 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 WT1)3fk$>y_#FOZbCfnvORQ-ReZSqS1 diff --git a/danars/src/main/jniLibs/x86_64/libBleEncryption.so b/danars/src/main/jniLibs/x86_64/libBleEncryption.so index 48d5708f6004dcf5f95ee257b9daef65b17a3b86..00e47ca4d38eb16e46e204ef0dcb5a695d455af7 100644 GIT binary patch delta 8468 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 fZdK`Y)- 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