蓝牙协议栈源码分析:从Java API到Native层
做蓝牙开发这么多年,我经常被问到同一个问题:「我调用了BluetoothAdapter.enable(),底层到底发生了什么?」
说实话,刚入行那会儿我也挺好奇的。Java层一个简单的API调用,怎么就控制硬件去搜设备、连耳机了?今天我们就从源码角度,把这条链路彻底走一遍。
一、整体架构:三层模型
Android蓝牙协议栈,说白了就是三层结构:
- Java Framework层 — 我们开发者直接调用的API,比如BluetoothAdapter、BluetoothDevice
- Native层 — 用C++实现的BluetoothManagerService、AdapterService等核心服务
- HCI层 — 硬件控制接口,直接和蓝牙芯片打交道
嗯,这里要注意:Java层和Native层之间通过JNI通信,Native层再通过socket或vendor扩展库下发HCI命令。
二、Java API 到 Native 的桥梁
我们以BluetoothAdapter.enable()为例,看看这条调用链。
2.1 Java 端入口
// frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java
public boolean enable() {
// 检查权限
if (isEnabled()) {
return true;
}
try {
return mManagerService.enable();
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
}
}
这里mManagerService是一个Binder代理对象,实际对应的是BluetoothManagerService。
RemoteException,多半是蓝牙服务进程挂了。建议在调用enable()前先检查isEnabled(),避免重复调用导致异常。
2.2 BluetoothManagerService 的处理
// frameworks/base/services/core/java/com/android/server/bluetooth/BluetoothManagerService.java
public boolean enable() {
// 状态检查
if (mState == BluetoothAdapter.STATE_BLE_ON) {
// 从BLE only切换到BR/EDR
return enableBle(bridge);
}
// 发送消息到内部Handler
Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
msg.arg1 = 0; // 静默模式
mHandler.sendMessage(msg);
return true;
}
这里有个细节:蓝牙使能不是同步的。Handler收到消息后,会调用enableNative()这个JNI方法。
2.3 JNI 调用到 Native
// frameworks/base/core/jni/android_bluetooth_common.cpp
static jboolean enableNative(JNIEnv* env, jobject obj) {
// 获取BluetoothService的native实例
bluetooth::hal::BluetoothInterface* btif = getBluetoothInterface();
if (btif == nullptr) return JNI_FALSE;
// 调用HAL层接口
bt_status_t status = btif->enable();
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
看到没?JNI层其实很薄,就是个转发。真正的逻辑在BluetoothInterface这个HAL接口里。
三、HCI命令下发流程
到了Native层,事情就变得有意思了。我当年啃这块代码时,花了整整两周才理清脉络。
3.1 核心数据结构:HCI Command Packet
HCI命令包格式是固定的:
| 字段 | 长度 | 说明 |
|---|---|---|
| OpCode | 2字节 | 操作码,高6位是OGF,低10位是OCF |
| Parameter Total Length | 1字节 | 参数总长度 |
| Parameters | 0-255字节 | 具体参数 |
举个例子,Reset命令的OpCode是0x0C03:
- OGF = 0x03 (Controller & Baseband命令)
- OCF = 0x0003 (Reset)
3.2 命令下发源码追踪
// system/bt/hci/src/hci_layer.cpp
static void transmit_command(command_t* cmd) {
// 1. 序列化命令
uint8_t* packet = (uint8_t*)malloc(cmd->packet_size);
serialize_command(cmd, packet);
// 2. 写入HCI传输层
hci_transmit(packet, cmd->packet_size);
// 3. 等待命令完成事件
wait_for_command_complete(cmd->opcode, cmd->timeout_ms);
free(packet);
}
这里hci_transmit最终会调用到bt_vendor_interface_t中的send_hci_command函数指针。
四、Vendor扩展:芯片厂商的「私房菜」
标准HCI命令只能做通用操作。但每家芯片都有自己的特色功能,比如:
- Broadcom的「快速连接」优化
- Qualcomm的「低功耗扫描」
- MTK的「双模共存」策略
这些功能都通过Vendor Specific HCI Command实现。
4.1 Vendor命令格式
// Vendor命令的OpCode范围:0xFC00 - 0xFFFF
// OGF = 0x3F (Vendor Specific)
// OCF = 0x0000 - 0x03FF
typedef struct {
uint16_t opcode; // 0xFC00 + vendor_id
uint8_t param_len;
uint8_t params[255]; // 厂商自定义格式
} vendor_hci_cmd_t;
4.2 实际项目中的Vendor扩展
我记得在做一个TWS耳机项目时,遇到个坑:耳机连接后频繁断连。抓了HCI log发现,是芯片的「省电模式」参数太激进。
解决方案就是通过Vendor命令调整参数:
// 伪代码:设置Broadcom芯片的省电参数
uint8_t params[] = {0x01, 0x0A, 0x32}; // 模式、超时、间隔
vendor_hci_cmd_t cmd = {
.opcode = 0xFC45, // Broadcom自定义
.param_len = 3,
.params = {0x01, 0x0A, 0x32}
};
hci_send_vendor_cmd(&cmd);
五、完整调用链总结
我们从头捋一遍enable()的完整路径:
- App调用 →
BluetoothAdapter.enable() - System Service →
BluetoothManagerService.enable() - JNI桥接 →
enableNative() - HAL接口 →
BluetoothInterface::enable() - Stack内部 → 构造HCI Reset命令
- HCI层 →
transmit_command()序列化并发送 - Vendor层 → 通过UART/USB写入芯片
- Controller → 执行命令,返回Complete事件
整个过程,从Java到硬件,大概经过7-8层函数调用。我刚开始看源码时,经常在JNI和HAL之间迷路。后来养成个习惯:先抓HCI log,再反推代码路径,效率高很多。
adb shell抓取蓝牙HCI log:adb shell cat /sys/kernel/debug/bluetooth/hci0 > hci_log.bin然后用
hcidump或Wireshark解析,能看到每条命令的收发细节。
好了,这一章我们从Java API一路追到了HCI命令和Vendor扩展。说白了,蓝牙协议栈就是个层层封装、逐级下放的过程。理解了这个脉络,后面分析具体功能(扫描、连接、GATT)就会轻松很多。
公众号:蓝海资料掘金营,微信deep3321