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

【Bluedroid】 HID 设备应用注册与主机服务禁用流程源码解析

本文分析Android Bluetooth协议栈中HID设备(Human Interface Device)注册时禁用HID主机(HID Host)服务的完整流程。核心流程包括:

  1. 注册检查:防止重复注册;

  2. 参数复制:存储应用描述符与QoS配置;

  3. 服务冲突处理:通过btif_hh_service_registration(FALSE)禁用HID主机服务;

  4. 连接关闭:通过L2CAP层断开所有HID主机连接;

  5. 资源释放:清理SDP、描述符缓存等资源。

一、概述

1.1 HID设备应用程序注册入口:register_app()

  • 功能:注册HID设备应用,初始化参数。

  • 关键步骤

    • 重复注册检查:通过btif_hd_cb.app_registered标志位拦截已注册场景。

    • 应用信息拷贝:复制名称、描述符、供应商等数据到app_info结构体。

    • QoS参数配置:分别设置输入(in_qos)与输出(out_qos)服务质量参数。

    • 触发HID主机禁用:调用btif_hh_service_registration(FALSE)关闭主机服务。

1.2 禁用HID HOST服务:btif_hh_service_registration(FALSE)

  • 逻辑分支

    • 首次禁用:若HID主机从未初始化(bt_hh_callbacks == NULL),直接调用btif_hd_service_registration()启用HID设备服务。

    • 动态禁用:若主机已启用,设置btif_hh_cb.service_dereg_active标志,调用BTA_HhDisable()触发协议栈层关闭。

1.3 协议栈层禁用操作:BTA_HhDisable()

  • 执行步骤

    • 系统注销bta_sys_deregister(BTA_ID_HH)移除HID主机的系统注册。

    • 主线程任务投递:通过post_on_bt_main调用bta_hh_api_disable(),处理连接关闭。

      • 无连接场景:直接调用bta_hh_disc_cmpl()完成清理。

      • 活跃连接场景:遍历所有设备,通过BTA_HH_API_CLOSE_EVT事件关闭连接。

        • 底层注销与资源清理:HID_HostDeregister 函数清理所有已注册设备的相关资源,解除连接注册,并且更新注册标志。

        • 设备移除HID_HostRemoveDev 将指定的 HID 从主机管理的设备列表中移除,关闭设备连接并重置设备的相关状态信息。

        • 断开设备连接HID_HostCloseDev 函数断开指定 HID 的连接,在执行断开连接操作之前,会进行一系列的检查,若检查通过,会取消设备的重传定时器,并设置连接尝试次数,最后调用底层函数执行断开连接操作。

        • L2CAP 通道断开hidh_conn_disconnect 函数根据设备连接的控制通道和中断通道的状态,决定是否执行断开操作,若连接存在则将连接状态置为断开中并发起断开请求,若连接已不存在则将连接状态置为未使用。

        • 通道断开后续处理hidh_l2cif_disconnect 函数先发起 L2CAP 断开请求,然后根据 CID 找到对应的设备连接信息,更新连接状态,在必要时断开另一个通道,并在所有通道都断开后更新设备状态、触发回调。

1.4 连接关闭与资源释放

  • L2CAP层断开hidh_l2cif_disconnect()发送L2CA_DisconnectReq,释放CID(Channel ID)。

  • 状态机处理bta_hh_close_act()更新设备状态,通知应用层(BTA_HH_CLOSE_EVT),清理控制块。

  • 全局清理bta_hh_cleanup_disable()释放报告描述符内存,取消SDP搜索,重置控制块。

1.5 回调与状态同步

  • 事件传递bte_hh_evt()将底层事件(如BTA_HH_DISABLE_EVT)转发至BTIF层。

  • BTIF处理btif_hh_upstreams_evt()更新HID设备服务状态,触发connection_state_cb回调,通知Java层连接状态变更。

二、源码分析

register_app

