16、Wi-Fi与蓝牙:wpa_supplicant、Bluedroid协议栈、HCI层
说到嵌入式Android的无线通信,Wi-Fi和蓝牙绝对是绕不开的两座大山。我在做车载系统那会儿,这两个模块几乎天天打交道。你想想看,用户连不上Wi-Fi会骂娘,蓝牙断连也会骂娘——所以搞懂它们的底层原理,真的很重要。
这一章,我们就来扒一扒wpa_supplicant、Bluedroid协议栈和HCI层。说白了,就是搞清楚Android是怎么管Wi-Fi和蓝牙的。
16.1 wpa_supplicant:Wi-Fi的守门人
wpa_supplicant,这个名字看着挺唬人,其实它就是个Wi-Fi认证的守护进程。我习惯叫它「wpa小助手」。它运行在用户空间,负责跟Wi-Fi驱动打交道,处理扫描、关联、认证、密钥协商这些脏活累活。
为什么Android要用它?因为wpa_supplicant是开源界的标准实现,支持WPA/WPA2/WPA3,甚至企业级的EAP认证。你想想看,要是每个厂商都自己写一套Wi-Fi认证逻辑,那还不乱套了?
核心要点:wpa_supplicant通过socket与Framework通信。Framework发命令,wpa_supplicant执行,然后返回结果。典型的C/S架构。
它的工作流程大致是这样的:
- 扫描:Framework下发SCAN命令,wpa_supplicant驱动网卡扫描周围AP。
- 选择网络:Framework根据扫描结果,选择一个AP下发连接指令。
- 关联与认证:wpa_supplicant完成802.11关联,然后进行WPA四次握手。
- 获取IP:关联成功后,DHCP获取IP地址,网络就通了。
我在项目中遇到过一个问题:某款芯片在WPA3连接时总是超时。查了半天,发现是wpa_supplicant的配置文件里缺少了SAE(Simultaneous Authentication of Equals)的支持。说白了,就是编译时没把WPA3的feature打开。嗯,这种坑踩过一次就记住了。
调试小技巧:用 logcat -s wpa_supplicant 可以实时看wpa_supplicant的日志。如果连不上Wi-Fi,先看这里,80%的问题都能找到线索。
16.2 Bluedroid协议栈:蓝牙的神经中枢
蓝牙这块,Android从4.2开始就用Bluedroid替代了BlueZ。Bluedroid是Broadcom贡献的,后来Google一直在维护。它分为两层:
- BTA (Bluetooth Application):上层应用接口,处理各种profile,比如A2DP(听歌)、HFP(打电话)、GATT(低功耗数据传输)。
- BTC (Bluetooth Core):核心协议栈,处理L2CAP、RFCOMM、SDP这些底层协议。
我个人习惯把Bluedroid想象成一个「协议翻译官」。上层应用说「我要连蓝牙耳机听歌」,Bluedroid就把它翻译成A2DP的协议指令,然后通过HCI层发给蓝牙芯片。
Bluedroid的启动流程是这样的:
- Framework调用
BluetoothAdapter.enable()。 - Bluedroid加载固件,初始化HCI接口。
- 启动BTIF(Bluetooth Interface)层,注册各种回调。
- 扫描、配对、连接,一气呵成。
注意:Bluedroid是C++写的,但它的上层接口是通过JNI暴露给Java Framework的。所以如果你在调试蓝牙,经常会在JNI层看到各种崩溃。我曾经遇到一个bug,就是JNI的全局引用没释放,导致蓝牙反复开关后内存泄漏。排查了两天才找到根因。
16.3 HCI层:硬件与软件的桥梁
HCI,全称Host Controller Interface。它是主机(CPU侧)和控制器(蓝牙芯片侧)之间的通信协议。说白了,就是软件怎么跟蓝牙芯片说话。
HCI定义了三种数据传输方式:
| 类型 | 用途 | 示例 |
|---|---|---|
| Command | 主机下发指令给控制器 | 扫描、连接、断开 |
| Event | 控制器上报事件给主机 | 连接成功、断开通知 |
| Data | 传输实际数据 | A2DP音频流、GATT数据 |
在Android里,HCI的实现通常通过UART或USB接口。我记得在调试一款低端平板时,蓝牙经常断连。抓了HCI日志一看,发现是UART的波特率设置不对,导致数据包频繁丢帧。调了一下设备树里的蓝牙UART配置,问题就解决了。
调试利器:抓HCI日志可以用 adb shell hcidump 或者 btmon(如果芯片支持)。HCI日志能让你看到蓝牙芯片和CPU之间所有的原始通信,是排查蓝牙问题的终极手段。
16.4 三者如何协同工作?
你可能会问:wpa_supplicant、Bluedroid和HCI层,它们之间是什么关系?
其实很简单:
- Wi-Fi这边:Framework → wpa_supplicant → Wi-Fi驱动 → 硬件。wpa_supplicant是软件层面的核心,驱动负责跟硬件交互。
- 蓝牙这边:Framework → Bluedroid → HCI层 → 蓝牙芯片。Bluedroid处理协议,HCI层负责传输。
它们俩在Android系统里是独立的两个模块,互不干扰。但底层都用到了Linux的驱动框架和硬件抽象层(HAL)。
一个经验:如果你在调试Wi-Fi和蓝牙共存的问题(比如同时连Wi-Fi和蓝牙耳机时互相干扰),记得检查一下芯片的共存机制。很多芯片支持2.4GHz频段的时分复用,但需要驱动和固件配合。我曾经调过一个项目,Wi-Fi和蓝牙共用一根天线,结果因为共存策略没配好,Wi-Fi速度直接掉到原来的三分之一。
16.5 实战:如何快速定位问题?
最后,分享几个我常用的定位方法:
- Wi-Fi连不上:先看wpa_supplicant日志,确认是扫描不到AP,还是认证失败。如果是认证失败,检查密码和加密方式。
- 蓝牙搜不到设备:看HCI日志,确认扫描命令是否下发成功。如果没下发,可能是Bluedroid没启动;如果下发了没回应,可能是蓝牙芯片固件挂了。
- 蓝牙断连:抓HCI日志,看是主机主动断的,还是控制器上报的断开事件。如果是控制器上报的,大概率是信号干扰或固件bug。
避坑指南:我曾经遇到一个蓝牙配对后无法连接的问题。查了三天,最后发现是Bluedroid的配对记录缓存满了。Android的蓝牙配对记录默认存储在/data/misc/bluetooth/目录下,如果设备频繁配对不同设备,缓存会爆。解决方案是定期清理,或者修改Bluedroid的缓存上限。
好了,这一章的内容就到这里。Wi-Fi和蓝牙虽然复杂,但只要掌握了wpa_supplicant、Bluedroid和HCI层这三个核心,大部分问题都能迎刃而解。下次遇到无线连接的问题,别慌,先从日志入手,一步步排查。