У меня возникла проблема с уведомлениями BLE на некоторых устройствах, в частности на моделях Google Pixel и Samsung Galaxy. Проблема возникает при получении ответа, длина которого превышает размер MTU, что приводит к его разделению на две части. На этих устройствах я получаю только вторую часть сообщения.
При инициализации связи с устройством BLE мы сначала выполняем команду DiscoverServices() и устанавливаем MTU равным 512.
После этого мы отправляем исходное сообщение через определенную характеристику BluetoothGattCharacteristic.
Затем мы ожидаем получить ответ JSON от устройства, разделенный на две части из-за его длина. Однако на устройствах Google Pixel и Samsung Galaxy принимается только вторая часть сообщения (метод onCharacteristicChanged на упомянутых устройствах вызывается только один раз, тогда как на остальных протестированных нами устройствах он вызывается дважды).
Проблема была обнаружена в Android 14 и протестирована на совершенно новой версии Android 15, но не решена в новом обновлении ОС.
Любая подскажите, что может быть причиной такого поведения? Ниже я прикрепил фрагмент кода:
Код:
Код: Выделить всё
// STEP 1 - Connect to the BluetoothDevice
fun connect(device: BluetoothDevice, context: Context) {
try {
device.connectGatt(context, false, callback)
} catch (e: SecurityException) {
Log.d("Error connecting to ${device.address}")
}
}
private val callback = object : BluetoothGattCallback() {
// STEP 2 - Request the services after a successful connection
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
try {
val deviceAddress = gatt.device.address
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Handler(Looper.getMainLooper()).post {
gatt.discoverServices()
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
disconnect()
}
} else {
disconnect(status)
}
} catch (e: SecurityException) {
Log.d("onConnectionStateChange: Error occurred $e")
}
}
// STEP 3 - MTU request after a successful discoverServices()
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
try {
with(gatt) {
if (status == BluetoothGatt.GATT_SUCCESS) {
requestMtu(512)
} else {
disconnect()
}
}
} catch (e: SecurityException) {
Log.d("onServicesDiscovered: Error occurred $e")
}
}
// STEP 4 - Send message after setting the MTU properly
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
deviceBluetoothGatt!!.services?.firstOrNull { it.uuid == serviceUUID.uuid }?.let { service ->
val characteristic = service.characteristics.first { it.uuid == readCharacteristicUUID }
try {
deviceBluetoothGatt!!.findCharacteristic(characteristic.uuid)?.let { _ ->
val payload = when {
characteristic.isIndicatable() -> BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
characteristic.isNotifiable() -> BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
else -> error("${characteristic.uuid} doesn't support notifications/indications")
}
characteristic.getDescriptor(configurationCharacteristicUUID)?.let { cccDescriptor ->
if (!deviceBluetoothGatt!!.setCharacteristicNotification(characteristic, true)) {
return
}
cccDescriptor.value = payload
deviceBluetoothGatt!!.writeDescriptor(cccDescriptor)
} ?: this@BLEManager.run {
Log.e("${characteristic.uuid} doesn't contain the CCC descriptor!")
}
} ?: this@BLEManager.run {
Log.e("Cannot find $characteristic.uuid! Failed to enable notifications.")
}
} catch (e: SecurityException) {
Log.d("onMtuChanged: Error occurred: $e")
}
}
} else {
Log.d("onMtuChanged: Error occurred $e")
disconnect()
}
}
// STEP 5 - Receive the message on this callback in blocks
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
with(characteristic) {
decodeResponse(characteristic.value)
}
}
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
with(descriptor) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
writeCharacteristic(GetinfoRequest().encode())
}
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> {
Log.d("Write not permitted for $uuid!")
}
else -> {
Log.d("Descriptor write failed for $uuid, error: $status")
}
}
}
}
}
- Протестированные версии Android: Android 14, Android 15
Дополнительная информация:
Протестированные версии Android: Android 14, Android 15
li>
Затронутые устройства: Google Pixel 6A, Galaxy Tab S8
Подробнее здесь: https://stackoverflow.com/questions/790 ... xy-devices
Мобильная версия