【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设备的管理提供了标准化解决方案。