packages/modules/Bluetooth/system/btif/src/btif_hd.ccbtif_hd_cb_t btif_hd_cb;
static tBTA_HD_APP_INFO app_info;
static tBTA_HD_QOS_INFO in_qos;
static tBTA_HD_QOS_INFO out_qos;/********************************************************************************* Function         register_app** Description      Registers HID Device application** Returns          bt_status_t*******************************************************************************/
static bt_status_t register_app(bthd_app_param_t* p_app_param, // 包含应用程序的参数信息bthd_qos_param_t* p_in_qos, // 包含输入方向的 QoS 参数bthd_qos_param_t* p_out_qos // 包含输出方向的 QoS 参数) {log::verbose("");// 1. 应用程序已注册检查if (btif_hd_cb.app_registered) {log::warn("application already registered");return BT_STATUS_DONE;}// 2. 复制应用程序信息app_info.p_name = (char*)osi_calloc(BTIF_HD_APP_NAME_LEN);strlcpy(app_info.p_name, p_app_param->name, BTIF_HD_APP_NAME_LEN);app_info.p_description = (char*)osi_calloc(BTIF_HD_APP_DESCRIPTION_LEN);strlcpy(app_info.p_description, p_app_param->description,BTIF_HD_APP_DESCRIPTION_LEN);app_info.p_provider = (char*)osi_calloc(BTIF_HD_APP_PROVIDER_LEN);strlcpy(app_info.p_provider, p_app_param->provider, BTIF_HD_APP_PROVIDER_LEN);app_info.subclass = p_app_param->subclass;app_info.descriptor.dl_len = p_app_param->desc_list_len;app_info.descriptor.dsc_list =(uint8_t*)osi_malloc(app_info.descriptor.dl_len);memcpy(app_info.descriptor.dsc_list, p_app_param->desc_list,p_app_param->desc_list_len);// 3. 复制 QoS 参数in_qos.service_type = p_in_qos->service_type;in_qos.token_rate = p_in_qos->token_rate;in_qos.token_bucket_size = p_in_qos->token_bucket_size;in_qos.peak_bandwidth = p_in_qos->peak_bandwidth;in_qos.access_latency = p_in_qos->access_latency;in_qos.delay_variation = p_in_qos->delay_variation;out_qos.service_type = p_out_qos->service_type;out_qos.token_rate = p_out_qos->token_rate;out_qos.token_bucket_size = p_out_qos->token_bucket_size;out_qos.peak_bandwidth = p_out_qos->peak_bandwidth;out_qos.access_latency = p_out_qos->access_latency;out_qos.delay_variation = p_out_qos->delay_variation;// 3. 禁用 HID 主机服务并返回结果/* register HID Device with L2CAP and unregister HID Host with L2CAP *//* Disable HH */btif_hh_service_registration(FALSE);return BT_STATUS_SUCCESS;
}

注册 HID(Human Interface Device,人机接口设备)设备应用程序。会检查应用程序是否已经注册,若未注册则复制应用程序相关信息,同时复制 QoS(Quality of Service,服务质量)参数,最后禁用 HID 主机服务并返回注册成功状态。

btif_hh_service_registration

/********************************************************************************* Function         btif_hh_service_registration** Description      Registers or derigisters the hid host service** Returns          none*******************************************************************************/
static bthh_callbacks_t* bt_hh_callbacks = NULL;void btif_hh_service_registration(bool enable) {log::verbose("");log::verbose("enable = {}", enable);if (bt_hh_callbacks == NULL) { // 说明 HID 主机服务从未被初始化// The HID Host service was never initialized (it is either disabled or not// available in this build). We should proceed directly to changing the HID// Device service state (if needed).if (!enable) {btif_hd_service_registration();}} else if (enable) {BTA_HhEnable(bte_hh_evt, bt_hh_enable_type.hidp_enabled,bt_hh_enable_type.hogp_enabled);} else {btif_hh_cb.service_dereg_active = TRUE; // 表示服务注销操作正在进行BTA_HhDisable(); // 禁用 HID 主机服务}
}

BTA_HhDisable

packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/********************************************************************************* Function         BTA_HhDisable** Description      Disable the HID host. If the server is currently*                  connected, the connection will be closed.** Returns          void*******************************************************************************/
void BTA_HhDisable(void) {bta_sys_deregister(BTA_ID_HH); // 系统注销操作post_on_bt_main([]() { bta_hh_api_disable(); }); // 在蓝牙主线程执行操作
}

禁用 HID主机。先在系统层面注销 HID 主机的注册信息,接着把调用 bta_hh_api_disable 函数的任务投递到蓝牙主线程去执行,以此完成 HID 主机的禁用操作,并且若服务器当前处于连接状态,会关闭连接。

bta_sys_deregister
packages/modules/Bluetooth/system/bta/sys/bta_sys_main.cc
/********************************************************************************* Function         bta_sys_deregister** Description      Called by other BTA subsystems to de-register*                  handler.*** Returns          void*******************************************************************************/
/* system manager control block definition */
tBTA_SYS_CB bta_sys_cb;void bta_sys_deregister(uint8_t id) { bta_sys_cb.is_reg[id] = false; }

从系统中注销指定子系统处理程序的功能。当某个子系统不再需要在系统中进行注册时,可以调用此函数来更新系统的注册状态。

bta_hh_api_disable
packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/****************************************************************************** Global data****************************************************************************/
tBTA_HH_CB bta_hh_cb;/********************************************************************************* Function         bta_hh_api_disable** Description      Perform necessary operations to disable HID host.*** Returns          void*******************************************************************************/
void bta_hh_api_disable(void) {uint8_t xx;// 1. 检查服务是否启用/* service is not enabled */if (bta_hh_cb.p_cback == NULL) return;// 2. 处理无连接的情况/* no live connection, signal DISC_CMPL_EVT directly */if (!bta_hh_cb.cnt_num) {bta_hh_disc_cmpl(); // 发送断开连接完成的事件通知} // 3. 处理有连接的情况else /* otherwise, disconnect all live connections */{bta_hh_cb.w4_disable = true; // 表示正在等待禁用操作完成for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) {/* send API_CLOSE event to every connected device */if (bta_hh_cb.kdev[xx].state == BTA_HH_CONN_ST) { // 检查是否处于连接状态/* disconnect all connected devices */bta_hh_sm_execute(&bta_hh_cb.kdev[xx], BTA_HH_API_CLOSE_EVT, NULL); // 请求关闭连接}}}return;
}

执行禁用 HID主机所需的必要操作。首先检查 HID 主机服务是否启用,然后根据当前连接的数量采取不同的操作。如果没有连接,直接通知断开连接完成;如果有连接,则设置等待禁用标志,并向所有连接的设备发送关闭事件,以确保所有连接都被正确关闭。

先看情况一,无连接的情况。

情况一:bta_hh_disc_cmpl

packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function         bta_hh_disc_cmpl** Description      All connections have been closed, disable service.*** Returns          void*******************************************************************************/
void bta_hh_disc_cmpl(void) {// 1. 日志记录与状态初始化log::debug("Disconnect complete");tBTA_HH_STATUS status = BTA_HH_OK;// 2. 底层注销操作/* Deregister with lower layer */if (HID_HostDeregister() != HID_SUCCESS) status = BTA_HH_ERR;// 3. 根据 GATT 接口状态执行不同操作if (bta_hh_cb.gatt_if != BTA_GATTS_INVALID_IF) {log::debug("Deregister HOGP host before cleanup");bta_hh_le_deregister();} else {bta_hh_cleanup_disable(status);}
}

在所有连接都关闭后,先尝试进行底层的 HID 主机注销操作,根据操作结果更新状态。接着根据 GATT(Generic Attribute Profile)接口的状态,决定是否先注销 HOGP(Human Interface Device over GATT Profile)主机,最后清理禁用相关的资源。确保 HID 主机服务被正确禁用。

HID_HostDeregister

packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/********************************************************************************* Function         HID_HostDeregister** Description      This function is called when the host is about power down.** Returns          tHID_STATUS*******************************************************************************/
tHID_STATUS HID_HostDeregister(void) {uint8_t i;// 1. 检查主机注册状态if (!hh_cb.reg_flag) return (HID_ERR_NOT_REGISTERED);// 2. 遍历并清理已注册设备for (i = 0; i < HID_HOST_MAX_DEVICES; i++) {HID_HostRemoveDev(i);alarm_free(hh_cb.devices[i].conn.process_repage_timer); // 释放与该设备连接相关的定时器资源hh_cb.devices[i].conn.process_repage_timer = NULL;}// 3. 解除连接注册并更新标志hidh_conn_dereg();hh_cb.reg_flag = false;return (HID_SUCCESS);
}

在 HID主机即将关机时执行注销操作。清理所有已注册设备的相关资源,解除连接注册,并且更新注册标志。

HID_HostRemoveDev

packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/********************************************************************************* Function         HID_HostRemoveDev** Description      This removes the device from the list of devices that the*                  host has to manage.** Returns          tHID_STATUS*******************************************************************************/
tHID_STATUS HID_HostRemoveDev(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_REMOVE_DEV,1);return HID_ERR_INVALID_PARAM;}// 3.关闭设备连接并重置状态HID_HostCloseDev(dev_handle);hh_cb.devices[dev_handle].in_use = false;hh_cb.devices[dev_handle].conn.conn_state = HID_CONN_STATE_UNUSED;hh_cb.devices[dev_handle].conn.ctrl_cid =hh_cb.devices[dev_handle].conn.intr_cid = 0;hh_cb.devices[dev_handle].attr_mask = 0;return HID_SUCCESS;
}

