Android 13设备重连蓝牙GATT后重复写入特征的原因排查
问题描述
使用搭载Android 13系统的LM-G900UM手机作为BLE主机,与支持Microchip UART LE协议的从机芯片通信。正常通信无异常,但重连后首次调用writeCharacteristic时,同一特征会被写入两次,几秒后恢复为正常的单次写入。
已通过日志确认:应用仅执行了一次写入操作,但从机芯片报告接收到两次相同的写入数据。且已验证当didWrite被设置为成功时,不会触发重复写入逻辑。
写入特征的实现代码
val writeCode = mGatt.writeCharacteristic(rxChar, data, chunk.writeType) // writeType is WRITE_TYPE_DEFAULT when (writeCode) { BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND -> mLogger.log(Level.WARNING, "UART service not bound") BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> mLogger.log(Level.WARNING, "The receiver RX characteristic is not currently writable") BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> mLogger.log(Level.WARNING, "The receiver gatt interface is too busy to write to the receiver") 4 -> { // ERROR_DEVICE_NOT_CONNECTED mLogger.log(Level.WARNING, "The receiver is disconnected and cannot be written to. Reconnecting to gatt") connectGatt() } else -> if (writeCode != BluetoothStatusCodes.SUCCESS) mLogger.log(Level.WARNING, "Unknown error writing to the receiver: $writeCode") } didWrite = writeCode == BluetoothStatusCodes.SUCCESS
特征写入的回调处理逻辑
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { onCharacteristicWrite(characteristic, status) } fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) { val data = characteristic.value // If this timeout occurs, the data is re-written (this is not appearing in the logs) stopWriteTimeout() // mWritingChunk is set to a class containing the data just before it is written if (mWritingChunk != null) { val chunk = mWritingChunk!! mWritingChunk = null if (status != BluetoothGatt.GATT_SUCCESS) { mLogger.log(Level.WARNING, "Unable to write to the receiver; Re-adding the write request. The status was: $status") writeBuffer.remove(chunk) writeBuffer.addFirst(chunk) } else if (characteristic.writeType == BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE || Arrays.equals(chunk.data, data)) { // This portion is purely for logging if (data == null) { if (chunk.data.size > sentenceStart.size + sentenceEnd.size && isASCIIPrintable(chunk.data, 1, chunk.data.size - 2)) mLogger.log(Level.INFO, "Did write chunk: " + prettifyLine(String(chunk.data, StandardCharsets.US_ASCII))) else mLogger.log(Level.INFO, "Did write chunk: <binary> (bytes: " + chunk.data.size + ")") } else if (data.size > sentenceStart.size + sentenceEnd.size && isASCIIPrintable(data, 1, data.size - 2)) mLogger.log(Level.INFO, "Did write: " + prettifyLine(String(data, StandardCharsets.US_ASCII))) else mLogger.log(Level.INFO, "Did write: <binary> (bytes: " + data.size + ")") mLock?.didWrite(chunk) } else if (data != null) { if (data.size > sentenceStart.size + sentenceEnd.size && isASCIIPrintable(data, 1, data.size - 2)) mLogger.log(Level.INFO, "Unrecognized write: " + prettifyLine(String(data, StandardCharsets.US_ASCII))) else mLogger.log(Level.INFO, "Unrecognized write: <binary> (bytes: " + data.size + ")") } else mLogger.log(Level.WARNING, "Characteristic data was null") } else mLogger.log(Level.SEVERE, "Characteristic was written to without flushing any chunk!") // Takes the next entry in the buffer to write writeFlush() }
排查方向与解决方案
1. 同步BLE重连后的Gatt状态
重连后BLE栈可能未完成初始化,此时发起写入可能被栈重复发送:
- 重连成功后延迟1-2秒再执行首次写入,等待服务发现、特征初始化流程完成。
- 确保在
onServicesDiscovered回调触发后再执行写入,避免服务未完全发现时发起请求。
2. 检查从机重连后的初始化逻辑
从机可能存在状态未重置的情况:
- 重连时清空从机的BLE接收缓冲区,避免残留数据被误判为新写入。
- 检查从机UART LE协议实现,确认重连后是否正确处理了GATT连接参数更新,避免接收逻辑异常。
3. 调整写入类型与确认机制
当前使用的WRITE_TYPE_DEFAULT需要从机响应,重连后可能因ACK丢失触发栈自动重传:
- 临时切换为
WRITE_TYPE_NO_RESPONSE测试,观察是否仍有重复写入(注意此类型无ACK,适用于非关键数据)。 - 在
onCharacteristicWrite回调中,仅收到GATT_SUCCESS后再触发下一次写入,避免写入队列异常。
4. 排查设备特定BLE栈问题
LM-G900UM的Android 13 BLE栈可能存在重连bug:
- 测试其他Android 13设备是否有同样问题,确认是否为设备特定问题。
- 安装设备最新系统补丁,修复可能的BLE栈漏洞。
内容的提问来源于stack exchange,提问作者cj-
相关产品推荐
相关产品推荐

