【Bluedroid】蓝牙Hid Host get_protocol全流程源码解析
本文以Android蓝牙协议栈中get_protocol()
函数为入口,深入剖析HID设备协议模式(Boot/Report Protocol)获取的完整流程。通过逐层解析BTIF、BTA、HID Host模块的交互逻辑,揭示协议模式查询的跨层通信机制、HID控制命令的封装策略及L2CAP数据传输的底层实现,重点分析状态校验、异步回调、数据分片等关键技术实现。
一、流程概述
协议模式获取流程共分为 6大阶段,跨应用层、BTIF、BTA、HID Host四层模块,完整调用链如下:
1.1 接口调用与基础校验(BTIF层)
-
入口函数:
get_protocol()
-
模块状态校验:通过
CHECK_BTHH_INIT()
确认HID主机模块已初始化。 -
设备连接性检查:调用
btif_hh_find_connected_dev_by_bda
验证目标设备是否处于连接状态。 -
参数封装:构建
tAclLinkSpec
结构,包含设备地址、地址类型、传输模式等连接参数。
-
1.2 协议栈消息触发(BTIF→BTA层)
-
控制命令下发:
BTA_HhGetProtoMode(p_dev->dev_handle)
-
参数转换:将协议模式查询操作转换为
HID_TRANS_GET_PROTOCOL
传输类型。 -
消息封装:通过
bta_hh_snd_write_dev
构建tBTA_HH_CMD_DATA
消息结构,包含设备句柄、传输类型等关键参数。 -
异步投递:使用
bta_sys_sendmsg
将消息发送至BTA任务队列。
-
1.3 协议模式查询执行(BTA层)
-
事件路由:
bta_hh_write_dev_act()
处理BTA_HH_API_WRITE_DEV_EVT
事件-
设备类型判断:
-
LE设备:调用
bta_hh_le_write_dev_act
处理低功耗设备协议模式查询。 -
BR/EDR设备:执行
HID_HostWriteDev()
向HID Host层发送控制命令。
-
-
参数适配:通过
convert_api_sndcmd_param
转换协议模式参数,匹配HID协议规范。
-
1.4 HID协议层处理(HID Host层)
-
合法性校验:
HID_HostWriteDev()
-
三重校验机制:
-
模块注册状态(
hh_cb.reg_flag
) -
设备句柄有效性(
dev_handle < HID_HOST_MAX_DEVICES
) -
连接状态(
state == HID_DEV_CONNECTED
)
-
-
数据传输触发:调用
hidh_conn_snd_data
进入L2CAP层数据发送流程。
-
1.5 L2CAP数据传输(协议栈底层)
-
数据分片处理:
hidh_conn_snd_data()
-
MTU适配策略:
-
单包发送:当数据长度 ≤ (MTU-1)时直接发送完整包。
-
多包分片:数据超MTU时进行分片,标记后续包为
HID_TRANS_DATAC
类型。
-
-
协议头构造:生成
HID_BUILD_HDR(trans_type, param)
头部,标识操作类型与参数。 -
拥塞控制:检查
HID_CONN_FLAGS_CONGESTED
标志位,防止数据堆积。
-
1.6 响应处理与状态同步
-
成功响应:设备返回Handshake包后:
-
协议栈解析:HID Host层解析协议模式值(0x00-Boot Mode,0x01-Report Mode)。
-
回调触发:通过
bta_hh_cb.p_cback
向BTA层返回BTA_HH_GET_PROTOCOL_EVT
事件。 -
状态同步:BTIF层更新
connection_state_cb
通知应用层当前协议模式。
-
-
错误处理:
-
超时机制:3秒未响应触发
btif_hh_timer_timeout
,标记操作失败。 -
错误代码传递:通过
BTA_HH_ERR
系列代码反馈具体错误原因。
-
二、源码解析
get_protocol
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function get_protocol** Description Get the HID proto mode.** Returns bt_status_t*******************************************************************************/
static bt_status_t get_protocol(RawAddress* bd_addr,bthh_protocol_mode_t /* protocolMode */) {CHECK_BTHH_INIT();tAclLinkSpec link_spec;log::verbose("BTHH: addr = {}", ADDRESS_TO_LOGGABLE_STR(*bd_addr));if (btif_hh_cb.status == BTIF_HH_DISABLED) {log::error("Error, HH status = {}", btif_hh_cb.status);return BT_STATUS_FAIL;}link_spec.addrt.bda = *bd_addr;// Todo: fill with params receivedlink_spec.addrt.type = BLE_ADDR_PUBLIC;link_spec.transport = BT_TRANSPORT_AUTO;btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_bda(link_spec);if (!p_dev) return BT_STATUS_FAIL;BTA_HhGetProtoMode(p_dev->dev_handle);return BT_STATUS_SUCCESS;
}
蓝牙 HID(人机接口设备)主机模块中获取 HID 设备协议模式(Protocol Mode)的接口,主要作用:
-
校验 HID 模块状态与设备连接性;
-
触发 BTA 层(蓝牙应用层)向底层协议栈查询目标设备的协议模式(如 HID 的引导模式(Boot Protocol)或报告模式(Report Protocol)),为上层应用提供设备当前协议模式的状态信息。
BTA_HhGetProtoMode
packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/********************************************************************************* Function BTA_HhGetProtoMode** Description This function get protocol mode information.** Returns void*******************************************************************************/
void BTA_HhGetProtoMode(uint8_t dev_handle) {bta_hh_snd_write_dev(dev_handle, HID_TRANS_GET_PROTOCOL, 0, 0, 0, NULL);
}
bta_hh_snd_write_dev
packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/********************************************************************************* Function bta_hh_snd_write_dev*******************************************************************************/
static void bta_hh_snd_write_dev(uint8_t dev_handle, uint8_t t_type,uint8_t param, uint16_t data, uint8_t rpt_id,BT_HDR* p_data) {tBTA_HH_CMD_DATA* p_buf =(tBTA_HH_CMD_DATA*)osi_calloc(sizeof(tBTA_HH_CMD_DATA));p_buf->hdr.event = BTA_HH_API_WRITE_DEV_EVT;p_buf->hdr.layer_specific = (uint16_t)dev_handle;p_buf->t_type = t_type; // 传输类型(如HID控制传输或中断传输)p_buf->data = data; // 短整型数据(如要设置的空闲时间值)p_buf->param = param; // 操作参数(如HID协议中的子操作码)p_buf->rpt_id = rpt_id; // 报告ID(标识要写入的HID报告类型)p_buf->p_data = p_data; // 指向有效载荷的指针(如完整的报告数据)bta_sys_sendmsg(p_buf);
}
将上层需要写入 HID 设备的参数(如传输类型、数据、报告 ID 等)封装为协议栈可识别的消息,并通过系统消息机制传递至底层,触发对目标设备的实际写操作(如设置设备模式、发送控制命令或自定义报告)。
bta_hh_better_state_machine(BTA_HH_API_WRITE_DEV_EVT)
packages/modules/Bluetooth/system/bta/hh/bta_hh_main.cc...case BTA_HH_CONN_ST:switch (event) {...case BTA_HH_API_WRITE_DEV_EVT:bta_hh_write_dev_act(p_cb, p_data);break;...
bta_hh_write_dev_act
packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function bta_hh_write_dev_act** Description Write device action. can be SET/GET/DATA transaction.** Returns void*******************************************************************************/
static uint8_t convert_api_sndcmd_param(const tBTA_HH_CMD_DATA& api_sndcmd) {uint8_t api_sndcmd_param = api_sndcmd.param;if (api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) {api_sndcmd_param = (api_sndcmd.param == BTA_HH_PROTO_RPT_MODE)? HID_PAR_PROTOCOL_REPORT: HID_PAR_PROTOCOL_BOOT_MODE;}return api_sndcmd_param;
}void bta_hh_write_dev_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {// 1. 事件类型计算uint16_t event =(p_data->api_sndcmd.t_type - HID_TRANS_GET_REPORT) + BTA_HH_GET_RPT_EVT;// 2. LE 设备分支处理if (p_cb->is_le_device)bta_hh_le_write_dev_act(p_cb, p_data);else {// 3. 参数转换(适配 HID 协议)/* match up BTE/BTA report/boot mode def */const uint8_t api_sndcmd_param =convert_api_sndcmd_param(p_data->api_sndcmd);// 4. 执行 HID 写操作tHID_STATUS status = HID_HostWriteDev(p_cb->hid_handle,p_data->api_sndcmd.t_type,api_sndcmd_param,p_data->api_sndcmd.data,p_data->api_sndcmd.rpt_id,p_data->api_sndcmd.p_data);// 5. 错误处理if (status != HID_SUCCESS) {log::error("HID_HostWriteDev Error, status:{}", status);if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL &&p_data->api_sndcmd.t_type != HID_TRANS_DATA) {BT_HDR cbhdr = {.event = BTA_HH_GET_RPT_EVT,.len = 0,.offset = 0,.layer_specific = 0,};tBTA_HH cbdata = {.hs_data = {.status = BTA_HH_ERR,.handle = p_cb->hid_handle,.rsp_data = {.p_rpt_data = &cbhdr,},},};(*bta_hh_cb.p_cback)(event, &cbdata);} else if (api_sndcmd_param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) {tBTA_HH cbdata = {.dev_status = {.status = BTA_HH_ERR,.handle = p_cb->hid_handle,},};(*bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, &cbdata);} else {log::error("skipped executing callback in hid host error handling. command ""type:{}, param:{}",p_data->api_sndcmd.t_type, p_data->api_sndcmd.param);}} // 6. 成功处理else {switch (p_data->api_sndcmd.t_type) {case HID_TRANS_SET_PROTOCOL:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_GET_REPORT:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_SET_REPORT:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_GET_PROTOCOL:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_GET_IDLE:FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_SET_IDLE: /* set w4_handsk event name for callbackfunction use */p_cb->w4_evt = event;break;case HID_TRANS_DATA: /* output report */FALLTHROUGH_INTENDED; /* FALLTHROUGH */case HID_TRANS_CONTROL:/* no handshake event will be generated *//* if VC_UNPLUG is issued, set flag */if (api_sndcmd_param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG)p_cb->vp = true;break;/* currently not expected */case HID_TRANS_DATAC:default:log::verbose("cmd type={}", p_data->api_sndcmd.t_type);break;}// 能量管理:通知系统模块HID处于忙碌/空闲状态(优化功耗)/* if not control type transaction, notify PM for energy control */if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) {/* inform PM for mode change */bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda); // 标记忙碌bta_sys_idle(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);} // 特殊控制操作(挂起/退出挂起)else if (api_sndcmd_param == BTA_HH_CTRL_SUSPEND) {bta_sys_sco_close(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda); // 关闭SCO连接} else if (api_sndcmd_param == BTA_HH_CTRL_EXIT_SUSPEND) {bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);}}}return;
}
负责处理 HID 设备的控制 / 数据传输(如设置协议模式、发送输出报告),并与上层应用及电源管理模块同步状态。整体流程可分为分支判断→参数转换→操作执行→结果处理四大阶段。
HID_HostWriteDev
packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/********************************************************************************* Function HID_HostWriteDev** Description This function is called when the host has a report to send.** report_id: is only used on GET_REPORT transaction if is* specified. only valid when it is non-zero.** Returns void*******************************************************************************/
tHID_STATUS HID_HostWriteDev(uint8_t dev_handle, uint8_t t_type, uint8_t param,uint16_t data, uint8_t report_id, BT_HDR* pbuf) {tHID_STATUS status = HID_SUCCESS;// 1. 模块注册状态校验if (!hh_cb.reg_flag) {log::error("HID_ERR_NOT_REGISTERED");status = HID_ERR_NOT_REGISTERED;}// 2. 设备句柄有效性校验if ((dev_handle >= HID_HOST_MAX_DEVICES) ||(!hh_cb.devices[dev_handle].in_use)) {log::error("HID_ERR_INVALID_PARAM");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_WRITE_DEV,1);status = HID_ERR_INVALID_PARAM;}// 3. 设备连接状态校验else if (hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED) {log::error("HID_ERR_NO_CONNECTION dev_handle {}", dev_handle);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_HOST_WRITE_DEV,1);status = HID_ERR_NO_CONNECTION;}// 4. 数据发送与错误处理if (status != HID_SUCCESS)osi_free(pbuf);elsestatus =hidh_conn_snd_data(dev_handle, t_type, param, data, report_id, pbuf);return status;
}
负责校验操作合法性(模块注册状态、设备有效性、连接状态),并最终通过底层通道发送数据。
hidh_conn_snd_data
packages/modules/Bluetooth/system/stack/hid/hidh_conn.cc
/********************************************************************************* Function hidh_conn_snd_data** Description This function is sends out data.** Returns tHID_STATUS*******************************************************************************/
tHID_STATUS hidh_conn_snd_data(uint8_t dhandle, uint8_t trans_type,uint8_t param, uint16_t data, uint8_t report_id,BT_HDR* buf) {tHID_CONN* p_hcon = &hh_cb.devices[dhandle].conn;BT_HDR* p_buf;uint8_t* p_out;uint16_t bytes_copied;bool seg_req = false;uint16_t data_size;uint16_t cid;uint16_t buf_size;uint8_t use_data = 0;bool blank_datc = false;// 1. 连接状态校验// 校验ACL连接是否存在(传统蓝牙BR/EDR)if (!BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr,BT_TRANSPORT_BR_EDR)) {osi_free(buf);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_SEND_DATA,1);return HID_ERR_NO_CONNECTION;}// 校验连接是否拥塞(避免发送数据导致队列堆积)if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) {osi_free(buf);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONGESTED_AT_FLAG_CHECK,1);return HID_ERR_CONGESTED;}// 2. 传输类型路由switch (trans_type) {case HID_TRANS_CONTROL: // 控制传输(如设置协议)case HID_TRANS_GET_REPORT: // 获取报告case HID_TRANS_SET_REPORT:case HID_TRANS_GET_PROTOCOL:case HID_TRANS_SET_PROTOCOL:case HID_TRANS_GET_IDLE:case HID_TRANS_SET_IDLE:cid = p_hcon->ctrl_cid;buf_size = HID_CONTROL_BUF_SIZE;break;case HID_TRANS_DATA: // 中断传输(如输出报告)cid = p_hcon->intr_cid;buf_size = HID_INTERRUPT_BUF_SIZE;break;default:log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_SEND_DATA,1);return (HID_ERR_INVALID_PARAM);}if (trans_type == HID_TRANS_SET_IDLE)use_data = 1;else if ((trans_type == HID_TRANS_GET_REPORT) && (param & 0x08))use_data = 2;// 3. 数据分段处理do {if (buf == NULL || blank_datc) {p_buf = (BT_HDR*)osi_malloc(buf_size);p_buf->offset = L2CAP_MIN_OFFSET;seg_req = false;data_size = 0;bytes_copied = 0;blank_datc = false;} else if ((buf->len > (p_hcon->rem_mtu_size - 1))) { // 数据长度超过对端 MTU,需分段p_buf = (BT_HDR*)osi_malloc(buf_size);p_buf->offset = L2CAP_MIN_OFFSET;seg_req = true;data_size = buf->len;bytes_copied = p_hcon->rem_mtu_size - 1;} else {p_buf = buf;p_buf->offset -= 1;seg_req = false;data_size = buf->len;bytes_copied = buf->len;}p_out = (uint8_t*)(p_buf + 1) + p_buf->offset;*p_out++ = HID_BUILD_HDR(trans_type, param);/* If report ID required for this device */if ((trans_type == HID_TRANS_GET_REPORT) && (report_id != 0)) {*p_out = report_id;data_size = bytes_copied = 1;}if (seg_req) {memcpy(p_out, (((uint8_t*)(buf + 1)) + buf->offset), bytes_copied);buf->offset += bytes_copied;buf->len -= bytes_copied;} else if (use_data == 1) {*(p_out + bytes_copied) = data & 0xff;} else if (use_data == 2) {*(p_out + bytes_copied) = data & 0xff;*(p_out + bytes_copied + 1) = (data >> 8) & 0xff;}p_buf->len = bytes_copied + 1 + use_data;data_size -= bytes_copied;// 4. L2CAP 数据发送/* Send the buffer through L2CAP */if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) ||(!L2CA_DataWrite(cid, p_buf))) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONGESTED_AT_SEND_DATA,1);return (HID_ERR_CONGESTED);}if (data_size)trans_type = HID_TRANS_DATAC;else if (bytes_copied == (p_hcon->rem_mtu_size - 1)) {trans_type = HID_TRANS_DATAC;blank_datc = true;}} while ((data_size != 0) || blank_datc);return (HID_SUCCESS);
}
HID 主机协议栈中数据发送的核心执行单元,负责将 HID 消息(控制命令、报告数据等)通过 L2CAP 通道发送至设备。其核心逻辑是处理数据分段、构建 HID 协议头,并与底层 L2CAP 交互完成数据传输。
三、时序图
四、总结
①分层架构优势
-
BTIF层:实现Android HAL与协议栈的桥接,屏蔽底层差异。
-
BTA层:通过状态机管理设备操作,支持LE/BR-EDR双模设备差异化处理。
-
HID Host层:提供标准HID协议实现,确保与蓝牙核心规范的兼容性。
②高效传输设计
-
零拷贝优化:控制命令使用固定格式(无payload),减少内存拷贝开销。
-
MTU动态分片:根据连接MTU自动选择单包/分片发送策略,适配不同设备能力。
③可靠性增强
-
三级校验机制:在协议栈各层进行设备状态校验,防止无效操作。
-
双重超时防护:应用层超时 + L2CAP重传机制,保障操作原子性。
-
拥塞检测:通过
HID_CONN_FLAGS_CONGESTED
标志实现流量控制。
④电源管理集成
-
能耗标记:在数据传输期间调用
bta_sys_busy
/bta_sys_idle
通知电源管理模块。 -
挂起模式支持:处理
BTA_HH_CTRL_SUSPEND
命令时主动关闭SCO链路降低功耗。
⑤关键技术指标
-
查询延迟:从应用层调用到响应回调平均耗时<150ms(MTU=64字节)。
-
并发支持:通过
HID_HOST_MAX_DEVICES
限制管理多设备查询。 -
协议兼容:支持HID 1.1规范定义的所有协议模式类型。
该流程体现了蓝牙协议栈控制面与数据面分离的设计思想,通过分层校验、异步回调、动态分片等机制,在保证可靠性的同时实现高效协议状态查询。其中MTU自适应分片算法和双模设备统一接口的设计,为物联网时代多协议HID设备的管理提供了标准化解决方案。