将指定的 HID从主机管理的设备列表中移除。先检查主机的注册状态和设备句柄的有效性,若都符合要求,会关闭设备连接并重置设备的相关状态信息。

HID_HostCloseDev

packages/modules/Bluetooth/system/stack/hid/hidh_api.cc
/********************************************************************************* Function         HID_HostCloseDev** Description      This function disconnects the device.** Returns          void*******************************************************************************/
tHID_STATUS HID_HostCloseDev(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_CLOSE_DEV,1);return HID_ERR_INVALID_PARAM;}// 3. 检查设备连接状态if (hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_HOST_CLOSE_DEV,1);return HID_ERR_NO_CONNECTION;}// 4. 取消重传定时器并设置连接尝试次数alarm_cancel(hh_cb.devices[dev_handle].conn.process_repage_timer);hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY + 1;return hidh_conn_disconnect(dev_handle);
}

断开指定 HID的连接。在执行断开连接操作之前,会进行一系列的检查,确保主机已注册、设备句柄有效以及设备处于连接状态。若检查通过,会取消设备的重传定时器,并设置连接尝试次数,最后调用底层函数执行断开连接操作。

hidh_conn_disconnect

/packages/modules/Bluetooth/system/stack/hid/hidh_conn.cc
/********************************************************************************* Function         hidh_conn_disconnect** Description      This function disconnects a connection.** Returns          true if disconnect started, false if already disconnected*******************************************************************************/
tHID_STATUS hidh_conn_disconnect(uint8_t dhandle) {// 1. 获取连接信息tHID_CONN* p_hcon = &hh_cb.devices[dhandle].conn;// 2. 检查连接状态并执行断开操作if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) { // 连接存在情况p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; // 表示正在断开连接/* Set l2cap idle timeout to 0 (so ACL link is disconnected* immediately after last channel is closed) */L2CA_SetIdleTimeoutByBdAddr(hh_cb.devices[dhandle].addr, 0,BT_TRANSPORT_BR_EDR);/* Disconnect both interrupt and control channels */if (p_hcon->intr_cid)hidh_l2cif_disconnect(p_hcon->intr_cid); // 断开中断通道else if (p_hcon->ctrl_cid)hidh_l2cif_disconnect(p_hcon->ctrl_cid); // 断开控制通道BTM_LogHistory(kBtmLogTag, hh_cb.devices[dhandle].addr, "Disconnecting","local initiated");} else { // 连接不存在情况p_hcon->conn_state = HID_CONN_STATE_UNUSED;}return HID_SUCCESS;
}

