1. 从零开始:理解Android蓝牙的“骨架”
如果你刚接触Android蓝牙开发,可能会被一堆缩写搞晕:HCI、BTM、BTU、AIDL、HIDL…… 这感觉就像面对一台精密仪器的内部,到处都是齿轮和管线,不知道从哪里看起。别担心,今天我就带你从最底层开始,像拆解一台机器一样,把Android 12的蓝牙框架彻底捋清楚。我在这上面踩过不少坑,也花了很多时间读源码,希望能帮你省下那些折腾的功夫。
首先,我们得建立一个最基础的认知模型。在Android世界里,蓝牙功能不是由一个单一的程序完成的,它更像一个分工明确的团队。这个团队主要分成三大块:
- Bluetooth App (
bluetooth.apk):这是你手机设置里那个“蓝牙”开关背后的应用。它负责提供用户界面(UI),处理你的点击操作,比如开启蓝牙、搜索设备、配对等。它就像一个“前台接待”,直接和你打交道。 - 蓝牙协议栈 (
bluedroid):这是整个蓝牙功能的核心大脑,位于system/bt目录下。它负责实现蓝牙技术规范里定义的所有复杂协议,比如如何发现设备、如何建立连接、如何传输音频或文件。我们后面要深入剖析的BTM、BTU、BTA等模块,都在这里。它相当于“技术工程师团队”。 - 厂商适配层 (
libbt-vendor):这是连接“软件大脑”和“硬件身体”的桥梁。因为不同手机厂商使用的蓝牙芯片(比如高通、博通、联发科)可能不同,它们的硬件指令、初始化流程会有差异。这一层就是由芯片厂商提供的,用来适配自家的硬件。它就像是“硬件驱动专员”。
那么,数据是怎么流动的呢?想象一下,你在蓝牙设置里点击了“开启扫描”。这个点击动作首先被bluetooth.apk捕获,然后它通过一种叫AIDL (Android Interface Definition Language) 的进程间通信机制,告诉系统服务:“嘿,用户想扫描了”。系统服务(具体是BluetoothService)收到请求后,就会调用底层bluedroid协议栈提供的接口。
bluedroid协议栈开始忙碌起来,它内部的各个模块(BTM管理连接,BTA处理应用逻辑等)经过一系列计算,最终生成一条标准的、蓝牙芯片能听懂的指令。这条指令就是HCI (Host Controller Interface) 命令。HCI是主机(手机CPU)和控制器(蓝牙芯片)之间沟通的“普通话”。
最后,这条HCI命令会通过HIDL (HAL Interface Definition Language) 接口,传递给厂商的libbt-vendor库。这个库知道如何与具体的蓝牙硬件“对话”,它把HCI命令翻译成硬件能理解的信号,通过UART串口或USB总线,发送给蓝牙芯片。芯片执行扫描动作,并将扫描到的设备列表,按照相反的路径一层层上报,最终显示在你的手机屏幕上。
所以,整个流程就是一个自顶向下的请求下发和自底向上的事件上报过程。理解了这个“骨架”,我们就能深入每一块“肌肉”和“神经”去看个究竟了。
2. 基石:HCI层——与硬件对话的“翻译官”
HCI层是整个蓝牙栈的基石,也是软件和硬件的分界线。它的工作非常纯粹:把上层协议栈的逻辑指令,打包成蓝牙芯片规定的格式发送出去;同时,把芯片上报的原始数据,解析成上层能理解的事件。在Android上,由于协议栈在用户空间实现,HCI层就显得尤为关键。
2.1 HCI数据包:芯片能听懂的“语言”
蓝牙芯片不是智能AI,它只认识一种非常格式化的数据包,就是HCI数据包。主要分为三种类型:
- 命令包 (Command Packet):手机发给芯片的指令,比如“开始扫描”、“建立连接”。
- 事件包 (Event Packet):芯片发给手机的反馈,比如“扫描完成”、“有设备被发现”。
- 数据包 (Data Packet):承载上层实际传输的数据,比如音乐流、文件内容。这又分为ACL(异步无连接)数据和SCO(同步面向连接)数据,前者用于普通数据传输,后者用于语音。
一个HCI命令包长什么样?我们来看一个源码中的例子。还记得原始文章里提到的btsnd_hcic_write_cur_iac_lap函数吗?它的作用就是设置查询访问码,是传统蓝牙发现设备的基础。我们深入看一下:
// system/bt/stack/hcic/hcicmds.cc
void btsnd_hcic_write_cur_iac_lap(uint8_t num_cur_iac, LAP* const iac_lap) {
BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
uint8_t* pp = (uint8_t*)(p + 1);
p->len = HCIC_PREAMBLE_SIZE + 1 + (num_cur_iac * LAP_LEN);
p->offset = 0;
UINT16_TO_STREAM(pp, HCI_WRITE_CURRENT_IAC_LAP); // OGF=0x03, OCF=0x003A
UINT8_TO_STREAM(pp, p->len - HCIC_PREAMBLE_SIZE); // 参数总长度
UINT8_TO_STREAM(pp, num_cur_iac); // IAC数量
for (int i = 0; i < num_cur_iac; i++) {
LAP_TO_STREAM(pp, iac_lap[i]); // 写入具体的IAC LAP值
}
btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
}
这段代码就是在“组装”一个HCI命令包。BT_


1739

被折叠的 条评论
为什么被折叠?



