当前位置: 首页 > web >正文

【Bluedroid】蓝牙 HID HOST连接全流程源码解析

蓝牙 HID(Human Interface Device,人机接口设备)是智能设备与外设(如键盘、鼠标、游戏手柄)交互的核心协议。本文围绕Android蓝牙 HID 主机模块的连接流程,从上层应用发起连接请求开始,逐层解析协议栈内部的状态检查、设备管理、SDP 服务发现、L2CAP 通道建立等关键步骤,揭示 HID 设备从逻辑注册到物理连接的全链路实现。重点分析各模块的协作机制(如状态机驱动、异步事件处理)、关键函数的设计逻辑(如状态验证、资源管理),并总结其设计思想(如防御式编程、可观测性),为蓝牙 HID 开发与调试提供技术参考。

一、流程概述

蓝牙 HID 设备连接流程可分为上层请求触发→状态与资源检查→服务发现(SDP)→物理链路建立四大阶段,各阶段通过状态机和异步事件驱动,确保连接的安全性、可靠性和有序性。以下是关键步骤的详细拆解:

阶段 1:上层请求触发与初步检查

用户通过上层 API(如 connect)发起 HID 设备连接请求,触发以下操作:

  1. 状态预检查:检查 HID 模块全局状态(如是否处于禁用或连接中),避免非法状态下的操作。

  2. 重复连接检测:通过设备地址查找已连接或连接中的设备,防止重复请求(返回 BT_STATUS_DONEBT_STATUS_BUSY)。

  3. 异步上下文传递:通过 btif_transfer_context 将连接请求异步发送至 BTIF 线程(btif_hh_handle_evt),避免阻塞主线程。

阶段 2:设备管理与状态机驱动

BTIF 线程接收请求后,调用 btif_hh_connect 执行设备管理与状态机触发:

  1. 设备容量检查:验证当前已连接设备数是否超过最大限制(BTIF_HH_MAX_HID),避免资源耗尽(返回 BT_STATUS_NOMEM)。

  2. 设备添加状态验证:检查设备是否已被添加到管理列表(btif_hh_cb.added_devices),处理添加失败的异常情况。

  3. 状态机事件触发:更新全局状态为 BTIF_HH_DEV_CONNECTING,调用底层 API BTA_HhOpen 构建并发送连接事件消息(BTA_HH_API_OPEN_EVT)至 HID 状态机(bta_hh_hdl_event)。

阶段 3:服务发现(SDP)与设备能力获取

状态机接收事件后,通过 bta_hh_sm_execute 驱动状态转换(如从 BTA_HH_IDLE_STBTA_HH_W4_CONN_ST),并触发 bta_hh_connect 执行服务发现:

  1. 传输类型决策:根据设备支持的传输类型(BR/EDR 或 BLE)、已连接的 ACL 链路状态(BTM_IsAclConnectionUp)及服务 UUID(如传统 HID 的 UUID_SERVCLASS_HUMAN_INTERFACE 或低功耗 HID 的 UUID_SERVCLASS_LE_HID),选择最优传输路径(bta_hh_le_open_connbta_hh_bredr_conn)。

  2. SDP 服务发现(新设备场景)

    1. 通过 bta_hh_start_sdp 初始化 SDP 数据库(tSDP_DISCOVERY_DB),调用 SDP_DiDiscover 发起 DI(设备标识)搜索,获取设备 PNP 信息(如厂商 ID、产品 ID)。

    2. SDP 完成后通过回调(bta_hh_di_sdp_cback)触发状态机事件(BTA_HH_SDP_CMPL_EVT),进入连接决策阶段。

阶段 4:物理链路建立与连接确认

SDP 完成后,根据结果执行 HID 连接:

  1. 已识别设备处理(存在 app_id):

    1. 调用 HID_HostAddDev 将设备添加至 HID 主机管理列表(hh_cb.devices),分配唯一设备句柄(dev_handle)。

    2. 通过 bta_hh_add_device_to_list 存储设备属性(如 attr_mask、报告描述符),触发状态机事件(BTA_HH_SDP_CMPL_EVT)。

  2. 连接请求执行

    1. 调用 HID_HostOpenDev 验证设备句柄有效性及状态(仅允许 HID_DEV_NO_CONN 状态),初始化连接尝试次数(conn_tries)。

    2. 调用 hidh_conn_initiate 通过 L2CAP 层发起控制通道连接(L2CA_ConnectReq2),设置安全要求(认证 + 加密),并更新连接状态(如 HID_CONN_STATE_CONNECTING_CTRL)。

  3. 连接结果反馈:

    1. L2CAP 连接成功后,等待控制通道确认,后续触发中断通道建立;

    2. 连接失败时通过回调(hh_cb.callback)通知上层(如 HID_HDEV_EVT_CLOSE),并记录错误指标(log_counter_metrics)。

二、源码解析

connect

packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function        connect** Description     connect to hid device** Returns         bt_status_t*******************************************************************************/
static bt_status_t connect(RawAddress* bd_addr) {btif_hh_device_t* p_dev;tAclLinkSpec link_spec;// 1. 状态检查if (btif_hh_cb.status == BTIF_HH_DEV_CONNECTING) {log::warn("Error, HH status = {}", btif_hh_cb.status);return BT_STATUS_BUSY;} else if (btif_hh_cb.status == BTIF_HH_DISABLED ||btif_hh_cb.status == BTIF_HH_DISABLING) {log::warn("Error, HH status = {}", btif_hh_cb.status);return BT_STATUS_NOT_READY;}// 2. 设备地址处理link_spec.addrt.bda = *bd_addr;// Todo: fill with params receivedlink_spec.addrt.type = BLE_ADDR_PUBLIC;link_spec.transport = BT_TRANSPORT_AUTO;// 3 .重复连接检测p_dev = btif_hh_find_connected_dev_by_bda(link_spec);if (p_dev) {if (p_dev->dev_status == BTHH_CONN_STATE_CONNECTED ||p_dev->dev_status == BTHH_CONN_STATE_CONNECTING) {log::error("Error, device {} already connected.",ADDRESS_TO_LOGGABLE_CSTR(*bd_addr));return BT_STATUS_DONE;} else if (p_dev->dev_status == BTHH_CONN_STATE_DISCONNECTING) {log::error("Error, device {} is busy with (dis)connecting.",ADDRESS_TO_LOGGABLE_CSTR(*bd_addr));return BT_STATUS_BUSY;}}// 4. 异步请求处理return btif_transfer_context(btif_hh_handle_evt, BTIF_HH_CONNECT_REQ_EVT,(char*)&link_spec, sizeof(tAclLinkSpec), NULL);
}

发起与 HID 设备的连接请求。实现了连接前的状态检查、设备重复连接检测等关键逻辑,并通过上下文传递机制将连接请求异步发送到处理线程。

btif_hh_handle_evt(BTIF_HH_CONNECT_REQ_EVT)

packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function         btif_hh_handle_evt** Description      Switches context for immediate callback** Returns          void*******************************************************************************/static void btif_hh_handle_evt(uint16_t event, char* p_param) {CHECK(p_param != nullptr);tAclLinkSpec* p_link_spec = (tAclLinkSpec*)p_param;switch (event) {case BTIF_HH_CONNECT_REQ_EVT: {log::debug("Connect request received remote:{}",ADDRESS_TO_LOGGABLE_CSTR((*p_link_spec)));if (btif_hh_connect(p_link_spec) == BT_STATUS_SUCCESS) { // 执行实际的连接操作HAL_CBACK(bt_hh_callbacks, connection_state_cb, &p_link_spec->addrt.bda,BTHH_CONN_STATE_CONNECTING);} elseHAL_CBACK(bt_hh_callbacks, connection_state_cb, &p_link_spec->addrt.bda,BTHH_CONN_STATE_DISCONNECTED);} break;...    

接收和分发来自上层的异步请求。通过事件类型路由机制,将不同类型的请求分发给对应的处理函数,并通过回调机制向上层报告处理结果。

btif_hh_connect

packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function         btif_hh_connect** Description      connection initiated from the BTIF thread context** Returns          int status*******************************************************************************/bt_status_t btif_hh_connect(const tAclLinkSpec* link_spec) {btif_hh_added_device_t* added_dev = NULL;CHECK_BTHH_INIT();log::verbose("BTHH");// 1. 设备容量检查btif_hh_device_t* dev = btif_hh_find_dev_by_bda(*link_spec);if (!dev && btif_hh_cb.device_num >= BTIF_HH_MAX_HID) {// No space for more HID device now.log::warn("Error, exceeded the maximum supported HID device number {}",BTIF_HH_MAX_HID);return BT_STATUS_NOMEM;}// 2. 设备添加状态检查for (int i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) {if (btif_hh_cb.added_devices[i].link_spec.addrt.bda ==link_spec->addrt.bda) {added_dev = &btif_hh_cb.added_devices[i];log::warn("Device {} already added, attr_mask = 0x{:x}",ADDRESS_TO_LOGGABLE_STR(*link_spec), added_dev->attr_mask);}}if (added_dev != NULL) {if (added_dev->dev_handle == BTA_HH_INVALID_HANDLE) {// No space for more HID device now.log::error("Error, device {} added but addition failed",ADDRESS_TO_LOGGABLE_STR(*link_spec));added_dev->link_spec = {};added_dev->dev_handle = BTA_HH_INVALID_HANDLE;return BT_STATUS_NOMEM;}}// 3. 连接状态检查if (dev && dev->dev_status == BTHH_CONN_STATE_CONNECTED) {log::debug("HidHost profile already connected for {}",ADDRESS_TO_LOGGABLE_CSTR((*link_spec)));return BT_STATUS_SUCCESS;}/* Not checking the NORMALLY_Connectible flags from sdp record, and anywayssending thisrequest from host, for subsequent user initiated connection. If the remote isnot inpagescan mode, we will do 2 retries to connect before giving up */// 4. 更新全局状态btif_hh_cb.status = BTIF_HH_DEV_CONNECTING;btif_hh_cb.pending_link_spec = *link_spec;// 5. 调用底层 APIBTA_HhOpen(btif_hh_cb.pending_link_spec);// 6. 通知上层do_in_jni_thread(base::Bind([](RawAddress bd_addr) {HAL_CBACK(bt_hh_callbacks, connection_state_cb, &bd_addr,BTHH_CONN_STATE_CONNECTING);},link_spec->addrt.bda));return BT_STATUS_SUCCESS;
}

负责从 BTIF 线程上下文发起与 HID 设备的连接请求。实现了设备管理、连接状态检查和底层 API 调用等关键逻辑,确保连接请求的合法性和有序性。

BTA_HhOpen

packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/********************************************************************************* Function         BTA_HhOpen** Description      Connect to a device of specified BD address in specified*                  protocol mode and security level.** Returns          void*******************************************************************************/
void BTA_HhOpen(const tAclLinkSpec& link_spec) {// 内存分配与消息初始化tBTA_HH_API_CONN* p_buf =(tBTA_HH_API_CONN*)osi_calloc(sizeof(tBTA_HH_API_CONN));tBTA_HH_PROTO_MODE mode = BTA_HH_PROTO_RPT_MODE;// 设置消息头p_buf->hdr.event = BTA_HH_API_OPEN_EVT;p_buf->hdr.layer_specific = BTA_HH_INVALID_HANDLE;// 配置连接模式p_buf->mode = mode;p_buf->link_spec = link_spec;// 发送消息到状态机bta_sys_sendmsg((void*)p_buf);
}

负责构建并发送连接请求消息到 HID 状态机。将上层传入的连接参数封装为事件消息,通过系统消息队列异步处理,实现与目标 HID 设备的连接。

bta_hh_hdl_event(BTA_HH_API_OPEN_EVT)

/********************************************************************************* Function         bta_hh_hdl_event** Description      HID host main event handling function.*** Returns          void*******************************************************************************/
bool bta_hh_hdl_event(const BT_HDR_RIGID* p_msg) {uint8_t index = BTA_HH_IDX_INVALID;tBTA_HH_DEV_CB* p_cb = NULL;/* all events processed in state machine need to find correspondingCB before proceed */// 事件类型识别if (p_msg->event == BTA_HH_API_OPEN_EVT) { // 对于连接请求事件,通过设备地址查找index = bta_hh_find_cb(((tBTA_HH_API_CONN*)p_msg)->link_spec);} else if (p_msg->event == BTA_HH_API_MAINT_DEV_EVT) { // 对于设备管理事件,根据子事件类型选择不同的查找方式/* if add device */if (((tBTA_HH_MAINT_DEV*)p_msg)->sub_event == BTA_HH_ADD_DEV_EVT) {index = bta_hh_find_cb(((tBTA_HH_MAINT_DEV*)p_msg)->link_spec);} else /* else remove device by handle */ {index = bta_hh_dev_handle_to_cb_idx((uint8_t)p_msg->layer_specific);/* If BT disable is done while the HID device is connected and* Link_Key uses unauthenticated combination* then we can get into a situation where remove_bonding is called* with the index set to 0 (without getting* cleaned up). Only when VIRTUAL_UNPLUG is called do we cleanup the* index and make it MAX_KNOWN.* So if REMOVE_DEVICE is called and in_use is false then we should* treat this as a NULL p_cb. Hence we* force the index to be IDX_INVALID*/if ((index != BTA_HH_IDX_INVALID) && (!bta_hh_cb.kdev[index].in_use)) {index = BTA_HH_IDX_INVALID;}}} else if (p_msg->event == BTA_HH_INT_OPEN_EVT) { // 对于内部事件,通过连接参数查找index = bta_hh_find_cb(((tBTA_HH_CBACK_DATA*)p_msg)->link_spec);} else { // 对于其他事件,通过设备句柄查找index = bta_hh_dev_handle_to_cb_idx((uint8_t)p_msg->layer_specific);}if (index != BTA_HH_IDX_INVALID) p_cb = &bta_hh_cb.kdev[index];log::verbose("handle={} dev_cb[{}]", p_msg->layer_specific, index);// 状态机执行bta_hh_sm_execute(p_cb, p_msg->event, (tBTA_HH_DATA*)p_msg);return (true);
}

蓝牙 HID 主机模块的核心事件处理函数,负责接收并分发各类 HID 相关事件。通过事件类型路由机制,将不同来源的事件分发给对应的设备控制块 (Control Block),并通过状态机执行相应的状态转换。

bta_hh_sm_execute

packages/modules/Bluetooth/system/bta/hh/bta_hh_main.cc
/********************************************************************************* Function         bta_hh_sm_execute** Description      State machine event handling function for HID Host*** Returns          void*******************************************************************************/
void bta_hh_sm_execute(tBTA_HH_DEV_CB* p_cb, uint16_t event,const tBTA_HH_DATA* p_data) {tBTA_HH cback_data;tBTA_HH_EVT cback_event = 0;tBTA_HH_STATE in_state;tBTA_HH_INT_EVT debug_event = static_cast<tBTA_HH_INT_EVT>(event);memset(&cback_data, 0, sizeof(tBTA_HH));// 1. 参数校验与异常处理/* handle exception, no valid control block was found */if (!p_cb) {  // 处理无有效控制块的情况log::verbose("Event:{}, bta_hh_cb.p_cback:{}", bta_hh_evt_code(debug_event),fmt::ptr(bta_hh_cb.p_cback));/* BTA HH enabled already? otherwise ignore the event although it's bad*/if (bta_hh_cb.p_cback != NULL) {switch (event) {/* no control block available for new connection */case BTA_HH_API_OPEN_EVT:cback_event = BTA_HH_OPEN_EVT;/* build cback data */cback_data.conn.link_spec = ((tBTA_HH_API_CONN*)p_data)->link_spec;cback_data.conn.status = BTA_HH_ERR_DB_FULL;cback_data.conn.handle = BTA_HH_INVALID_HANDLE;break;/* DB full, BTA_HhAddDev */...default:/* invalid handle, call bad API event */log::error("wrong device handle:{}", p_data->hdr.layer_specific);/* Free the callback buffer now */if (p_data != NULL)osi_free_and_reset((void**)&p_data->hid_cback.p_data);break;}if (cback_event) (*bta_hh_cb.p_cback)(cback_event, &cback_data);}}/* corresponding CB is found, go to state machine */else { // 处理正常情况in_state = p_cb->state;log::verbose("State 0x{:02x} [{}], Event [{}]", in_state,bta_hh_state_code(in_state), bta_hh_evt_code(debug_event));// 2. 无效状态检查if ((p_cb->state == BTA_HH_NULL_ST) || (p_cb->state >= BTA_HH_INVALID_ST)) {log::error("Invalid state State=0x{:x}, Event={}", p_cb->state, event);return;}// 3. 状态机执行bta_hh_better_state_machine(p_cb, event, p_data);if (in_state != p_cb->state) {log::debug("HHID State Change: [{}] -> [{}] after Event [{}]",bta_hh_state_code(in_state), bta_hh_state_code(p_cb->state),bta_hh_evt_code(debug_event));}}
}

蓝牙 HID 主机模块的核心状态机执行函数,负责处理 HID 设备的各种事件并驱动状态转换。实现了状态验证、事件分发和回调通知等关键逻辑,确保 HID 设备状态的一致性和可预测性。

bta_hh_better_state_machine(BTA_HH_API_OPEN_EVT)

packages/modules/Bluetooth/system/bta/hh/bta_hh_main.cc
static void bta_hh_better_state_machine(tBTA_HH_DEV_CB* p_cb, uint16_t event,const tBTA_HH_DATA* p_data) {log::verbose("state:{}, event:{}", bta_hh_state_code(p_cb->state),bta_hh_evt_code(static_cast<tBTA_HH_INT_EVT>(event)));switch (p_cb->state) {case BTA_HH_IDLE_ST:switch (event) {case BTA_HH_API_OPEN_EVT:p_cb->state = BTA_HH_W4_CONN_ST;bta_hh_connect(p_cb, p_data);break;...    

bta_hh_connect

packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function         bta_hh_connect** Description      Start HID host connection.** Returns          void*******************************************************************************/
void bta_hh_connect(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {bool hid_available = false;bool hogp_available = false;bluetooth::Uuid remote_uuids[BT_MAX_NUM_UUIDS] = {};bt_property_t remote_properties = {BT_PROPERTY_UUIDS, sizeof(remote_uuids),&remote_uuids};const RawAddress& bd_addr = p_data->api_conn.link_spec.addrt.bda;// Find the device typetBT_DEVICE_TYPE dev_type;tBLE_ADDR_TYPE addr_type;// 1. 设备信息查询BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type);// 2. 连接状态检查// Find which transports are already connectedbool bredr = BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_BR_EDR);bool le_acl = BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_LE);// 3. 服务 UUID 解析// Find which services known to be availableif (btif_storage_get_remote_device_property(&bd_addr,&remote_properties) == BT_STATUS_SUCCESS) {// 解析UUID列表,查找HID和HOGP服务int count = remote_properties.len / sizeof(remote_uuids[0]);for (int i = 0; i < count; i++) {if (remote_uuids[i].Is16Bit()) {if (remote_uuids[i].As16Bit() == UUID_SERVCLASS_HUMAN_INTERFACE) { //传统 HIDhid_available = true;} else if (remote_uuids[i].As16Bit() == UUID_SERVCLASS_LE_HID) { // 低功耗 HIDhogp_available = true;}}if (hid_available && hogp_available) {break;}}}// 4. 传输类型决策/* Decide whether to connect HID or HOGP */if (bredr && hid_available) {p_cb->is_le_device = false;} else if (le_acl && hogp_available) {p_cb->is_le_device = true;} else if (hid_available) {p_cb->is_le_device = false;} else if (hogp_available) {p_cb->is_le_device = true;} else if (bredr) {p_cb->is_le_device = false;} else if (le_acl || dev_type == BT_DEVICE_TYPE_BLE) {p_cb->is_le_device = true;} else {p_cb->is_le_device = false;}log::debug("bd_addr:{}, bredr:{}, hid_available:{}, le_acl:{}, hogp_available:{}, ""dev_type:{}, is_le_device:{}",ADDRESS_TO_LOGGABLE_CSTR(bd_addr), bredr, hid_available, le_acl,hogp_available, dev_type, p_cb->is_le_device);p_cb->mode = p_data->api_conn.mode;bta_hh_cb.p_cur = p_cb;// 5. 连接初始化// Initiate HID host connectionif (p_cb->is_le_device) {bta_hh_le_open_conn(p_cb, p_data->api_conn.link_spec);} else {bta_hh_bredr_conn(p_cb, p_data);}
}

根据设备支持的传输类型 (BR/EDR 或 BLE)、服务 UUID 以及当前连接状态,智能选择最优的连接路径,确保与 HID 设备建立稳定连接。

bta_hh_bredr_conn

packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function         bta_hh_bredr_conn** Description      Initiate BR/EDR HID connection. This may be triggered by*                  the local application or as a result of remote initiated*                  HID connection.** Returns          void*******************************************************************************/
static void bta_hh_bredr_conn(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {bta_hh_cb.p_cur = p_cb;// 1. 设备状态检查/* If previously virtually cabled device */if (p_cb->app_id) { // 已识别设备处理流程tBTA_HH_DATA bta_hh_data;bta_hh_data.status = BTA_HH_OK;log::verbose("skip SDP for known devices");// 2. 已识别设备处理if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) {uint8_t hdl;      if (HID_HostAddDev(p_cb->link_spec.addrt.bda, p_cb->attr_mask, &hdl) ==HID_SUCCESS) { // 更新设备控制块和索引映射/* update device CB with newly register device handle */bta_hh_add_device_to_list(p_cb, hdl, p_cb->attr_mask, NULL,p_cb->sub_class,p_cb->dscp_info.ssr_max_latency,p_cb->dscp_info.ssr_min_tout, p_cb->app_id);/* update cb_index[] map */bta_hh_cb.cb_index[hdl] = p_cb->index;} else { // 处理添加设备失败bta_hh_data.status = BTA_HH_ERR_NO_RES;}}// 3. 触发状态转换bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, &bta_hh_data); // 发送 SDP 完成事件到状态机}else { /* First time connection, start SDP */ // 新设备处理流程bta_hh_start_sdp(p_cb, p_data); //  启动 SDP 服务发现, 获取设备支持的 HID 服务信息}
}

蓝牙 HID 主机模块中负责启动传统蓝牙 (BR/EDR) 连接。根据设备是否已被识别 (是否有 app_id),选择直接添加设备或先执行 SDP (Services Discovery Protocol) 服务发现,从而建立与 HID 设备的通信通道。

情况一:已识别设备处理

HID_HostAddDev
packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/********************************************************************************* Function         HID_HostAddDev** Description      This is called so HID-host may manage this device.** Returns          tHID_STATUS*******************************************************************************/
tHID_STATUS HID_HostAddDev(const RawAddress& addr, uint16_t attr_mask,uint8_t* handle) {// 1. 初始化检查int i;/* Find an entry for this device in hh_cb.devices array */if (!hh_cb.reg_flag) return (HID_ERR_NOT_REGISTERED);// 2. 设备查找for (i = 0; i < HID_HOST_MAX_DEVICES; i++) { // 遍历设备数组,查找是否已存在该设备if ((hh_cb.devices[i].in_use) && addr == hh_cb.devices[i].addr) break;}// 3. 空闲条目分配if (i == HID_HOST_MAX_DEVICES) {for (i = 0; i < HID_HOST_MAX_DEVICES; i++) {if (!hh_cb.devices[i].in_use) break;}}if (i == HID_HOST_MAX_DEVICES) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_RESOURCES_ADD_DEVICE,1);return HID_ERR_NO_RESOURCES;}// 4. 设备条目初始化if (!hh_cb.devices[i].in_use) {hh_cb.devices[i].in_use = true;hh_cb.devices[i].addr = addr;hh_cb.devices[i].state = HID_DEV_NO_CONN;hh_cb.devices[i].conn_tries = 0;}// 5. 设备属性设置// 设置设备属性掩码,包含设备支持的功能和特性hh_cb.devices[i].attr_mask = attr_mask;*handle = i;return (HID_SUCCESS);
}

负责将新的 HID 设备添加到系统管理列表中。通过查找或分配设备条目,初始化设备状态,并返回唯一的设备句柄,为后续的设备管理和通信提供基础。

bta_hh_add_device_to_list
/********************************************************************************* Function         bta_hh_add_device_to_list** Description      Maintain a known device list for BTA HH.** Returns          void*******************************************************************************/
void bta_hh_add_device_to_list(tBTA_HH_DEV_CB* p_cb, uint8_t handle,uint16_t attr_mask,const tHID_DEV_DSCP_INFO* p_dscp_info,uint8_t sub_class, uint16_t ssr_max_latency,uint16_t ssr_min_tout, uint8_t app_id) {
#if (BTA_HH_DEBUG == TRUE)log::verbose("subclass=0x{:2x}", sub_class);
#endif// 1. 基本参数设置p_cb->hid_handle = handle;p_cb->in_use = true;p_cb->attr_mask = attr_mask; // 存储设备的属性掩码(描述设备能力)// 2. 设备分类与应用 ID 设置p_cb->sub_class = sub_class; // 设置设备的子类(如键盘、鼠标等)p_cb->app_id = app_id; // 关联应用 ID(标识使用该设备的应用)// 3. 服务质量参数设置:设置最大延迟和最小超时参数// 用于 HID 服务的服务质量 (QoS) 控制p_cb->dscp_info.ssr_max_latency = ssr_max_latency;p_cb->dscp_info.ssr_min_tout = ssr_min_tout;// 4. 报告描述符处理/* store report descriptor info */if (p_dscp_info) {osi_free_and_reset((void**)&p_cb->dscp_info.descriptor.dsc_list);if (p_dscp_info->dl_len) {p_cb->dscp_info.descriptor.dsc_list =(uint8_t*)osi_malloc(p_dscp_info->dl_len);p_cb->dscp_info.descriptor.dl_len = p_dscp_info->dl_len;// 复制报告描述符内容memcpy(p_cb->dscp_info.descriptor.dsc_list, p_dscp_info->dsc_list,p_dscp_info->dl_len);}}
}

蓝牙 HID 主机模块中维护已知设备列表的核心函数。将设备的关键参数存储到设备控制块中,包括 HID 句柄、属性掩码、报告描述符等信息,为后续的设备通信和管理提供基础数据支持。

bta_hh_sm_execute(BTA_HH_SDP_CMPL_EVT)

bta_hh_better_state_machine(BTA_HH_SDP_CMPL_EVT)
    ...case BTA_HH_W4_CONN_ST:switch (event) {...case BTA_HH_SDP_CMPL_EVT:bta_hh_sdp_cmpl(p_cb, p_data);break;...
bta_hh_sdp_cmpl
packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function         bta_hh_sdp_cmpl** Description      When SDP completes, initiate a connection or report an error*                  depending on the SDP result.*** Returns          void*******************************************************************************/
void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {// 1. 参数校验与初始化CHECK(p_data != nullptr);tBTA_HH_CONN conn_dat;tBTA_HH_STATUS status = p_data->status;log::verbose("status 0x{:2X}", p_data->status);/* initialize call back data */memset((void*)&conn_dat, 0, sizeof(tBTA_HH_CONN));conn_dat.handle = p_cb->hid_handle;conn_dat.link_spec = p_cb->link_spec;// 2. SDP 成功处理/* if SDP compl success */if (status == BTA_HH_OK) {/* not incoming connection doing SDP, initiate a HID connection */if (!p_cb->incoming_conn) { //主动连接tHID_STATUS ret;/* open HID connection */ret = HID_HostOpenDev(p_cb->hid_handle);log::verbose("HID_HostOpenDev returned={}", ret);// 3. HID 连接结果处理if (ret == HID_SUCCESS || ret == HID_ERR_ALREADY_CONN) {status = BTA_HH_OK;} else if (ret == HID_ERR_CONN_IN_PROCESS) {/* Connection already in progress, return from here, SDP* will be performed after connection is completed.*/log::verbose("connection already in progress");return;} else {log::verbose("HID_HostOpenDev failed: Status 0x{:2X}", ret);/* open fail, remove device from management device list */HID_HostRemoveDev(p_cb->hid_handle);status = BTA_HH_ERR;}} else /* incoming connection SDP finish */{ // 被动连接:触发状态机处理连接完成事件bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL);}}// 4. 错误处理流程if (status != BTA_HH_OK) { /* Check if this was incoming connection request  from an unknown device* and connection failed due to missing HID Device SDP UUID* In above condition, disconnect the link as well as remove the* device from list of HID devices*/if ((status == BTA_HH_ERR_SDP) && (p_cb->incoming_conn) &&(p_cb->app_id == 0)) { // 处理 SDP 失败的被动连接(移除设备)log::error("SDP failed for  incoming conn hndl:{}",p_cb->incoming_hid_handle);HID_HostRemoveDev(p_cb->incoming_hid_handle);}// 通过回调通知上层连接失败conn_dat.status = status;(*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH*)&conn_dat);// 触发状态机处理关闭事件/* move state machine W4_CONN ->IDLE */bta_hh_sm_execute(p_cb, BTA_HH_API_CLOSE_EVT, NULL);// 清理未识别设备的控制块/* if this is an outgoing connection to an unknown device, clean up cb */if (p_cb->app_id == 0 && !p_cb->incoming_conn) {/* clean up device control block */bta_hh_clean_up_kdev(p_cb);}bta_hh_trace_dev_db();}// 状态重置p_cb->incoming_conn = false;p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE;return;
}

蓝牙 HID 主机模块中处理 SD完成事件的核心函数。根据 SDP 查询结果决定是发起 HID 连接还是报告错误,并更新设备状态机。在 HID 设备连接流程中起到关键的决策和协调作用。

HID_HostOpenDev
packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/********************************************************************************* Function         HID_HostOpenDev** Description      This function is called when the user wants to initiate a*                  connection attempt to a device.** Returns          void*******************************************************************************/
tHID_STATUS HID_HostOpenDev(uint8_t dev_handle) {// 1. 初始化状态检查if (!hh_cb.reg_flag) return (HID_ERR_NOT_REGISTERED);// 2. 设备句柄验证if ((dev_handle >= HID_HOST_MAX_DEVICES) ||(!hh_cb.devices[dev_handle].in_use)) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_OPEN_DEV,1);return HID_ERR_INVALID_PARAM;}// 3. 设备连接状态验证if (hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_ALREADY_CONN, 1);return HID_ERR_ALREADY_CONN;}// 4. 初始化连接尝试并执行连接hh_cb.devices[dev_handle].conn_tries = 1;return hidh_conn_initiate(dev_handle);
}

蓝牙 HID 主机协议栈中负责发起设备连接的核心函数。验证设备句柄有效性和状态后,调用底层连接初始化函数,确保只有在合法状态下才会尝试建立与 HID 设备的连接。

hidh_conn_initiate
packages/modules/Bluetooth/system/stack/hid/hidh_conn.cc
/********************************************************************************* Function         hidh_conn_initiate** Description      This function is called by the management to create a*                  connection.** Returns          void*******************************************************************************/
tHID_STATUS hidh_conn_initiate(uint8_t dhandle) {tHID_HOST_DEV_CTB* p_dev = &hh_cb.devices[dhandle];// 1. 连接状态合法性检查if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONN_IN_PROCESS,1);return (HID_ERR_CONN_IN_PROCESS);}// 2. 连接参数初始化p_dev->conn.ctrl_cid = 0;p_dev->conn.intr_cid = 0;p_dev->conn.disc_reason =HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: ConnectionAttempt was made but failed *//* We are the originator of this connection */p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG;// 3. 通过 L2CAP 层发起控制通道连接请求/* Check if L2CAP started the connection process */p_dev->conn.ctrl_cid = L2CA_ConnectReq2(HID_PSM_CONTROL, p_dev->addr, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT);// 4. 处理 L2CAP 连接结果if (p_dev->conn.ctrl_cid == 0) { // 连接失败(CID未分配)log::warn("HID-Host Originate failed");hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE,HID_ERR_L2CAP_FAILED, NULL); // 通知上层连接关闭log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_L2CAP_FAILED_AT_INITIATE,1);} else { // 连接成功/* Transition to the next appropriate state, waiting for connection confirm* on control channel. */p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; // 状态转为“控制通道连接中”BTM_LogHistory(kBtmLogTag, hh_cb.devices[dhandle].addr, "Connecting","control channel");}return (HID_SUCCESS);
}

通过 L2CAP层与 HID 设备建立控制通道,是 HID 设备连接流程中 “从逻辑请求到物理连接” 的关键执行环节。

整体遵循 “状态检查 → 连接初始化 → L2CAP 连接请求 → 状态更新与通知” 的流程。

情况二:新设备处理流程

bta_hh_start_sdp
packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function         bta_hh_start_sdp** Description      Start SDP service search, and obtain necessary SDP records.*                  Only one SDP service search request is allowed at the same*                  time. For every BTA_HhOpen API call, do SDP first unless SDP*                  has been done previously.** Returns          void*******************************************************************************/
static void bta_hh_start_sdp(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {// 1. 并发控制(确保单实例 SDP 搜索)if (!bta_hh_cb.p_disc_db) { // 无正在进行的SDP,执行发现流程// 2. 初始化 SDP 数据库bta_hh_cb.p_disc_db =(tSDP_DISCOVERY_DB*)osi_malloc(p_bta_hh_cfg->sdp_db_size);// 3. 启动设备信息(DI)发现/* Do DI discovery first */if (get_legacy_stack_sdp_api()->device_id.SDP_DiDiscover(p_data->api_conn.link_spec.addrt.bda, bta_hh_cb.p_disc_db,p_bta_hh_cfg->sdp_db_size, bta_hh_di_sdp_cback) == SDP_SUCCESS) { /* SDP搜索成功启动,等待回调处理结果 *//* SDP search started successfully* Connection will be triggered at the end of successful SDP search*/} else {  /* 处理SDP启动失败 */log::error("SDP_DiDiscover failed");osi_free_and_reset((void**)&bta_hh_cb.p_disc_db);tBTA_HH_DATA bta_hh_data;bta_hh_data.status = BTA_HH_ERR_SDP;bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, &bta_hh_data);}} else if (bta_hh_cb.p_disc_db) { // 已有SDP在进行中,忽略当前请求/* Incoming/outgoing collision case. DUT initiated HID connection at the* same time as the remote connected HID control channel.* When flow reaches here due to remote initiated connection, DUT may be* doing SDP. In such case, just do nothing and the ongoing SDP completion* or failure will handle this case.*/log::warn("Ignoring as SDP already in progress");}
}

通过 SDP 发现设备支持的服务信息(如设备能力、协议版本等),为后续的连接建立提供必要的元数据。其核心作用是确保 “在建立 HID 连接前,先完成服务发现”,是 HID 连接流程中 “信息获取” 的关键环节。

整体遵循 并发控制 → 初始化 SDP 数据库 → 启动 DI 发现 → 错误处理” 的流程。

SDP_DiDiscover
packages/modules/Bluetooth/system/stack/sdp/sdp_api.cc
/********************************************************************************* Function         SDP_DiDiscover** Description      This function queries a remote device for DI information.** Returns          SDP_SUCCESS if query started successfully, else error*******************************************************************************/
tSDP_STATUS SDP_DiDiscover(const RawAddress& remote_device,tSDP_DISCOVERY_DB* p_db, uint32_t len,tSDP_DISC_CMPL_CB* p_cb) {// 定义目标服务 UUIDtSDP_STATUS result = SDP_DI_DISC_FAILED;uint16_t num_uuids = 1;uint16_t di_uuid = UUID_SERVCLASS_PNP_INFORMATION; // PNP信息服务的UUID(0x1200)/* build uuid for db init */Uuid init_uuid = Uuid::From16Bit(di_uuid); // 转换为通用UUID格式if (SDP_InitDiscoveryDb(p_db, len, num_uuids, &init_uuid, 0, NULL)) // 初始化 SDP 发现数据库if (SDP_ServiceSearchRequest(remote_device, p_db, p_cb)) // 发起 SDP 服务搜索请求result = SDP_SUCCESS;return result;
}

查询远程设备 DI(Device Identification,设备标识信息。通过 SDP 协议搜索远程设备的 PNP(Plug and Play,即插即用)信息服务,获取设备的基础标识数据(如厂商 ID、产品 ID、版本号等),是蓝牙设备发现流程中 “获取设备元数据” 的关键接口。

整体遵循 “目标服务定义 → 数据库初始化 → 发起 SDP 搜索 → 返回结果” 的流程。

SDP发现数据初始化和和服务搜索请求参考https://blog.csdn.net/weixin_37800531/article/details/147704255

三、时序图

四、总结

蓝牙 HID 设备连接流程是多模块协作的复杂过程,核心设计思想体现在以下几点:

  • 状态机驱动:通过 bta_hh_sm_executebta_hh_better_state_machine 严格控制状态转换(如 BTA_HH_IDLE_STBTA_HH_W4_CONN_ST),确保流程可预测性。

  • 防御式编程:多层状态检查(如模块注册状态、设备句柄有效性、连接状态)和资源管理(如 SDP 数据库动态分配 / 释放),避免非法操作和内存泄漏。

  • 异步与解耦:通过消息队列(bta_sys_sendmsg)和回调机制(如 bta_hh_di_sdp_cback)实现跨线程通信,提升模块响应效率。

理解流程对蓝牙 HID 设备的开发、调试及兼容性优化具有重要意义,尤其是在处理多设备并发连接、低功耗 HID(BLE HID)适配等场景时,需重点关注状态同步、传输类型决策及 SDP 服务发现的正确性。


http://www.xdnf.cn/news/8259.html

相关文章:

  • 基于“理采存管用”的数据中台建设方案
  • 高等数学-三角函数
  • PyTorch模型生命周期管理全流程指南:从训练到生产部署
  • SpringBoot的前世今生
  • python 中 SchedulerManager 使用踩坑
  • Spring AI之Advisors (增强器)
  • 中证500股指期货的名词解释
  • Ubuntu-多显示器黑屏问题及nvidia显卡驱动安装
  • 华为模拟器练习简单的拓扑图(五台交换机和pc,4台路由器)
  • MongoDB数据库在现代应用中的高效实践与优化策略
  • 47页 @《人工智能生命体 新启点》中國龍 原创连载
  • Linux下的Socket编程
  • 多端协同开发能力大比拼: AI 编程工具技术架构对比
  • 华为2025年校招笔试真题手撕教程(一)
  • vue3项目动态路由的相关配置踩坑记录
  • LeetCode Hot100 (双指针)
  • 什么是出入库管理系统?2025年五大出入库管理软件推荐
  • 部署TOMEXAM
  • (tarjan 缩点)洛谷 P3119 USACO15JAN Grass Cownoisseur G 题解
  • 文章被检测出是AI写的怎么办?
  • 手写Tomcat(二)—Tomcat简化模型架构
  • 2023河南CCPC省赛vp部分补题
  • 电子墨水电子阅读器行业2025数据分析报告
  • 如果教材这样讲--碳膜电阻、金属氧化膜电阻、金属膜电阻、保险丝电阻、绕线电阻的区别和用途
  • 618服饰大促新打法:在抖音解锁增长三连击
  • 深入理解 PlaNet(Deep Planning Network):基于python从零实现
  • 50、js 中var { ipcRenderer } = require(‘electron‘);是什么意思?
  • Android System UI 深度解析:从架构演进到车载 / TV 场景的全维度定制
  • go中len和sizeof区别
  • 初学c语言18(自定义类型:结构体)