断开指定 HID的连接。根据设备连接的控制通道(ctrl_cid)和中断通道(intr_cid)的状态,决定是否执行断开操作,若连接存在则将连接状态置为断开中并发起断开请求,若连接已不存在则将连接状态置为未使用。

hidh_l2cif_disconnect

packages/modules/Bluetooth/system/stack/hid/hidh_conn.cc
static void hidh_l2cif_disconnect(uint16_t l2cap_cid) {// 1. 发起 L2CAP 断开请求L2CA_DisconnectReq(l2cap_cid);// 2. 根据 CID 查找设备连接信息/* Find CCB based on CID */const uint8_t dhandle = find_conn_by_cid(l2cap_cid);if (dhandle == kHID_HOST_MAX_DEVICES) {log::warn("HID-Host Rcvd L2CAP disc cfm, unknown CID: 0x{:x}", l2cap_cid);return;}// 3. 获取设备连接结构体指针tHID_CONN* p_hcon = &hh_cb.devices[dhandle].conn;// 4. 更新连接状态if (l2cap_cid == p_hcon->ctrl_cid) {p_hcon->ctrl_cid = 0; // 表示该通道已断开} else {p_hcon->intr_cid = 0;if (p_hcon->ctrl_cid) {log::verbose("HID-Host Initiating L2CAP Ctrl disconnection");L2CA_DisconnectReq(p_hcon->ctrl_cid); // 控制通道的断开请求p_hcon->ctrl_cid = 0; }}// 5. 检查所有通道是否都已断开if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;p_hcon->conn_state = HID_CONN_STATE_UNUSED;BTM_LogHistory(kBtmLogTag, hh_cb.devices[dhandle].addr, "Disconnected");// 通知上层设备已关闭hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE,p_hcon->disc_reason, NULL);}
}

断开指定 L2CAP通道的连接。先发起 L2CAP 断开请求,然后根据 CID(Channel Identifier,通道标识符)找到对应的设备连接信息,更新连接状态,在必要时断开另一个通道,并在所有通道都断开后更新设备状态、触发回调。

开始状态回调:bta_hh_cback

packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/******************************************************************************  Static Function****************************************************************************/
/********************************************************************************* Function         bta_hh_cback** Description      BTA HH callback function.*** Returns          void*******************************************************************************/
static void bta_hh_cback(uint8_t dev_handle, const RawAddress& addr,uint8_t event, uint32_t data, BT_HDR* pdata) {// 1. 局部变量声明uint16_t sm_event = BTA_HH_INVALID_EVT;uint8_t xx = 0;log::verbose("HID_event [{}]", bta_hh_hid_event_name(event));// 2. 事件处理switch (event) {case HID_HDEV_EVT_OPEN:sm_event = BTA_HH_INT_OPEN_EVT;break;case HID_HDEV_EVT_CLOSE:sm_event = BTA_HH_INT_CLOSE_EVT;break;case HID_HDEV_EVT_INTR_DATA:sm_event = BTA_HH_INT_DATA_EVT;break;case HID_HDEV_EVT_HANDSHAKE:sm_event = BTA_HH_INT_HANDSK_EVT;break;case HID_HDEV_EVT_CTRL_DATA:sm_event = BTA_HH_INT_CTRL_DATA;break;case HID_HDEV_EVT_RETRYING:break;case HID_HDEV_EVT_INTR_DATC:case HID_HDEV_EVT_CTRL_DATC:/* Unhandled events: Free buffer for DATAC */osi_free_and_reset((void**)&pdata);break;case HID_HDEV_EVT_VC_UNPLUG:for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) {if (bta_hh_cb.kdev[xx].hid_handle == dev_handle) {bta_hh_cb.kdev[xx].vp = true;break;}}break;}// 3. 发送事件消息if (sm_event != BTA_HH_INVALID_EVT) {tBTA_HH_CBACK_DATA* p_buf = (tBTA_HH_CBACK_DATA*)osi_malloc(sizeof(tBTA_HH_CBACK_DATA) + sizeof(BT_HDR));p_buf->hdr.event = sm_event;p_buf->hdr.layer_specific = (uint16_t)dev_handle;p_buf->data = data;p_buf->link_spec.addrt.bda = addr;p_buf->link_spec.addrt.type = BLE_ADDR_PUBLIC;p_buf->link_spec.transport = BT_TRANSPORT_BR_EDR;p_buf->p_data = pdata;bta_sys_sendmsg(p_buf);}
}

