作为一名常年跟蓝牙外设打交道的开发我太清楚那种“设备就在眼前回连就是失败Logcat翻了几百屏也看不出所以然”的滋味了。后来真正解决问题靠的是小米手机上抓一份HCI log再用Wireshark打开一帧一帧看协议栈底层的交互问题基本半小时内就锁定了。这篇就把我一直用的小米手机HCI log抓取与解析流程完整写出来含开关位置、导出命令、解析要点和避坑记录照着做五分钟就能上手。1. 先搞清楚HCI log到底在记录什么1.1 一次真实的蓝牙调试现场之前做一款BLE体重秤的适配遇到一个特别诡异的问题小米手机上App读历史记录经常超时但同一台秤换iPhone就一切正常。我在Logcat里看了半天只看到GATT回调偶尔报133错误至于为什么报133、是手机发的读请求没到秤端还是秤端回了数据但手机没收到完全看不出来。后来在小米手机上打开蓝牙HCI日志重新复现了一次超时导出日志用Wireshark打开一帧一帧对照发现是秤端返回的Read Response长度字段和实际数据长度不一致手机协议栈直接丢弃了根本没有上抛到应用层。这种问题不到HCI层是绝对定位不了的。1.2 HCI log和Logcat的分工很多初学者容易把Logcat和HCI log混为一谈。简单说Logcat是Android系统层面的日志记录App调用、系统服务状态、各种print输出HCI log则是蓝牙控制器Controller和蓝牙协议栈Host之间所有HCI报文的全量记录。用生活类比Logcat好比公司前台登记访客的记录写着“谁来了、几点走的”HCI log则是大厦监控视频每一帧画面都在谁按的电梯、在哪个楼层停留、门禁为什么没开全部有据可查。所以当问题发生在系统上层比如权限没开、服务没绑定Logcat基本够用但当问题涉及连接建立、断开、配对、加密、断连重连这些协议栈层面的交互时必须上HCI log。1.3 什么场景必须依赖HCI log根据我的经验这几类问题在小米手机上排查时HCI log几乎是唯一靠谱的突破口连接时好时坏回连成功率不稳定配对反复失败或配对成功后重启又失效连接后不久自动断开间隔没有明显规律GATT读写超时或偶尔返回133、257这类系统错误码多设备同时连接时的互相干扰问题不同品牌手机行为不一致需要对比协议栈层面的差异一句话只要问题涉及“蓝牙链路本身”先抓HCI log别在Logcat里瞎猜。2. 小米手机抓取HCI log从开关到拿到文件2.1 准备工作开发者模式与日志开关在小米手机上抓HCI log第一步是先确保开发者选项已经打开。路径是“设置 → 我的设备 → 全部参数与信息 → 连续点击OS版本号或MIUI版本号7次”直到提示已进入开发者模式。接着进“设置 → 更多设置 → 开发者选项”里面有几个开关容易让人晕我逐个说清楚“日志抓取”或“日志分析”总开关部分机型需要先打开这个下面才会显示更多日志细分项“蓝牙HCI日志”这是核心开关打开后系统会把HCI报文存到btsnoop文件“蓝牙调试日志”一些机型上是这个叫法作用一致需要注意的是开启蓝牙HCI日志后很多小米机型会先关闭蓝牙需要在通知栏重新打开蓝牙。这不是故障是在重置蓝牙芯片的日志记录上下文保证后续新开的连接都会被完整记录下来。2.2 开启HCI日志的两种方式我平时常用的方式有两种按场景选。第一种是纯界面操作适合临时抓一次日志。设置 → 开发者选项 → 打开“蓝牙HCI日志”重新打开蓝牙复现问题然后到文件管理器里把日志导出来。日志默认在“内部存储 / MIUI / log / bluetooth /”目录下文件名一般是btsnoop_hci.log或带时间戳的hci_xxx文件。第二种是配合adb命令适合需要长时间抓取或者自动化复现的场景。先把开发者选项里的USB调试打开连接电脑后执行# 查看当前蓝牙HCI日志开关状态 adb shell settings get global bluetooth_hci_log # 打开HCI日志部分平台支持 adb shell settings put global bluetooth_hci_log 1 # 重启蓝牙服务让其生效 adb shell service call bluetooth_manager 6这里多说一句service call bluetooth_manager 6这个写法在不同Android版本上可能不稳定我一般直接在手机上手动关开一下蓝牙更省事。日志开关打开后为了保险起见我习惯再用命令确认一下配置有没有写进去。导出日志时我推荐用adb pull比在手机上翻文件管理器可靠得多# 导出整个蓝牙日志目录 adb pull /sdcard/MIUI/log/bluetooth/ D:/bt_log/ # 如果目录不存在试着找一下常见路径 adb shell ls /sdcard/MIUI/log/ adb shell ls /sdcard/Android/data/com.android.bluetooth/files/部分小米机型权限收紧后/sdcard/MIUI/log不一定直接可见这时候可以先执行一次adb bugreport把系统诊断包拉到电脑上里面通常也会附带蓝牙HCI日志只是路径更深稍麻烦一点。2.3 5分钟流程怎么安排最合理很多人说的5分钟搞定其实不是打开开关那一下而是一条完整的操作链路。我自己的固定节奏是这样头1分钟开启开发者选项里的蓝牙HCI日志关闭再打开蓝牙确认状态正常。中间2分钟按照预先想好的测试步骤“干净地”复现一次问题。注意不要在这个阶段做其他无关操作比如切Wi-Fi、刷抖音这些都有可能在日志里引入大量干扰帧。最后2分钟用adb pull或文件管理器把日志文件导出拉到电脑上用Wireshark打开。整个过程看起来简单但大部分人在第一步就栽了——没复现就切走了导出来的日志里什么都没有。另外如果问题不是必现的我建议把“蓝牙HCI日志”长期开着日志文件会滚动覆盖出问题时再导出比临时打开等着复现有效得多。3. 用Wireshark解析HCI log的实战细节3.1 工具选择为什么首选Wireshark市面上能看btsnoop的工具有不少Frontline、Ellisys这些商业方案功能很强但价格不菲且上手成本高。对大部分Android开发者来说Wireshark完全够用而且免费、跨平台、对btsnoop格式支持得非常好。小米手机抓出来的日志一般就是标准btsnoop格式Wireshark可以直接打开。如果遇到文件后缀是.log但Wireshark识别不出来的情况可以尝试在打开文件时手动选择“Bluetooth Snoop Capture”作为解析格式多数时候能救回来。3.2 打开文件与过滤器的使用用Wireshark打开HCI log后界面会直接按时间顺序显示所有蓝牙HCI报文。不熟悉的人第一眼会觉得很乱因为里面全是HCI_Command、HCI_Event、HCI_ACL这种字样。我的建议是先用过滤栏把协议类别分开。最常用的几个过滤器btl2cap 只看L2CAP层适合分析连接建立和通道管理 btgatt 只看GATT层适合分析服务发现和读写请求 bthci_evt 只看HCI事件适合快速找连接、断开、配对状态变化 bthci_cmd 只看HCI命令适合确认主机到底下发了什么指令 btsmp 只看安全管理层适合定位配对和加密问题举个例子如果怀疑是断连问题直接在过滤栏输入bthci_evt bthci_evt.code 0x05就能把所有Disconnection Complete事件筛出来每条记录都会带Reason字段那个数字就是断连原因码。如果是GATT读写问题建议用btgatt过滤然后在“电话”右侧的协议详情里双击某条GATT请求右键选择“Follow”可以追踪同一条ATT通道上的完整交互序列很快就能看出是请求没发出去还是响应没回来。3.3 三种典型故障在log里的样子解析这块光讲理论容易飘我拿三个真实遇到过的场景演示一下特征长什么样。场景一是“扫描不到设备”。在HCI日志里主机会周期性地下发LE Set Scan Parameters和LE Set Scan Enable命令事件里会有LE Advertising Report。如果扫描命令正常下发但完全没有Advertising Report说明手机射频或天线附近干扰严重或者设备压根没在广播如果连Scan Enable命令都没有那问题在协议栈上层跟设备端无关。场景二是“配对失败”。配对过程里一定会出现SMP层的Command和Response如果看到Pairing Failed后面带一个Reason例如0x05表示不支持的配对方式0x08表示密钥或确认值不匹配排查方向就非常明确了。我曾遇到过一个设备HCI日志里反复出现Security Request后主机直接返回Negative Reply一看就是设备端bonding信息没存住每次都当成新配对来处理。场景三是“连接建立后立刻断开”。一般会看到三条连续的记录Connection Complete成功、随后不久出现Disconnection Complete、Reason指向0x08或0x13。0x08代表连接超时常见于设备端没有及时回应握手包0x13代表对端主动断开常见于设备端业务逻辑主动踢人。这一步定位后再联合设备端日志一起看基本都能收敛。4. 从HCI log反推根因错误码与交互链4.1 错误码速查与责任划分HCI log最有价值的地方在于断连原因码不会说谎。我把开发中高频遇到的错误码整理成了速查表解析时对照着看非常方便错误码含义一般指向0x08Connection Timeout对端没有及时响应链路超时0x13Remote User Terminated Connection对端主动断开0x16Connection Terminated by Local Host手机本地主动断开0x0DConnection Rejected due to Limited Resources设备资源不足常见于设备端连接数满0x0EConnection Rejected due to Security Reasons安全机制拒绝连接0x05PIN or Key Missing配对密钥丢失或未保存0x3EConnection Failed to be Established连接建立失败物理层或对端无响应需要说明的是不同蓝牙芯片厂商在错误码映射上可能存在细微差异速查表只作为参考方向拿到具体日志后还是要去对照相应IC的文档确认一次。判断责任方的逻辑其实很简单先看谁发的Disconnection Complete本地芯片给的事件Reason代表手机协议栈或驱动那边的判断远端断的一般会通过Link Loss或对端主动断开的Reason传回来。再结合时间点前后有没有对端发来的数据基本就能确定是手机的问题、协议栈的问题还是设备端的问题。4.2 时间轴对齐与GATT交互追踪抓HCI log之后只做单看往往还不够。我习惯把HCI log和Logcat按时间戳对齐来看特别适合那种“应用层看起来卡了很久但协议栈层面实际已经走完好几轮交互”的场景。做法是先在HCI log里找到问题现象对应的时间点记下时间戳然后回到Logcat里把同一时间段的日志筛出来看应用层收到回调的时机。如果HCI log显示数据早就到了但Logcat回调一直没有触发那就是数据在内部分发环节丢了如果HCI log里显示一直在重传或等待ACK那就要去查空包和干扰问题。追踪GATT交互时Wireshark的“电话”追踪功能很好用。两条GATT消息之间的时间间隔非常直观只要间隔超过预期值几百毫秒甚至几秒基本就是中间有重传或退避逻辑在作祟。4.3 业务问题定位思路除了协议栈层面的问题HCI log还能帮我们反向验证业务层逻辑是否正确。比如低功耗蓝牙设备常见的电量上报、步数同步、固件升级这类业务本质上都是GATT上的一次次读写和通知。排查时先把台账拉出来连接是哪个设备、服务发现是否完成、MTU协商到了多少、每次Write和Notification的字节数是否符合预期。这些信息在HCI log里全部都有。我之前遇到一个OTA升级到一半总失败的问题就是通过HCI log发现每次大包传输时设备端在收到Write Request后都没有回Write Response直到主机端触发了超时才中止。看log的时候那个等待时间序列特别明显几乎是一帧一个hole。后来定位到是设备端Flash写入耗时过长完全依赖主机的流控保护手机蓝牙协议栈只要稍调一下连接参数就扛不住了。这种问题不抓HCI log单看应用层回调是真的看不出来。5. 小米手机HCI log抓取的常见问题排查实录5.1 开关找不到或日志抓不到这是我被问过最多的问题没有之一。在小米手机上开发者选项里如果没有“蓝牙HCI日志”这个选项先不要急着怪系统大概率是日志抓取总开关没有打开。先到开发者选项里找到“日志抓取”或“日志分析”把它打开有些网上放出的教程会管它叫“日志捕获”小米澎湃OS上的入口名称可能还会变多留意一下。如果总开关已经打开还是看不到蓝牙日志选项可以试试插上USB线连电脑开启USB调试后输入adb shell settings put global ble_log_on 1 adb shell settings put global bt_hci_log_on 1然后重新开关蓝牙。部分开发版系统支持这两个全局配置项稳定版不一定全有但值得一试。还有一种情况是日志开关都开了文件也生成了但内容只有几百字节。这个大概率是打开日志开关之后没有重新开关蓝牙导致蓝牙芯片的日志功能没有实际加入新的会话。处理方法就是先关掉蓝牙再打开蓝牙然后重新连接设备复现问题。5.2 文件异常与解析失败Wireshark打不开日志文件先别急着换工具。btsnoop文件有个固定文件头如果文件被截断或者中间有坏块Wireshark就会报错。这时可以用十六进制工具打开看看文件头是不是62 74 73 6E 6F 6F 70也就是字符btsnoop如果是基本没救不了如果不是有可能文件被系统转换过格式或者只抓到了部分包可以试试用text2pcap工具把纯文本的日志转换成pcap格式再打开。另外在小米手机上如果用文件管理器直接查看日志文件有可能会看到两个相似文件一个带后缀.cfa或.tmp一个才是当前可用的。优先导出不带后缀修改时间最新的那个。文本模式下的HCI log解析是另一个坑。小米部分机型导出的日志不是标准btsnoop二进制而是类似HCI RX、HCI TX开头的人类可读文本。这时候直接拖进Wireshark是打不开的可以用text2pcap转一下或者干脆换用Android Studio自带的Device File Explorer从/data/misc/bluetooth/logs/目录导出原始文件。5.3 容易混淆的细节与经验技巧最后分享几个我在实际过程中踩过坑才总结出来的经验每一条都对应过一次真实的加班第一抓HCI log时尽量把手机上的其他蓝牙设备断开。小米手机经常会同时连着耳机、手表、手环这些设备这些设备产生的周期性连接和断连信息会把日志搞得很杂分析时很难聚焦到目标设备上。如果条件允许清空其他配对记录再抓否则后期过滤时很痛苦。第二HCI log里没有“应用名”这个概念只有蓝牙设备地址。想在日志里定位到自己的设备先在Logcat里找到目标设备地址再在Wireshark里用设备地址过滤而不是盯着日志翻。第三抓日志前关闭蓝牙后重开不只是为了让功能生效更是为了拿到一条“干净的连接时间线”。从扫描到连接、服务发现、MTU协商再到业务发送完整走一遍后面任何一环出问题都能顺着时间线往前找。第四动态调整连接参数的问题。如果问题表现为偶尔卡顿、时延忽高忽低可以重点看HCI log里LE Connection Update Complete事件前后的连接间隔和从机延迟参数。小米手机在系统负载变化时会调整蓝牙参数这在某些芯片上是正常行为但如果你做的是对时延敏感的音频或运动数据设备就得在设备端做兼容。6. 最后再分享一个小技巧日志自动化保存如果你的日常工作需要经常复现蓝牙问题我强烈建议在小米手机上做一次“半自动化”的日志习惯把“蓝牙HCI日志”长期开启手机存储充裕的情况下它会滚动覆盖问题出现时再快速导出问题没出现时也不用管它。导出时除了HCI log最好同时导出adb bugreport的压缩包里面有系统版本、蓝牙协议栈版本、已配对设备列表等信息。排查问题时这些资料往往是HCI log之外最有力的佐证。我自己还会在电脑上建立一个按日期命名的日志归档目录每份日志都附一段文字描述记下现象、操作步骤、设备型号和系统版本。坚持一段时间后你会发现很多“偶发问题”其实是同类原因反复出现只是之前从没把日志留存下来做对比分析。小米手机上抓HCI log这件事难度真的不大关键是要养成“先抓日志再分析”的习惯。协议栈底下的报文不会说谎数据在哪、卡在哪、谁拒绝了谁全部一清二楚。希望这篇内容能帮你少走一些弯路。