本文基于 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 設備的穩定斷開提供了關鍵保障。