bta_hh_cback 是一个静态回调函数,用于处理蓝牙 HID主机的各种事件。根据不同的 HID 事件类型,将其映射为内部状态机事件,对部分事件进行特殊处理,如释放缓冲区或更新设备状态,最后将处理后的事件消息发送给系统。

bta_sys_sendmsg(BTA_HH_INT_CLOSE_EVT)

直接看直接处理函数。

bta_hh_better_state_machine


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:...case BTA_HH_CONN_ST:switch (event) {case BTA_HH_API_CLOSE_EVT:bta_hh_api_disc_act(p_cb, p_data);break;case BTA_HH_INT_OPEN_EVT:bta_hh_open_act(p_cb, p_data);break;case BTA_HH_INT_CLOSE_EVT:p_cb->state = BTA_HH_IDLE_ST;bta_hh_close_act(p_cb, p_data);p_cb->statebreak;  ...      

bta_hh_close_act

/packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function         bta_hh_close_act** Description      HID Host process a close event*** Returns          void*******************************************************************************/
void bta_hh_close_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {tBTA_HH_CBDATA disc_dat = {BTA_HH_OK, 0};// 1. 解析关闭原因uint32_t reason = p_data->hid_cback.data; /* Reason for closing (32-bit) */const bool l2cap_conn_fail = reason & HID_L2CAP_CONN_FAIL;const bool l2cap_req_fail = reason & HID_L2CAP_REQ_FAIL;const bool l2cap_cfg_fail = reason & HID_L2CAP_CFG_FAIL;const tHID_STATUS hid_status = static_cast<tHID_STATUS>(reason & 0xff);// 2. 确定事件类型/* if HID_HDEV_EVT_VC_UNPLUG was received, report BTA_HH_VC_UNPLUG_EVT */uint16_t event = p_cb->vp ? BTA_HH_VC_UNPLUG_EVT : BTA_HH_CLOSE_EVT;// 3. 填充断开数据结构体disc_dat.handle = p_cb->hid_handle;disc_dat.status = to_bta_hh_status(p_data->hid_cback.data);std::string overlay_fail =base::StringPrintf("%s %s %s", (l2cap_conn_fail) ? "l2cap_conn_fail" : "",(l2cap_req_fail) ? "l2cap_req_fail" : "",(l2cap_cfg_fail) ? "l2cap_cfg_fail" : "");BTM_LogHistory(kBtmLogTag, p_cb->link_spec.addrt.bda, "Closed",base::StringPrintf("%s reason %s %s",(p_cb->is_le_device) ? "le" : "classic",hid_status_text(hid_status).c_str(),overlay_fail.c_str()));// 4. 通知蓝牙系统层关闭 HID 主机连接,释放系统资源/* inform role manager */bta_sys_conn_close(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);// 5. 更新连接计数/* update total conn number */bta_hh_cb.cnt_num--;// 6. 触发回调if (disc_dat.status) disc_dat.status = BTA_HH_ERR;(*bta_hh_cb.p_cback)(event, (tBTA_HH*)&disc_dat);// 7. 处理虚拟拔出场景/* if virtually unplug, remove device */if (p_cb->vp) {HID_HostRemoveDev(p_cb->hid_handle);bta_hh_clean_up_kdev(p_cb);}// 8. 跟踪设备数据库bta_hh_trace_dev_db();// 9. 重置控制块状态/* clean up control block, but retain SDP info and device handle */p_cb->vp = false;p_cb->w4_evt = 0;// 10. 检查是否禁用 HID 主机服务/* if no connection is active and HH disable is signaled, disable service */if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) {bta_hh_disc_cmpl();}return;
}

处理 HID 主机的连接关闭事件。解析关闭原因、更新设备状态、通知系统连接关闭、触发回调,并在必要时清理资源或禁用 HID 主机服务。

bta_hh_cleanup_disable
/packages/modules/Bluetooth/system/bta/hh/bta_hh_utils.cc
/********************************************************************************* Function         bta_hh_cleanup_disable** Description      when disable finished, cleanup control block and send*                  callback*** Returns          void*******************************************************************************/
void bta_hh_cleanup_disable(tBTA_HH_STATUS status) {// 1. 释放报告描述符缓冲区uint8_t xx;/* free buffer in CB holding report descriptors */for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) {osi_free_and_reset((void**)&bta_hh_cb.kdev[xx].dscp_info.descriptor.dsc_list);}// 2. 取消 SDP 搜索并释放相关内存if (bta_hh_cb.p_disc_db) { // 说明之前启动了 SDP 搜索/* Cancel SDP if it had been started. */(void)get_legacy_stack_sdp_api()->service.SDP_CancelServiceSearch(bta_hh_cb.p_disc_db); // 取消正在进行的 SDP 搜索osi_free_and_reset((void**)&bta_hh_cb.p_disc_db);}// 3. 发送回调并清理控制块if (bta_hh_cb.p_cback) {tBTA_HH bta_hh;bta_hh.status = status;(*bta_hh_cb.p_cback)(BTA_HH_DISABLE_EVT, &bta_hh); // 通知调用者 HID 主机禁用操作完成/* all connections are down, no waiting for diconnect */memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB));}
}

在 HID主机禁用操作完成后,进行清理工作并发送回调。依次释放设备报告描述符的内存、取消可能正在进行的 SDP(Service Discovery Protocol)搜索并释放相关内存,然后通过回调函数通知调用者禁用操作的状态,最后清理 HID 主机的控制块结构体。确保系统资源的正确释放和状态的重置。

SDP_CancelServiceSearch
packages/modules/Bluetooth/system/stack/sdp/sdp_api.cc
/********************************************************************************* Function         SDP_CancelServiceSearch** Description      This function cancels an active query to an SDP server.** Returns          true if discovery cancelled, false if a matching activity is*                  not found.*******************************************************************************/
bool SDP_CancelServiceSearch(const tSDP_DISCOVERY_DB* p_db) {// 查找连接控制块tCONN_CB* p_ccb = sdpu_find_ccb_by_db(p_db);if (!p_ccb) return (false);// 断开 SDP 连接并更新状态sdp_disconnect(p_ccb, SDP_CANCEL);p_ccb->disc_state = SDP_DISC_WAIT_CANCEL; // 表示当前处于等待取消查询的状态return (true);
}

取消对 SDP服务器正在进行的查询操作。若成功取消发现过程,函数返回 true;若未找到匹配的查询活动,则返回 false

sdp_disconnect
/********************************************************************************* Function         sdp_disconnect** Description      This function disconnects a connection.** Returns          void*******************************************************************************/
void sdp_disconnect(tCONN_CB* p_ccb, tSDP_REASON reason) {// 1. 引用连接控制块tCONN_CB& ccb = *p_ccb;log::verbose("SDP - disconnect  CID: 0x{:x}", ccb.connection_id);// 2. 检查连接 ID 并处理断开操作/* Check if we have a connection ID */if (ccb.connection_id != 0) { // 说明存在有效的连接ccb.disconnect_reason = reason;if (SDP_SUCCESS == reason && sdpu_process_pend_ccb_same_cid(*p_ccb)) {sdpu_callback(ccb, reason);sdpu_release_ccb(ccb);return;} else {L2CA_DisconnectReq(ccb.connection_id);}}// 3. 处理处于连接建立状态的情况/* If at setup state, we may not get callback ind from L2CAP *//* Call user callback immediately */if (ccb.con_state == SDP_STATE_CONN_SETUP) { // 连接正在建立过程中sdpu_callback(ccb, reason); // 通知用户连接断开sdpu_clear_pend_ccb(ccb); // 清除可能存在的挂起连接控制块信息sdpu_release_ccb(ccb); // 释放连接控制块资源}
}

断开 SDP连接。根据连接的当前状态和断开原因,执行相应的操作,包括调用回调函数、释放连接控制块资源等。

开始处理结果回调:bte_hh_evt(BTA_HH_DISABLE_EVT)

/packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function         bte_hh_evt** Description      Switches context from BTE to BTIF for all HH events** Returns          void*******************************************************************************/static void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH* p_data) {bt_status_t status;int param_len = 0;tBTIF_COPY_CBACK* p_copy_cback = NULL;// 1. 事件类型与参数长度匹配if (BTA_HH_ENABLE_EVT == event)param_len = sizeof(tBTA_HH_STATUS);else if (BTA_HH_OPEN_EVT == event)param_len = sizeof(tBTA_HH_CONN);else if (BTA_HH_DISABLE_EVT == event)param_len = sizeof(tBTA_HH_STATUS);else if (BTA_HH_CLOSE_EVT == event)param_len = sizeof(tBTA_HH_CBDATA);else if (BTA_HH_GET_DSCP_EVT == event)param_len = sizeof(tBTA_HH_DEV_DSCP_INFO);else if ((BTA_HH_GET_PROTO_EVT == event) || (BTA_HH_GET_IDLE_EVT == event))param_len = sizeof(tBTA_HH_HSDATA);else if (BTA_HH_GET_RPT_EVT == event) { // 特殊事件处理(报告数据事件)BT_HDR* hdr = p_data->hs_data.rsp_data.p_rpt_data;param_len = sizeof(tBTA_HH_HSDATA);if (hdr != NULL) {p_copy_cback = btif_hh_hsdata_rpt_copy_cb;  // 注册数据复制回调param_len += BT_HDR_SIZE + hdr->offset + hdr->len; // 计算总长度(含数据缓冲区)}} else if ((BTA_HH_SET_PROTO_EVT == event) || (BTA_HH_SET_RPT_EVT == event) ||(BTA_HH_VC_UNPLUG_EVT == event) || (BTA_HH_SET_IDLE_EVT == event))param_len = sizeof(tBTA_HH_CBDATA);else if ((BTA_HH_ADD_DEV_EVT == event) || (BTA_HH_RMV_DEV_EVT == event))param_len = sizeof(tBTA_HH_DEV_INFO);else if (BTA_HH_API_ERR_EVT == event)param_len = 0;// 2. 上下文传输/* switch context to btif task context (copy full union size for convenience)*/status = btif_transfer_context(btif_hh_upstreams_evt, (uint16_t)event,(char*)p_data, param_len, p_copy_cback);/* catch any failed context transfers */ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
}

