You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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-

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 14:37:00