21、蓝牙调试:Bluedroid、HCI日志、配对问题、BLE调试
蓝牙调试,说难不难,说简单也不简单。我见过不少同事,一遇到蓝牙连不上、配对失败,就直接抓个 log 扔给芯片原厂。其实很多问题我们自己就能定位,关键是要知道从哪下手。
今天我就把蓝牙调试的几个核心模块拆开来讲。Bluedroid 架构、HCI 日志怎么抓、配对流程怎么跟、BLE 低功耗调试的要点,咱们一个一个过。
21.1 Bluedroid 架构概览
Android 从 4.2 开始就用 Bluedroid 替代了 BlueZ。说白了,Bluedroid 是 Google 和 Broadcom 一起搞的蓝牙协议栈。它分两层:
- 上层(BTA / BTU):处理蓝牙 profile、配对、连接管理。跑在 app 进程或 system_server 里。
- 下层(HCI / VND):直接跟蓝牙芯片打交道。通过 HCI 命令、事件、数据通道通信。
我个人习惯,遇到蓝牙问题先判断是上层还是下层。怎么判断?看 log 里的 tag。
关键日志 Tag 速查表
| Tag | 所属模块 | 典型问题 |
|---|---|---|
| bt_btif | 蓝牙接口层 | profile 注册失败、回调未执行 |
| bt_btu | 蓝牙任务层 | 协议栈内部状态机异常 |
| bt_hci | HCI 层 | 命令超时、ACL 数据断流 |
| bt_vendor | 厂商适配层 | 固件加载失败、波特率不匹配 |
举个例子。有一次我遇到手机连不上车载蓝牙,log 里全是 bt_btif 的 "state mismatch"。我一看就知道是上层状态机乱了,直接查 profile 连接流程,最后发现是车载端发了两次连接请求,上层没处理好。
21.2 HCI 日志:蓝牙调试的“黑匣子”
HCI 日志是蓝牙调试的基石。它记录了主机和蓝牙芯片之间的所有命令、事件、数据包。你想想看,没有 HCI 日志,你就像蒙着眼睛修车。
怎么抓 HCI 日志?我推荐两种方式:
- 开发者选项抓取:打开「开发者选项」→「开启蓝牙 HCI 信息收集日志」。抓到的文件在
/sdcard/btsnoop_hci.log。 - adb 命令实时抓取:
adb shell cat /sys/kernel/debug/bluetooth/hci0/snoop(需要 root)。
抓到的日志可以用 Wireshark 打开。我个人习惯用 Wireshark 的过滤器:
# 只看配对相关事件
btl2cap.cid == 0x0001
# 只看 HCI 命令和事件
hci_h4.type == 0x01 || hci_h4.type == 0x04
# 过滤特定蓝牙地址
btcommon.bd_addr == xx:xx:xx:xx:xx:xx
我的小技巧:抓 HCI 日志时,最好同时抓 logcat -b all。把系统日志和 HCI 日志放在一起看,能快速定位是上层没发命令,还是芯片没响应。
21.3 配对问题:从 HCI 日志看完整流程
配对失败是蓝牙调试里最常见的坑。我遇到过好多次,用户说“手机搜不到设备”或者“配对码输对了还是连不上”。
正常的配对流程,从 HCI 日志看是这样的:
- Inquiry / LE Scan:发现设备。
- Create Connection / LE Create Connection:建立 ACL 连接。
- IO Capability Request / Reply:交换配对能力(是否支持输入输出)。
- Simple Pairing (SSP) 或 Legacy Pairing:生成密钥。
- Link Key Notification:密钥存储。
- Authentication Complete:配对完成。
如果卡在第三步,通常是 IO Capability 不匹配。比如手机显示“需要输入 PIN 码”,但设备没有键盘。我曾经调试过一个智能门锁,它上报的 IO Capability 是 NoInputNoOutput,但手机端要求 KeyboardOnly,结果配对一直失败。解决办法是让设备端改上报 DisplayYesNo。
注意:有些国产芯片在配对完成后,不会主动发送 Link Key Notification。这时候手机端会认为配对没完成,下次连接又要重新配对。遇到这种情况,可以抓 HCI 日志确认芯片是否真的生成了密钥。
21.4 BLE 调试:连接参数与 MTU
BLE 调试和经典蓝牙不太一样。BLE 更关注功耗和吞吐量。我调试 BLE 时,最常看三个东西:
- 连接参数(Connection Interval / Latency / Timeout):影响功耗和响应速度。
- MTU 大小:影响单次传输的数据量。
- GATT 操作:读写、通知、指示。
连接参数协商,在 HCI 日志里对应 LE Connection Update Complete 事件。如果手机端请求的参数和设备端不匹配,连接可能不稳定。我遇到过一款手环,它只支持 30ms 的连接间隔,但手机端请求了 7.5ms,结果手环直接断开连接。
MTU 协商也很关键。默认 MTU 是 23 字节,但大部分 BLE 芯片支持 512 字节以上。如果 app 需要传输大量数据,一定要主动请求 MTU 更新。代码示例:
// 请求 MTU 为 512
BluetoothGatt gatt = device.connectGatt(context, false, callback);
gatt.requestMtu(512);
在 HCI 日志里,MTU 协商对应 Exchange MTU Request 和 Exchange MTU Response。如果 app 没有发这个请求,那数据包就会被拆成很多小包,效率极低。
21.5 实战:用 HCI 日志定位 BLE 断连问题
我记得有一次,客户反馈他们的 BLE 设备每隔几分钟就断连一次。我抓了 HCI 日志,发现每次断连前都有一个 Disconnection Complete 事件,原因是 0x08(Connection Timeout)。
这说明设备端没有在连接超时时间内收到手机端的包。进一步看日志,发现手机端每隔 100ms 发一次 Empty PDU,但设备端每隔 200ms 才回复一次。手机端的连接超时设置是 500ms,设备端的超时设置是 1s。手机端因为没收到回复,主动断开了连接。
解决办法:让设备端把连接超时设置改为 2s,或者让手机端把连接间隔调大。嗯,这里要注意,连接超时不能设得太长,否则断连后手机要等很久才能发现。
BLE 调试常用 HCI 命令
| 命令 | Opcode | 用途 |
|---|---|---|
| LE Read Remote Features | 0x0016 | 读取对端设备支持的特性 |
| LE Connection Update | 0x0018 | 更新连接参数 |
| LE Read Buffer Size | 0x0002 | 查看蓝牙芯片的 buffer 大小 |
21.6 知识体系:蓝牙调试核心逻辑
下面这张图是我自己总结的蓝牙调试思路。从问题现象出发,先判断是上层还是下层,再选择对应的调试工具。
这张图的核心逻辑就是:先看 logcat 里的 tag,再决定抓什么日志。我个人习惯,遇到蓝牙问题第一件事就是开 HCI 日志,因为它是“铁证”,不会骗人。
21.7 避坑指南
最后分享几个我踩过的坑:
- HCI 日志没抓到:有些手机在开发者选项里开启了 HCI 日志,但实际没写入文件。我建议用 adb 命令实时抓,确保数据落盘。
- 配对码显示问题:如果手机显示 6 位数字,但设备只支持 4 位,配对会失败。可以在 HCI 日志里看
IO Capability Response里的OOB Data字段。 - BLE 连接参数协商失败:有些芯片对连接参数有严格限制,比如连接间隔必须是 10 的倍数。手机端请求 11.25ms,芯片直接拒绝。解决办法是让 app 端先读取设备支持的参数范围。
我的经验:如果你不确定问题出在哪,先抓一份正常的 HCI 日志做对比。我电脑里存了好几份不同芯片的“黄金日志”,遇到问题直接 diff,效率很高。
蓝牙调试其实没那么玄乎。掌握 HCI 日志,理解配对和 BLE 的流程,大部分问题都能自己搞定。嗯,今天就聊到这,希望这些内容对你有帮助。