
搞蓝牙开发的朋友应该都有过这种经历手机和你的板子连不上、传输断断续续、配对闪退打开logcat翻半天翻来覆去就那么几行Device is not connected根本定位不了问题。真正能还原现场的是btsnoop_hci.log——手机蓝牙协议栈把每个HCI数据包都记录下来的黑匣子文件。我这几年调低功耗蓝牙外设至少有一半的疑难杂症是靠小米手机抓的这份日志定位出来的。这篇就从开启开发者选项开始把从手机端抓到btsnoop_hci.log的完整流程整理出来顺带把过程里踩过的坑和怎么看日志的要点一并讲清楚。不管你是做嵌入式、写APP还是单纯想排查自己手环连不上的问题这篇都能当一份直接抄作业的蓝本。1. 为什么蓝牙调试离不开btsnoop_hci.log1.1 这份日志到底是什么btsnoop_hci.log名字看上去像个临时文件实际上它记录的是蓝牙协议栈最底层的一手通信数据。蓝牙协议栈分两层控制器Controller和主机Host两者之间的接口叫HCIHost Controller Interface。手机上运行蓝牙时主机向控制器下命令、控制器回事件、两边传输数据这些交互全部被Android系统蓝牙服务记下来保存成一个btsnoop格式的文件也就是我们说的btsnoop_hci.log。你可以把它理解成蓝牙版的飞机黑匣子每一个广播包、每一个连接请求、每一次断开、每一对连接参数甚至连空中的跳频数据包都有记录。跟logcat最大的区别是logcat属于应用层日志可能被系统过滤掉、延迟掉甚至日志缓冲区满了之后直接丢数据但btsnoop_hci.log存在控制器和主机交互的那一层抓到的都是确确实实发出去和收到的原始数据时间戳也是精确到微秒级别排查协议层问题基本绕不开它。1.2 什么场景下必须靠它我用过的场景大致分五类基本覆盖了大多数蓝牙调试需求连接失败手机和你的设备明明离得很近但就是连不上。这时候看日志里有没有发出LE Create Connection命令、有没有收到LE Connection Complete事件、事件的status是0x00还是报错一眼就能判断是哪一方拒绝了连接。频繁断开连接建立后几分钟就掉线HCI日志里的Disconnect Complete事件会带上Reason Code是超时断的还是对端主动断的看码就知道。广播扫描不到设备明明在广播手机搜不到。日志里能看出来广播包有没有到达手机到了之后是扫描过滤器把它滤掉了还是手机压根没开扫描。这个不抓HCI日志基本只能靠猜。配对/绑定异常配对过程中密钥协商失败、PIN码不匹配日志里会有完整的认证流程记录。传输速率上不去连接参数interval、latency、timeout没协商到预期值导致吞吐量上不去。日志里连接参数更新请求和回应都清清楚楚。在动手抓包之前我也建议先大致估算一下自己遇到的问题属于哪一类这样后面打开日志之后才知道该先看哪个部分而不是对着几千个包发呆。2. 小米手机开启开发者选项的细节2.1 一步一步找到隐藏入口小米手机MIUI和现在的HyperOS默认隐藏开发者选项开启方式不算复杂但路径变过几次很多人卡在找不到版本号。以我手头几台小米手机的实际经验通用路径是打开设置进入我的设备。点击全部参数与信息旧版本叫全部参数。连续点击MIUI版本或HyperOS版本这一栏大概7次。系统会提示您已进入开发者模式如果设置了锁屏密码可能还需要验证一次锁屏密码。回到设置进入更多设置拉到最下方就能看到开发者选项。有几个细节值得注意。第一连点的时候是点击版本号那一行文字不是点击右上角的菜单第二如果一直没反应说明当前系统可能有定制限制先检查一下是不是把系统桌面换成了第三方的、某些权限被改过第三进入开发者模式之后如果没有立刻看到入口直接在设置页顶部搜索框输入开发者选项也能跳转过去这个搜索方式在MIUI的多个版本里都是最省事的方法。2.2 MIUI与HyperOS之间的路径差异我之前在MIUI 12、MIUI 13、MIUI 14和HyperOS上都抓过HCI日志发现入口位置有细微差别写出来给不同机型的朋友参考系统版本开发者选项入口路径蓝牙日志开关大致位置MIUI 12/12.5设置 - 更多设置 - 开发者选项网络分组下叫蓝牙日志MIUI 13/14设置 - 更多设置 - 开发者选项或搜索直达蓝牙调试日志在调试分组HyperOS 1.0/2.0设置 - 更多设置 - 开发者选项部分机型在我的设备里直出打开蓝牙HCI信息收集日志或类似名称另外一个经验是如果手机曾经连过电脑、开过USB调试开发者选项本身不会消失但有些开关会随系统更新重置回去。所以我每次抓日志前的第一步不是直接打开蓝牙日志开关而是先确认开发者选项还在否则后面白忙半天。2.3 正式抓包前需要顺手调好的几个开关进入开发者选项之后除了找到蓝牙日志开关我建议先把下面几件事做掉不然抓包过程容易出幺蛾子打开USB调试后面用adb导出日志会用到授权提示弹出来的时候记得勾选始终允许。关闭系统自动锁屏或调长休眠时间长时间抓包时屏幕一黑某些机型的系统省电策略会把蓝牙相关进程进入休眠导致日志里出现一大段空白。把日志记录器缓冲区大小调到最大这条不是必须的但建议顺手设置因为排查问题时往往也要看logcat配合。不要在开发者选项里随便改蓝牙A2DP、蓝牙AVRCP版本网上有些教程让你改这些值来提升音质跟HCI日志抓取没有任何关系乱改反而会给分析日志增加变量。有个反直觉的点必须提醒很多人以为开发者选项开得越多越好实际上HCI日志只需要开蓝牙日志相关的那一个开关其他调试开关保持默认就行改多了反而干扰判断。3. 蓝牙HCI日志抓取配置与生成3.1 开关名称不一样搜索最靠谱小米手机开发者选项里的蓝牙日志开关不同系统和机型的命名差异蛮大常见的有这么几种蓝牙日志、蓝牙调试日志、打开蓝牙HCI信息收集日志、Open Bluetooth HCI snoop log如果系统语言切成英文。开关是一个Switch控件打开之后不需要立即重启手机但建议重新开关一次蓝牙确保日志记录进程带着新配置跑起来。我的经验是不要硬翻列表找直接在开发者选项页面顶部搜索蓝牙两个字出来的结果里第一个带Switch的通常就是要找的目标。只需要记住一点这个开关打开时Android系统蓝牙服务com.android.bluetooth会在内部挂载一个日志回调将所有经过HCI层的数据包写入文件。关掉开关则立即停止写入不会自动清理掉已有的日志文件。3.2 抓包前的准备动作决定日志可用率抓包最怕的是日志抓到一半发现没开记录或者开了记录但问题没复现等于白做。我一般按照这个顺序来准备清理旧日志先把手机里现存的所有btsnoop文件删掉或改名移走保证接下来抓到的是本次操作产生的数据。记录时间点打开日志开关后看一眼手机时间记下来。后面分析日志时直接按时间戳过滤效率高很多。只保留目标设备把手机上其他已配对的蓝牙设备全部断开最好关掉其他设备的蓝牙避免无关设备的广播、连接请求混进来。让手机保持解锁状态抓包过程中把自动锁屏关掉把屏幕亮度调低放着就行千万不要锁屏。打开蓝牙日志开关后先关一次蓝牙再打开蓝牙这一步能让日志记录器以干净的连接状态开始工作不然某些系统版本会漏掉最开始的命令。之后才开始复现问题。比如你要测连接失败就点APP里的连接按钮要测断开就正常连接后等待它断开。整个过程尽量只操作目标流程不要反复去配对、解绑日志里混入过多操作会显著增加分析难度。3.3 btsnoop_hci.log在小米手机上的保存位置抓完日志之后接下来是找到它在哪。Android原生系统通常把HCI日志放在/data/misc/bluetooth/logs/目录下这个目录需要root权限才看得到。好消息是小米手机在MIUI上做了自定义通常会把日志暴露到用户可访问的目录里。我实测过的路径有这么几个/sdcard/MIUI/debug_log/common/btsnoop_hci.log/sdcard/Android/data/com.android.bluetooth/files/logs//storage/emulated/0/Logs/btsnoop_hci.log不同机型、不同系统版本差异极大最靠谱的办法不是挨个翻目录而是打开手机自带的文件管理APP在搜索框里直接搜btsnoop_hci.log。如果搜不到再试试搜索btsnoop因为部分ROM会把文件命名为btsnoop_hci.log.cfa或类似后缀。有一个容易踩的坑如果你打开文件管理器搜不到先确认文件管理APP是否开启了显示隐藏文件选项。HCI日志文件在某些MIUI版本里被标记为隐藏文件默认搜索不出来。在文件管理器设置里打开显示隐藏文件开关之后再去搜基本都能找到。4. 把btsnoop_hci.log从手机里完整导出4.1 不连电脑也能导的文件管理器方案找到btsnoop_hci.log之后最简单粗暴的方式是直接在文件管理器里长按文件选择发送或分享。小米的手机一般自带快传和发送到电脑的功能但这里有个隐藏的坑部分系统分享菜单会调用小米换机或云服务走的是加密通道文件名会被改掉拿到电脑上Wireshark可能识别不了格式。我自己的做法是优先把文件复制到Download目录再从文件管理器用数据线导出这样能最大程度保证文件没被动过。文件本身一般几MB到几十MB对存储卡来说不算大但复制的时候要注意如果手机打开了MIUI优化并且没有关闭USB安装监控连接电脑后默认是仅充电模式需要在通知栏里把USB模式切到传输文件MTP否则电脑上根本看不到文件。4.2 用adb导出最稳最常用对于经常抓日志的人来说学会用adb是绕不开的一道坎。它的好处是绕开MTP协议的各种问题直接通过USB从底层把文件拉出来速度稳定也不会被系统文件管理器干扰。操作步骤电脑上装好platform-tools装过Android Studio的话自带adb。USB连接手机手机上确认允许USB调试。打开终端执行命令# 先查看日志在哪个路径如果知道确切路径可跳过 adb shell find /sdcard -name *btsnoop* 2/dev/null根据find命令输出结果找到完整的日志路径比如/sdcard/MIUI/debug_log/common/btsnoop_hci.log然后执行adb pull /sdcard/MIUI/debug_log/common/btsnoop_hci.log ./btsnoop_hci.log拉取成功后本地会生成一份一模一样的文件用ls看一下大小跟手机上文件管理显示的大小大致一致说明拉取完整。当然了如果是一个几十MB的日志文件MTP复制可能会在中途卡住但adb pull基本没遇到过这种情况。另外如果在执行find命令时发现设备权限不够可以先执行adb root——不过小米的正式版系统一般不允许root那就只能用文件管理器先复制到 /sdcard再导出。4.3 传输日志时最容易犯的几个错关于日志导出的坑我专门归纳几条不要用蓝牙传日志虽然这个操作本身没什么问题但它会在蓝牙协议栈上产生大量额外数据干扰你后续复现问题而且传几十MB文件的速度慢得让人崩溃。不要用微信传原文件微信接收文件时会另存一个副本后缀名可能变成.log.bak或者其他奇怪名字内容虽然一般不变但有些版本会自动改编码格式导致Wireshark识别异常。不要直接双击打开.log文件Windows不会自动关联btsnoop格式双击默认记事本打开会看到一堆乱码。导出后文件名随意但打开工具一定得是Wireshark或者专用解析工具。我自己通常会把文件重命名为hci_日期_场景的格式比如hci_20250111_连接失败.log存到一个固定的项目目录里方便日后回溯对比。5. 解析日志从原始包到可用结论5.1 Wireshark打开后的第一件事Wireshark原生支持btsnoop格式所以拿到文件后的操作很简单打开Wireshark直接把btsnoop_hci.log拖进窗口它会自动识别出蓝牙相关的协议栈。如果弹出的框问你这是哪种封装格式一般选Bluetooth HCI H4因为Android日志导出的基本就是H4封装。打开之后你会看到几千行甚至几万行的数据包记录这个时候千万别盯着全量列表发呆。我的习惯是先看时间分布看一眼第一个包和最后一个包的时间戳确认覆盖了复现问题的前后窗口。如果文件是持续记录很久的优先用显示过滤器把操作窗口内的时间段筛出来。几个我日常用最多的过滤表达式btl2cap查看逻辑信道上的数据交互适合分析连接建立过程。btrfcomm查看串口模拟通道的数据适合传统蓝牙SPP调试。btatt过滤ATT层的读写请求和响应GATT调试基本靠它。hci_event只看HCI事件搭配btsnoop的显示字段能快速判断当前连接状态。5.2 借助小牛蓝牙调试助手这类工具快速上手我知道不是所有人都有耐心每周对着Wireshark上千个包做协议分析尤其是刚接触HCI日志的同学看到十六进制数据往往一头雾水。这两年开始有一些国产的辅助工具专门做蓝牙日志的解析和可视化小牛蓝牙调试助手就是搜索时大家常提到的一个。这类工具的思路很简单把btsnoop日志里的原始数据包解析成可读事件流比如设备发起了连接请求连接参数协商成功收到断开请求原因0x13。它会自动把GATT读写、广播包里的厂商自定义数据、连接事件都列出来不用手动去Wireshark里翻。对于快速判断问题发生在哪一层这种辅助工具确实省时间。不过我也提醒一句辅助工具不等于分析工具它最多帮你定位到某个事件最终的根因判断还是要靠对蓝牙协议的理解。比如它告诉你收到了断开连接事件reason code 0x08但0x08意味着什么、是手机主动断还是对端不发包导致超时这个判断还是要回到协议本身。所以我的用法是先用辅助工具跑一遍拿到初步结论后再回到Wireshark对应的时间点核验数据包内容两者互补才比较稳妥。5.3 直接读三大关键信息广播、连接建立、断开原因如果想完全靠自己读日志最值得优先掌握的是三段内容。第一段是广播包。扫描不到设备时先用btle.advertising_address之类的过滤条件找广播包。看包里有没有目标设备的MAC地址有的话说明物理层把广播收上来了问题出在上层过滤或扫描参数没有的话说明广播连手机都没收到就要检查设备端的广播功率、广播间隔或者周围环境干扰。第二段是连接建立过程。使用btatt或btl2cap过滤后找到LE Create Connection (0x200D)命令再往下找对应的LE Connection Complete (0x200E)事件。这个事件的Status字段0x00代表成功非0则代表失败。失败时里面带有具体的错误码比如0x3E表示连接建立失败这时候基本可以断定是物理链路没建立起来而不是上层GATT的问题。第三段是断开原因。这是所有蓝牙调试者必须背得滚瓜烂熟的一张表。断开连接的包在HCI层是Disconnect Complete事件里面带一个Reason Code字段我用Excel整理了一份高频值做成了表格Reason Code含义常见原因0x08Connection Timeout链路超时对端设备在超时时间内没有响应最常见于设备离太远或没做链路维持0x13Remote User Terminated Connection对端设备主动断开常见于应用层调用了disconnect接口0x16Connection Terminated by Local Host本地手机主动断开可能是APP调了close或系统判定异常0x22LL Response Timeout链路层响应超时通常说明对端芯片异常或进入休眠0x3EConnection Failed to be Established连接建立失败链路层压根没握手成功物理环境干扰较多每次拿到一个莫名其妙断开的问题我都先看Reason Code再沿着时间倒推几十个包基本能还原出断开的完整脉络。比如Reason Code是0x08往前翻大概率能看到手机发过连接参数更新请求一直没收到ACK或者收到过多个空包但没回应。这样原因就从它自己断了变成断之前发生了什么整个问题就等于解决了一大半。6. 抓包路上的坑我替你踩过的几个版本6.1 日志开关开了但文件没生成这个问题我被问过不止一次自己也踩过。明明开发者选项里的蓝牙日志开关已经打开复现完问题去搜btsnoop_hci.log却一无所获。排查步骤并不复杂按顺序来确认开关状态回开发者选项看开关是否还是打开状态。部分系统在切换网络或重启蓝牙后开关会自动复位。确认文件管理器权限搜索不到不代表文件不存在可能只是隐藏文件没显示开启显示隐藏文件再搜。确认有没有点过清除所有数据如果之前清理过蓝牙相关的应用数据日志目录结构可能会重建路径变了但搜索还是应该能搜到。如果上述都不行试试重启手机重启后再开关一次蓝牙日志重新抓一次基本都能解决。6.2 日志文件过大导致导出失败长时间开着蓝牙日志不关文件能涨到一两百MB。文件太大导出时容易出现两种现象一是用文件管理器复制到电脑时进度条卡在99%不动二是能复制但Wireshark打开时内存吃满卡到崩溃。解决办法也很简单抓包前预估好时间窗口只记录和问题相关的5到10分钟操作不要从早上抓到晚上导出前如果文件已经很大可以在手机上先对它做一次重命名或者移到另一个目录然后用zip压缩包导出减小体积的同时也避免了传输过程中被系统杀掉的风险。6.3 分析时发现日志里全是空白有一次我抓了一整天的设备交互日志打开却发现后半段全是空白最后才想起来是屏幕在抓包过程中自动锁了MIUI的省电策略把蓝牙扫描服务裁剪了导致空中抓包窗口出现大段空档。从那以后我的抓包前准备清单里必定有关闭自动锁屏这一项并且会把手机插上充电器防止电池低功耗模式干扰蓝牙。还有一次抓完发现logcat里有大量蓝牙日志丢失的警告查了才知道是手机开了极致省电模式。所以抓包期间把省电模式、极致省电全部关掉蓝牙相关的系统进程才能正常工作。7. 一点个人经验收尾最后一次分享一个我自己的习惯不管用小米还是其他安卓机型正式抓包之前先把手机重启一次把后台乱七八糟的连接清干净然后再只打开目标设备的连接程序。这样抓出来的btsnoop_hci.log里几乎不会有干扰数据打开Wireshark之后整个分析过程都能快上一倍。另外每次抓完日志记得第一时间在文件管理器里把它复制一份备份文件名加上日期和场景描述。这份日志不只是给你自己看的也可能在提交给芯片原厂、蓝牙协议栈供应商或者带着去问同事的时候派上大用场。一个干净的、附带了时间背景的HCI日志往往比一段文字描述的问题更能让研发人员快速判断真因。愿大家都能干净利落地抓到目标包少走弯路。