【Bluedroid】蓝牙HID Host disconnect流程源码解析
本文基于 Android 蓝牙 HID(Human Interface Device)Host 模块的源码,深入解析 HID 设备断开连接的完整流程。重点覆盖从应用层触发断开请求,到 BTIF 层(接口适配层)状态校验与异步传递、BTA 层(协议栈适配层)状态机驱动、HID 协议栈执行物理断连,最终通过 BTA/BTIF 层回调通知应用层的全链路逻辑。揭示各层级如何通过状态校验、事件传递、资源清理和状态同步,确保断开操作的健壮性、可诊断性和用户体验的一致性。
一、流程概述
蓝牙 HID Host 模块的断开连接流程可分为6 大核心环节,贯穿应用层、BTIF 层、BTA 层和 HID 协议栈,各层级协作完成状态校验、物理断连和状态通知:
1.1 应用层触发断开请求(BTIF 层:disconnect 函数)
-
状态校验:BTIF 层首先校验模块状态(是否已禁用 / 禁用中)和设备状态(是否存在、是否已连接 / 连接中),避免无效操作。
-
异步传递:通过
btif_transfer_context
将断开请求(BTIF_HH_DISCONNECT_REQ_EVT
)异步传递至 BTIF 线程,确保操作在统一上下文执行。
1.2 BTIF 层发起断开(BTIF 层:btif_hh_handle_evt/btif_hh_disconnect)
-
事件处理:BTIF 线程接收
BTIF_HH_DISCONNECT_REQ_EVT
后,调用btif_hh_disconnect
触发断开。 -
底层接口调用:通过
BTA_HhClose
通知 BTA 层执行断开,传递设备句柄(dev_handle
)。
1.3 BTA 层状态机驱动(BTA 层:BTA_HhClose→状态机→bta_hh_api_disc_act)
-
事件封装:BTA 层将断开请求封装为
BTA_HH_API_CLOSE_EVT
事件,通过消息队列传递至状态机。 -
设备类型分发:根据设备类型(LE / 传统蓝牙)调用
bta_hh_le_api_disc_act
或直接调用 HID 协议栈接口HID_HostCloseDev
执行物理断连。
1.4 HID 协议栈执行物理断开(HID 协议栈:HID_HostCloseDev→hidh_conn_disconnect)
-
多级校验:校验模块注册状态、设备句柄有效性、连接状态,确保断开操作仅作用于合法已连接设备。
-
物理断连:关闭 L2CAP 控制 / 中断通道(优先断开中断通道),设置 ACL 链路空闲超时为 0 触发立即断开,清理定时器并重试标志。
1.5 BTA 层处理断开完成事件(BTA 层:bta_hh_cback→状态机→bta_hh_close_act)
-
事件转换:HID 协议栈通过
HID_HDEV_EVT_CLOSE
通知断开完成,BTA 层将其转换为状态机事件BTA_HH_INT_CLOSE_EVT
。 -
资源清理与通知:解析断开原因(L2CAP 层 / 协议层错误码),更新连接计数,通知角色管理器协同清理,触发上层回调(
BTA_HH_CLOSE_EVT
)。
1.6 BTIF 层同步状态并通知应用层(BTIF 层:bte_hh_evt→btif_hh_upstreams_evt)
-
上下文转移:BTA 层通过
bte_hh_evt
将BTA_HH_CLOSE_EVT
事件异步传递至 BTIF 线程。 -
应用层通知:BTIF 层查找目标设备,更新本地状态(
BTHH_CONN_STATE_DISCONNECTED
),通过HAL_CBACK
触发应用层回调,通知设备已断开(预通知→最终通知)。
二、源码解析
disconnect
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function disconnect** Description disconnect from hid device** Returns bt_status_t*******************************************************************************/
static bt_status_t disconnect(RawAddress* bd_addr) {CHECK_BTHH_INIT();log::verbose("BTHH");btif_hh_device_t* p_dev;tAclLinkSpec link_spec;// 1. 状态校验(模块级)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_UNHANDLED;}// 2. 设备存在性检查link_spec.addrt.bda = *bd_addr;// Todo: fill with params receivedlink_spec.addrt.type = BLE_ADDR_PUBLIC;link_spec.transport = BT_TRANSPORT_AUTO;p_dev = btif_hh_find_connected_dev_by_bda(link_spec);if (!p_dev) {log::error("Error, device {} not opened.",ADDRESS_TO_LOGGABLE_CSTR(*bd_addr));return BT_STATUS_UNHANDLED;}// 3. 设备状态校验(设备级)if (p_dev->dev_status == BTHH_CONN_STATE_DISCONNECTED ||p_dev->dev_status == BTHH_CONN_STATE_DISCONNECTING) {log::error("Error, device {} already disconnected.",ADDRESS_TO_LOGGABLE_CSTR(*bd_addr));return BT_STATUS_DONE; // 已断开或断开中,无需操作} else if (p_dev->dev_status == BTHH_CONN_STATE_CONNECTING) {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_DISCONNECT_REQ_EVT,(char*)&link_spec, sizeof(tAclLinkSpec), NULL);
}
蓝牙 HID Host 模块中断开 HID 设备连接的核心接口,主要负责连接状态校验、设备存在性检查,并通过异步机制触发后续断开操作。核心流程:
btif_transfer_context(BTIF_HH_DISCONNECT_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_DISCONNECT_REQ_EVT: {log::debug("Disconnect request received remote:{}",ADDRESS_TO_LOGGABLE_CSTR((*p_link_spec)));btif_hh_disconnect(p_link_spec);HAL_CBACK(bt_hh_callbacks, connection_state_cb, &p_link_spec->addrt.bda,BTHH_CONN_STATE_DISCONNECTING);} break;...
btif_hh_disconnect
packages/modules/Bluetooth/system/btif/src/btif_hh.cc
/********************************************************************************* Function btif_hh_disconnect** Description disconnection initiated from the BTIF thread context** Returns void*******************************************************************************/
void btif_hh_disconnect(tAclLinkSpec* link_spec) {CHECK(link_spec != nullptr);const btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_bda(*link_spec);if (p_dev == nullptr) {log::debug("Unable to disconnect unknown HID device:{}",ADDRESS_TO_LOGGABLE_CSTR((*link_spec)));return;}log::debug("Disconnect and close request for HID device:{}",ADDRESS_TO_LOGGABLE_CSTR((*link_spec)));BTA_HhClose(p_dev->dev_handle);
}
校验设备存在性并调用底层接口(BTA_HhClose
)触发物理层断开,确保断开操作的合法性和有序性。
BTA_HhClose
/packages/modules/Bluetooth/system/bta/hh/bta_hh_api.cc
/********************************************************************************* Function BTA_HhClose** Description Disconnect a connection.** Returns void*******************************************************************************/
void BTA_HhClose(uint8_t dev_handle) {BT_HDR* p_buf = (BT_HDR*)osi_calloc(sizeof(BT_HDR));p_buf->event = BTA_HH_API_CLOSE_EVT; // 事件类型:API 层断开请求p_buf->layer_specific = (uint16_t)dev_handle;bta_sys_sendmsg(p_buf);
}
蓝牙 HID Host 模块中BTA 层的断开连接接口,其核心作用是将上层(如 BTIF 模块)的断开请求封装为事件消息,并通过系统消息队列异步传递给 HID 状态机,触发实际的断开逻辑。
bta_hh_better_state_machine(BTA_HH_API_CLOSE_EVT)
...case BTA_HH_CONN_ST:switch (event) {case BTA_HH_API_CLOSE_EVT:bta_hh_api_disc_act(p_cb, p_data);break;...
bta_hh_api_disc_act
packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* Function bta_hh_api_disc_act** Description HID Host initiate a disconnection.*** Returns void*******************************************************************************/
void btif_hh_remove_device(tAclLinkSpec link_spec);
void bta_hh_api_disc_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {CHECK(p_cb != nullptr);// 设备类型判断(LE vs 传统蓝牙)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);} else { // 传统蓝牙设备断开逻辑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));}tBTA_HH bta_hh = {.dev_status = {.status =(status == HID_SUCCESS) ? BTA_HH_OK : BTA_HH_ERR,.handle = hid_handle},};(*bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, &bta_hh);}
}
根据设备类型(LE 或传统蓝牙)分发断开请求,调用底层协议栈接口关闭连接,并通过回调通知上层断开结果。
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;// 5. 执行底层断开操作return hidh_conn_disconnect(dev_handle);
}
负责多级状态校验和连接资源清理,确保断开操作仅作用于合法、已连接的设备。
核心逻辑可概括为:校验模块状态 → 校验设备句柄 → 校验连接状态 → 清理准备 → 执行断开。
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)) { // 存在有效通道,执行断开逻辑// 3. 标记断开中状态p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;// 4. 设置 ACL 链路立即断开/* 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);// 5. 断开 L2CAP 通道(中断→控制)/* 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;
}
负责关闭控制 / 中断通道、释放 ACL 链路并更新连接状态。
调用 L2CAP 接口 L2CA_SetIdleTimeoutByBdAddr
,将目标设备的 ACL 链路空闲超时时间设为 0。
作用:ACL 链路是 L2CAP 通道的物理承载,当最后一个 L2CAP 通道关闭后,空闲超时时间为 0 可触发 ACL 链路立即断开,避免残留的物理连接占用资源。
优先断开中断通道:HID 设备通常使用两个 L2CAP 通道:
-
控制通道(
ctrl_cid
):用于传输 HID 协议命令(如设备配置)。 -
中断通道(
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. 查找设备句柄(dhandle)/* 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;if (l2cap_cid == p_hcon->ctrl_cid) {p_hcon->ctrl_cid = 0; // 控制通道断开,清除 CID} else {p_hcon->intr_cid = 0; // 中断通道断开,清除 CIDif (p_hcon->ctrl_cid) { // 若控制通道仍存在,主动断开log::verbose("HID-Host Initiating L2CAP Ctrl disconnection");L2CA_DisconnectReq(p_hcon->ctrl_cid);p_hcon->ctrl_cid = 0;}}// 4. 所有通道断开后的状态更新与通知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 断开 → 查找设备 → 清理通道状态 → 同步断开关联通道 → 更新状态并通知上层。
bta_hh_cback(HID_HDEV_EVT_CLOSE)
packages/modules/Bluetooth/system/bta/hh/bta_hh_act.cc
/********************************************************************************* 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) {uint16_t sm_event = BTA_HH_INVALID_EVT;uint8_t xx = 0;log::verbose("HID_event [{}]", bta_hh_hid_event_name(event));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;}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);}
}
HID_HDEV_EVT_CLOSE
事件的处理是蓝牙 HID Host 断开流程中底层协议栈向 BTA 层传递断开完成信号的关键环节。其核心作用是将 HID 协议栈的断开完成事件(HID_HDEV_EVT_CLOSE
)转换为 BTA 层状态机可识别的内部事件(BTA_HH_INT_CLOSE_EVT
),并通过消息机制触发 BTA 层的状态更新和上层通知。
bta_hh_better_state_machine(BTA_HH_INT_CLOSE_EVT)
...case BTA_HH_CONN_ST:switch (event) {...case BTA_HH_INT_CLOSE_EVT:p_cb->state = BTA_HH_IDLE_ST;bta_hh_close_act(p_cb, p_data);break;...
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); // HID状态码(低8位)// 2. 确定事件类型(虚拟拔插 vs 正常断开)/* 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()));// 5. 通知角色管理器(资源协同清理)/* inform role manager */bta_sys_conn_close(BTA_ID_HH, p_cb->app_id, p_cb->link_spec.addrt.bda);// 6. 更新连接计数(系统状态跟踪)/* update total conn number */bta_hh_cb.cnt_num--;if (disc_dat.status) disc_dat.status = BTA_HH_ERR; // 非零状态标记为错误// 7. 触发上层回调(状态同步关键)(*bta_hh_cb.p_cback)(event, (tBTA_HH*)&disc_dat);// 8. 虚拟拔插处理(彻底移除设备)/* if virtually unplug, remove device */if (p_cb->vp) {HID_HostRemoveDev(p_cb->hid_handle); // 从HID协议栈移除设备bta_hh_clean_up_kdev(p_cb); // 清理BTA层的设备控制块}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. 检查是否禁用服务(资源回收)/* 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;
}
解析断开原因、通知上层状态变化并清理连接资源,确保断开操作的完整闭环和系统资源的有效管理。
核心逻辑可概括为:解析原因→分类事件→记录日志→协同清理→通知上层→资源回收。
核心流程:
bta_sys_conn_close处理逻辑见【Bluedroid】蓝牙HID Device disconnect流程源码分析-CSDN博客
bta_hh_disc_cmpl处理流程见【Bluedroid】 蓝牙HID Device register_app流程源码解析_human interface device可以禁用吗-CSDN博客
(*bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT)
bte_hh_evt(BTA_HH_CLOSE_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;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;/* switch context to btif task context (copy full union size for convenience)*/// 上下文转移(BTA → BTIF 线程)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);
}
BTA_HH_CLOSE_EVT
事件的处理是蓝牙 HID 断开流程中BTA 层向 BTIF 层(接口层)传递断开事件的关键环节。其核心作用是将 BTA 层的断开完成事件(BTA_HH_CLOSE_EVT
)转换为 BTIF 层可处理的上下文,并通过线程切换确保事件在 BTIF 任务上下文中执行,最终通知应用层设备已断开。
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);// 1. 查找设备对象(定位目标设备)p_dev = btif_hh_find_connected_dev_by_handle(p_data->dev_status.handle);if (p_dev != NULL) {// 2. 触发 “断开中” HAL 回调(应用层预通知)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);// 3. 停止虚拟拔插定时器(避免残留事件)btif_hh_stop_vup_timer(&(p_dev->link_spec));// 4. 处理本地虚拟拔插或服务变更(特殊场景清理)/* 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;BTA_DmRemoveDevice(p_dev->link_spec.addrt.bda); // 移除设备绑定} else if (p_data->dev_status.status == BTA_HH_HS_SERVICE_CHANGED) {// 断开原因是 HID 设备的服务变更(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); // 清除HID描述符缓存}// 5. 更新 BTIF 层设备状态(内部状态同步)btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_DISCONNECTED;p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;// 6. 关闭协同连接(多协议清理)bta_hh_co_close(p_dev);//7. 触发 “已断开” HAL 回调(应用层最终通知)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;...
BTA_HH_CLOSE_EVT
事件的处理是蓝牙 HID 断开流程的最终环节,负责同步 BTIF 层设备状态并通知应用层设备已断开。核心作用是将 BTA 层传递的断开事件转换为应用层可感知的状态变化,确保整个系统状态的一致性。
其核心逻辑可概括为:定位设备→预通知应用层→清理特殊场景资源→同步内部状态→关闭协同连接→最终通知应用层。BTIF 层实现了从协议栈到应用层的状态同步,确保断开操作的完整性和用户体验的一致性。
三、时序图
四、总结
蓝牙 HID Host 模块的断开流程是多层级协作、状态同步和资源管理的典型案例,核心设计特点如下:
-
多层校验机制:从 BTIF 层的设备存在性检查,到 HID 协议栈的句柄 / 状态校验,确保断开操作的合法性。
-
异步事件传递:通过
btif_transfer_context
和消息队列,避免跨线程并发问题,保证操作原子性。 -
资源高效清理:关闭 L2CAP 通道、设置 ACL 链路立即断开、清理虚拟拔插定时器,避免资源残留。
-
状态同步闭环:通过两次应用层回调(断开中→已断开),确保用户及时感知状态变化,提升体验一致性。
流程通过严格的状态管理和层级协作,为蓝牙 HID 设备的稳定断开提供了关键保障。