HID 主机(HH)事件从 蓝牙协议栈底层(BTA) 向 蓝牙接口层(BTIF) 传递的桥梁。其根据不同的事件类型,计算事件参数长度,构建上下文传输数据,并通过 btif_transfer_context 函数将事件从 BTA 层切换到 BTIF 层处理,确保事件在正确的任务上下文中执行。

btif_hh_upstreams_evt(BTA_HH_DISABLE_EVT)
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function         btif_hh_upstreams_evt** Description      Executes HH UPSTREAMS events in btif context** Returns          void*******************************************************************************/
static void btif_hh_upstreams_evt(uint16_t event, char* p_param) {tBTA_HH* p_data = (tBTA_HH*)p_param;btif_hh_device_t* p_dev = NULL;int i;int len, tmplen;log::verbose("event={} dereg = {}", dump_hh_event(event),btif_hh_cb.service_dereg_active);switch (event) {...case BTA_HH_DISABLE_EVT:// 检查 HID 主机状态并清理回调if (btif_hh_cb.status == BTIF_HH_DISABLING) {bt_hh_callbacks = NULL;}btif_hh_cb.status = BTIF_HH_DISABLED; // 更新 HID 主机状态// 处理服务注销激活状态if (btif_hh_cb.service_dereg_active) { // 表示 HID 设备服务的注销操作已激活log::verbose("BTA_HH_DISABLE_EVT: enabling HID Device service");btif_hd_service_registration(); // 重新启用 HID 设备服务btif_hh_cb.service_dereg_active = FALSE; // 表示注销操作已完成}if (p_data->status == BTA_HH_OK) { // 处理禁用成功情况int i;// Clear the control blockfor (i = 0; i < BTIF_HH_MAX_HID; i++) {alarm_free(btif_hh_cb.devices[i].vup_timer);}memset(&btif_hh_cb, 0, sizeof(btif_hh_cb));for (i = 0; i < BTIF_HH_MAX_HID; i++) {btif_hh_cb.devices[i].dev_status = BTHH_CONN_STATE_UNKNOWN;}} elselog::warn("BTA_HH_DISABLE_EVT: Error, HH disabling failed, status = {}",p_data->status);break;...

在蓝牙接口层(BTIF)上下文中处理 HID 主机(HH)的上游事件。这里主要分析 BTA_HH_DISABLE_EVT 事件的处理逻辑。

情况二:bta_hh_sm_execute(BTA_HH_API_CLOSE_EVT, NULL)

bta_hh_better_state_machine

bta_hh_api_disc_act
/********************************************************************************* Function         bta_hh_api_disc_act** Description      HID Host initiate a disconnection.*** Returns          void*******************************************************************************/
void bta_hh_api_disc_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {CHECK(p_cb != nullptr);// 1. 处理 LE 设备断开if (p_cb->is_le_device) {log::debug("Host initiating close to le device:{}",ADDRESS_TO_LOGGABLE_CSTR(p_cb->link_spec));bta_hh_le_api_disc_act(p_cb);}// 2. 处理经典设备断开else {// 提取设备句柄(优先从 p_data 中获取,否则使用 p_cb 中的句柄)const uint8_t hid_handle =(p_data != nullptr) ? static_cast<uint8_t>(p_data->hdr.layer_specific): p_cb->hid_handle;tHID_STATUS status = HID_HostCloseDev(hid_handle); // 关闭设备连接if (status != HID_SUCCESS) {log::warn("Failed closing classic device:{} status:{}",ADDRESS_TO_LOGGABLE_CSTR(p_cb->link_spec),hid_status_text(status));} else {log::debug("Host initiated close to classic device:{}",ADDRESS_TO_LOGGABLE_CSTR(p_cb->link_spec));}// 3. 构造回调数据tBTA_HH bta_hh = {.dev_status = {.status =(status == HID_SUCCESS) ? BTA_HH_OK : BTA_HH_ERR,.handle = hid_handle},};// 4. 触发断开事件回调,通知上层连接已关闭(*bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, &bta_hh);}
}

处理 HID 主机主动发起的断开连接请求。根据设备类型(LE 或经典蓝牙)执行不同的断开逻辑:

  • LE 设备:调用专门的 LE 断开函数。

  • 经典设备:调用 HID_HostCloseDev 关闭连接,并处理结果回调。

处理结果回调:bte_hh_evt(BTA_HH_CLOSE_EVT)

根据前文分析,转btif函数btif_hh_upstreams_evt继续处理。

btif_hh_upstreams_evt(BTA_HH_CLOSE_EVT)
/********************************************************************************* Function         btif_hh_upstreams_evt** Description      Executes HH UPSTREAMS events in btif context** Returns          void*******************************************************************************/
static void btif_hh_upstreams_evt(uint16_t event, char* p_param) {tBTA_HH* p_data = (tBTA_HH*)p_param;btif_hh_device_t* p_dev = NULL;int i;int len, tmplen;log::verbose("event={} dereg = {}", dump_hh_event(event),btif_hh_cb.service_dereg_active);switch (event) {...case BTA_HH_CLOSE_EVT:log::verbose("BTA_HH_CLOSE_EVT: status = {}, handle = {}",p_data->dev_status.status, p_data->dev_status.handle);p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle); // 查找设备控制块if (p_dev != NULL) { // 设备存在时的处理// 触发连接状态变更回调(断开中)HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->link_spec.addrt.bda), BTHH_CONN_STATE_DISCONNECTING);log::verbose("uhid fd={} local_vup={}", p_dev->fd, p_dev->local_vup);btif_hh_stop_vup_timer(&(p_dev->link_spec)); // 停止虚拟拔出定时器//  处理本地发起的虚拟拔出场景/* If this is a locally initiated VUP, remove the bond as ACL got*  disconnected while VUP being processed.*/if (p_dev->local_vup) {p_dev->local_vup = false;// 删除设备的绑定关系,因为连接断开时 VUP 流程未完成BTA_DmRemoveDevice(p_dev->link_spec.addrt.bda); } else if (p_data->dev_status.status == BTA_HH_HS_SERVICE_CHANGED) { // 处理因服务变更导致的断开/* Local disconnection due to service change in the HOGP device.HID descriptor would be read again, so remove it from cache. */log::warn("Removing cached descriptor due to service change, handle = {}",p_data->dev_status.handle);btif_storage_remove_hid_info(p_dev->link_spec.addrt.bda); // 清除该设备的缓存描述符}// 更新设备状态btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_DISCONNECTED;p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;// 调用清理函数并触发最终状态回调bta_hh_co_close(p_dev);HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->link_spec.addrt.bda), p_dev->dev_status);} else { // 设备不存在时的错误处理log::warn("Error: cannot find device with handle {}",p_data->dev_status.handle);}break;...

在蓝牙接口层(BTIF)上下文处理 HID 主机的 连接关闭事件(BTA_HH_CLOSE_EVT)。核心逻辑包括:

  1. 根据设备句柄查找已连接的设备。

  2. 更新设备连接状态并触发 HAL 回调。

  3. 处理虚拟拔出(VUP)场景下的绑定关系删除。

  4. 清除缓存的设备描述符(如因服务变更导致断开)。

  5. 调用清理函数释放资源。

三、时序图

3.1 HID 设备应用注册时序图

3.2 HID 主机服务禁用时序图

四、总结

HID 设备应用注册与主机服务禁用流程体现了蓝牙协议栈的分层设计与角色管理机制:

  • 应用层负责参数初始化和角色切换触发,通过动态内存管理和 QoS 配置为设备功能提供基础。

  • 接口层(BTIF)作为跨层桥梁,实现事件上下文切换和上层回调,保障应用与底层的解耦。

  • 协议栈层(BTA)通过状态机和事件驱动机制处理连接断开、资源释放,确保协议合规性。

    • 冲突避免:HID设备注册时强制禁用HID主机服务,确保同一时刻仅一种角色(Host/Device)活跃。

    • 连接管理:通过L2CAP层断开所有活跃连接,防止残留链路影响设备功能。

    • 资源隔离:清理SDP、描述符缓存等资源,避免数据污染。

    • 状态同步:跨层(BTA→BTIF→HAL)事件传递保证应用层状态一致性。

通过严谨的状态检查、资源清理和跨层协作,确保 HID 设备在角色切换(如从主机转为设备)时的稳定性和可靠性,为蓝牙外设(如键盘、鼠标)的多模式工作提供了底层支持。

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

相关文章:

  • SpringBoot项目接入DeepSeek
  • 「Mac畅玩AIGC与多模态24」开发篇20 - 多语言输出工作流示例
  • 17.Java 注解与实例
  • C++回顾 Day4
  • 【Bootstrap V4系列】学习入门教程之 组件-轮播(Carousel)高级用法
  • 基于供热企业业务梳理的智能化赋能方案
  • 易境通散货拼柜系统:如何让拼箱货代协作效率翻倍?
  • 编程日志4.28
  • python23-函数返回值和参数处理,变量作用域
  • 记录学习的第三十五天
  • 2025-05-08-如何在一次 cmd 会话中批量设置多个 API key?
  • 英文论文查重笔记
  • 用3D slicer 去掉影像中的干扰体素而还原干净影像(脱敏切脸处理同)
  • 按拼音首字母进行排序组成新的数组(vue)
  • 强人工智能是否会诞生于现在的AI之中
  • 第二章 MySql
  • lc3341. 到达最后一个房间的最少时间 Ⅰ 算法解析
  • Red Hat linux环境openssh升级到openssh-10.0p1
  • FileInputStream
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(15):何と どういう
  • process-exporter服务安装并启动
  • 【C++游戏引擎开发】第32篇:物理引擎(Bullet)—约束系统
  • ollama+deepseek+openwebui安装
  • OrangePi Zero 3学习笔记(Android篇)2 - 第一个C程序
  • 创建需求跟踪矩阵5大常见步骤(附注意事项)
  • linux - shell脚本编程
  • 解锁 AI 生产力:Google 四大免费工具全面解析20250507
  • vue3+ts的watch全解!
  • 登顶中国:基于 Trae AI与 EdgeOne MCP 的全国各省最高峰攀登攻略博客构建实践
  • 比较入站和出站防火墙规则