Android-BLE-Library
Android-BLE-Library copied to clipboard
Notification does not clear after setup a device
class MyBleManager(
@ApplicationContext context: Context
) : BleManager(context), AltaBLE {
override val firmwareVersion: MutableStateFlow<String?> = MutableStateFlow(EMPTY_STRING)
override val serialNumber: MutableStateFlow<String?> = MutableStateFlow(EMPTY_STRING)
override val pubKeyValue: MutableStateFlow<String?> = MutableStateFlow(EMPTY_STRING)
override val isConnected: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val scope = CoroutineScope(Dispatchers.IO)
private var serialNumberCharacteristic: BluetoothGattCharacteristic? = null
private var firmwareRevisionCharacteristic: BluetoothGattCharacteristic? = null
private var readChar: BluetoothGattCharacteristic? = null
private var txCtlChar: BluetoothGattCharacteristic? = null
private var writeChar: BluetoothGattCharacteristic? = null
private var rxCredits: Int = 0x1000
private var rxData: ByteArray = ByteArray(0)
private var pubKey: ByteArray = ByteArray(0)
private var pupkey: String? = null
private var txData = ByteArray(0)
override fun resetValues (){
disableNotifications(readChar).enqueue()
rxCredits = 0x1000
pupkey = null
txData = ByteArray(0)
pubKey = ByteArray(0)
isConnected.value = false
firmwareVersion.value = null
serialNumber.value = null
pubKeyValue.value = null
onServicesInvalidated()
release()
}
override val state = stateAsFlow()
.map {
when (it) {
is ConnectionState.Connecting,
is ConnectionState.Initializing -> AltaBLE.State.LOADING
is ConnectionState.Ready -> AltaBLE.State.READY
is ConnectionState.Disconnecting,
is ConnectionState.Disconnected -> AltaBLE.State.NOT_AVAILABLE
}
}
.stateIn(scope, SharingStarted.Lazily, AltaBLE.State.NOT_AVAILABLE)
override suspend fun connectDevice(device: BluetoothDevice) {
Log.wtf("kevin","get connect device ${device.address}")
connect(device)
.retry(3, 600)
.useAutoConnect(false)
.timeout(6000)
.suspend()
}
override fun release() {
// Cancel all coroutines.
scope.cancel()
disconnect().enqueue()
}
override fun log(priority: Int, message: String) {
Timber.log(priority, message)
}
override fun getMinLogPriority(): Int {
// By default, the library logs only INFO or
// higher priority messages. You may change it here.
return Log.VERBOSE
}
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
val switchService = gatt.getService(SWITCH_SERVICE)
firmwareRevisionCharacteristic = switchService?.getCharacteristic(FIRMWARE_CHARACTERISTIC)
serialNumberCharacteristic = switchService?.getCharacteristic(SERIAL_CHARACTERISTIC)
val sppService = gatt.getService(SPP_UUID)
readChar = sppService?.getCharacteristic(READ_UUID)
txCtlChar = sppService?.getCharacteristic(TX_CTL_UUID)
writeChar = sppService?.getCharacteristic(WRITE_UUID)
return switchService != null && firmwareRevisionCharacteristic != null && serialNumberCharacteristic != null &&
sppService != null && readChar != null && txCtlChar != null && writeChar != null
}
@OptIn(ExperimentalUnsignedTypes::class)
private fun sendCredits() {
rxCredits = 0x1000
val data =
ubyteArrayOf((rxCredits and 0xff).toUByte(), (rxCredits ushr 8).toUByte()).toByteArray()
writeCharacteristic(txCtlChar, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
.with { _, _ -> rxCredits = 0 }
.fail { _, status ->
Timber.tag(ALTA_BLE_TAG).e("Failed to send credits, status: %s", status)
}.enqueue()
}
override fun initialize() {
requestMtu(REQUEST_MTU_DEFAULT).enqueue()
readCharacteristic(serialNumberCharacteristic).with { _, data ->
serialNumber.value = data.getStringValue(0)
Timber.tag(ALTA_BLE_TAG).wtf("get serial number %s", serialNumber.value)
}.enqueue()
readCharacteristic(firmwareRevisionCharacteristic).with { device, data ->
val firmwareRevision = data.getStringValue(0)
firmwareVersion.value = firmwareRevision
Timber.tag(ALTA_BLE_TAG).wtf("get firmware %s", firmwareRevision)
}.enqueue()
setNotificationCallback(readChar).with { device, data ->
val value = data.value
if (value != null) {
collectDataKey(value)
}
}
enableNotifications(readChar)
.done {
}.enqueue()
sendCredits()
}
override fun onServicesInvalidated() {
serialNumberCharacteristic = null
firmwareRevisionCharacteristic = null
txCtlChar = null
readChar = null
writeChar = null
}
private fun collectDataKey(value: ByteArray?) {
value?.size?.let {
rxCredits += it
}
if (value != null) {
rxData += value
Timber.tag("DEVICE-CHANGED-KEY").d("Value ${value.size}")
}
Timber.tag("DEVICE-CHANGED-KEY").d("RXData ${rxData.size}")
if (rxData.size < THREE_INT) return
val total: Int =
rxData.copyOfRange(ONE_INT, THREE_INT).fold(ZER0_INT) { a, b -> (a shl 8) + b.toInt() }
if (rxData.size < total + THREE_INT) return
val pktType = rxData[ZER0_INT]
val payload = rxData.copyOfRange(THREE_INT, THREE_INT + total)
rxData = rxData.copyOfRange(THREE_INT + total, rxData.size)
when (pktType.toUByte()) {
DEVICE_PUB_KEY -> {
pubKey = payload
val pubKeyStr = pubKey.decodeToString()
pupkey = pubKeyStr
Timber.tag(ALTA_BLE_TAG).wtf("get pup key %s", pubKeyStr)
if (pubKey.isNotEmpty()) {
pubKeyValue.value = pubKeyStr
}
Timber.tag("DEVICE-CHANGED-KEY").d(pubKeyStr)
}
DEVICE_STATE -> {
val state = payload[ZER0_INT].toInt()
val inet = if (state and (ONE_INT shl ZER0_INT) != ZER0_INT) INET else EMPTY_STRING
val mesh = if (state and (ONE_INT shl ONE_INT) != ZER0_INT) MESH else EMPTY_STRING
val cloud = if (state and (ONE_INT shl TWO_INT) != ZER0_INT) CLOUD else EMPTY_STRING
val setup =
if (state and (ONE_INT shl THREE_INT) != ZER0_INT) SET_UP else EMPTY_STRING
if (state and (ONE_INT shl THREE_INT) != ZER0_INT) {
Log.wtf("kevin","completed")
isConnected.value = true
}
Timber.tag("DEVICE-STATUS").d("state: $inet $mesh $cloud $setup")
}
else -> {}
}
}
override suspend fun confirmSetup(setUpKey: String) = runBlocking {
Timber.tag("DEVICE-CHANGED-STATE").d("internalkkey: $setUpKey")
val decodedBytes = Base64.decode(setUpKey, Base64.DEFAULT)
val data = decodedBytes.copyOfRange(ZER0_INT, decodedBytes.size)
val count = data.size
val prefixBytes =
byteArrayOf(altaSetup.toByte(), (count shr 8).toByte(), (count and 0xff).toByte())
val combined = prefixBytes + data
txData += combined
if (txData.isNotEmpty()) {
flushData()
}
}
private fun flushData() = runBlocking {
while (txData.isNotEmpty()) {
flushDataTxData().join()
}
}
private fun flushDataTxData() = scope.launch {
var mtu = min(mtu, REQUEST_MTU_DEFAULT) - REDUCE_BYTES_HEADER
if (mtu < CHECK_NEGOTIATED_MTU_SWITCHES_VALUE) {
mtu = min(MTU_SWITCHES, REQUEST_MTU_DEFAULT)
}
var txDataCopy = txData.copyOf()
if (txDataCopy.size > mtu) {
txDataCopy = txDataCopy.copyOfRange(ZER0_INT, mtu)
txData = txData.copyOfRange(mtu, txData.size)
} else {
txData = ByteArray(ZER0_INT)
}
writeChar?.let { characteristic ->
val writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
writeCharacteristic(characteristic, txDataCopy, writeType)
.with { device, data ->
Timber.tag(TAG_BLE_MANAGER).d("WRITE CHAR FLUSH $txDataCopy --- ${txDataCopy.size}")
Timber.tag(TAG_BLE_MANAGER).d("Data written to ${device.address}")
}
.fail { device, status ->
Timber.tag(TAG_BLE_MANAGER)
.e("Failed to write data to ${device.address}, status: $status")
}
.enqueue()
} ?: run {
Timber.tag(TAG_BLE_MANAGER).d("Characteristic for writing not found")
}
}
`
i am using the kotlin library but i have an issue when i have 2 devices to setup. it works with the first one but when try to setup a second one i get the firmware and serial number but it does not complete the process for getting the keys since like it does not clear the notification and detect it as setup
Services discovered
Primary service found
Requesting new MTU...
gatt.requestMtu(517)
configureMTU() - device: XX:XX:XX:XX:01:59 mtu: 517
onConfigureMTU() - Device=FC:B9:23:80:01:59 mtu=247 status=0
MTU changed to: 247
Reading characteristic 00002a25-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a25-0000-1000-8000-00805f9b34fb)
Read Response received from 00002a25-0000-1000-8000-00805f9b34fb, value: (0x) 62-63-62-39-32-33-38-30-30-31-35-38
Reading characteristic 00002a26-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a26-0000-1000-8000-00805f9b34fb)
onConnectionUpdated() - Device=FC:B9:23:80:01:59 interval=36 latency=0 timeout=500 status=0
Connection parameters updated (interval: 45.0ms, latency: 0, timeout: 5000ms)
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
Read Response received from 00002a26-0000-1000-8000-00805f9b34fb, value: (0x) 32-2E-31-65
gatt.setCharacteristicNotification(0734594a-a8e7-4b1a-a6b1-cd5243059a57, true)
setCharacteristicNotification() - uuid: 0734594a-a8e7-4b1a-a6b1-cd5243059a57 enable: true
Enabling notifications for 0734594a-a8e7-4b1a-a6b1-cd5243059a57
gatt.writeDescriptor(00002902-0000-1000-8000-00805f9b34fb, value=0x01-00)
get firmware 2.1e
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
unable to parse scan record: [2, 1, 6, 2, -1, 50, 5, 9, 65, 108, 116, 97]
Data written to descr. 00002902-0000-1000-8000-00805f9b34fb
Writing characteristic ba04c4b2-892b-43be-b69c-5d13f2195392 (WRITE REQUEST)
gatt.writeCharacteristic(ba04c4b2-892b-43be-b69c-5d13f2195392, value=0x0010, WRITE REQUEST)
Data written to ba04c4b2-892b-43be-b69c-5d13f2195392
Notification received from 0734594a-a8e7-4b1a-a6b1-cd5243059a57, value: (0x) 01-01-74-41-41-41-41-42-33-4E-7A-61-43-31-79-63-32-45-41-41-41-41-44-41-51-41-42-41-41-41-42-41-51-43-54-34-4B-42-5A-2B-47-76-72-33-4C-43-71-68-51-54-52-73-37-77-78-69-41-62-4C-48-67-70-6E-6B-48-48-58-6E-34-6D-31-71-53-66-61-4A-70-52-59-4B-34-68-51-6E-73-37-4A-41-37-69-33-78-6A-62-41-6D-46-4A-72-67-6A-77-4A-2F-66-4A-68-42-6D-68-5A-77-6E-41-79-6C-41-6A-61-64-35-62-34-42-69-4B-4E-47-2B-2F-4F-37-68-35-38-48-66-6F-6C-4E-6D-52-42-68-4C-30-45-36-30-31-2F-70-2F-32-56-45-42-4D-77-74-6C-4A-74-6A-65-6E-68-70-4D-46-37-70-79-67-66-55-50-34-39-34-79-41-41-45-61-54-76-57-45-44-69-68-6E-30-47-55-58-72-4A-67-4F-77-4B-53-4C-68-52-50-73-36-6A-6A-43-30-6C-72-61-55-77-6D-68-66-69-30-4D-4F-42-4F-31-2B-4A-2F-38-75-47-48-36-4D-39-43
Value 240
RXData 248
completed
get into is connected
gatt.setCharacteristicNotification(0734594a-a8e7-4b1a-a6b1-cd5243059a57, false)
tagSocket(208) with statsTag=0xffffffff, statsUid=-1
setCharacteristicNotification() - uuid: 0734594a-a8e7-4b1a-a6b1-cd5243059a57 enable: false
Disabling notifications and indications for 0734594a-a8e7-4b1a-a6b1-cd5243059a57
gatt.writeDescriptor(00002902-0000-1000-8000-00805f9b34fb, value=0x00-00)
get into is connected
it does not continue with the process of the notifications
I think we solved it in another issue and can close this one?