本文圍繞藍牙 HID(人機接口設備)服務注冊流程,詳細解析從 HID 服務啟用、設備初始化、L2CAP 通道注冊到 SDP(服務發現協議)記錄構建的全流程。通過分析關鍵函數如btif_hd_service_registration
、BTA_HdEnable
、HID_DevRegister
及HID_DevAddRecord
的邏輯,揭示了 HID 設備如何通過協議棧完成注冊、配置及發現功能,確保設備可被其他藍牙主機識別和連接。結合Android Bluedroid代碼邏輯與協議規范,闡述了狀態機管理、內存分配、安全配置及數據傳輸的核心機制。
一、概述
藍牙 HID 設備(如鍵盤、鼠標)的注冊與啟用流程是藍牙協議棧中實現人機交互功能的核心部分。通過分析 Bluedroid 協議棧中的相關代碼,詳細闡述 HID 設備如何通過 SDP(服務發現協議)暴露自身服務、如何配置 L2CAP 通道以確保數據傳輸的可靠性,以及如何管理 HID 描述符以實現與主機的兼容通信。
1.1 HID 服務注冊入口:btif_hd_service_registration
作為 HID 服務注冊的入口,通過調用BTA_HdEnable
觸發協議棧的 HID 服務初始化。核心邏輯包括:
-
檢查回調函數指針
bt_hd_callbacks
是否有效,確保上層應用可接收狀態通知。 -
向 BTA(藍牙協議棧適配層)發送啟用事件
BTA_HD_API_ENABLE_EVT
,啟動后續注冊流程。
1.2 協議棧初始化:BTA_HdEnable
與bta_hd_api_enable
-
系統注冊:通過
bta_sys_register
將 HID 服務注冊到藍牙系統層,關聯事件處理函數bta_hd_hdl_event
和禁用函數BTA_HdDisable
。 -
消息傳遞:構造并發送啟用事件消息,觸發
bta_hd_api_enable
執行初始化:-
調用
HID_DevInit
清零全局控制塊hd_cb
,確保初始狀態正確。 -
通過
HID_DevRegister
將 HID 設備注冊到 L2CAP 層,完成控制通道(PSM=0x1124)和中斷通道(PSM=0x1125)的注冊,配置 MTU 及安全級別(認證與加密)。
-
1.3 L2CAP 通道注冊:hidd_conn_reg
與協議配置
-
雙通道注冊:
-
控制通道:用于傳輸配置命令,注冊時指定安全級別
BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT
。 -
中斷通道:用于實時數據傳輸(如按鍵事件),確保低延遲。
-
-
虛擬 PSM 機制:對于動態 PSM 場景,通過
L2CA_Register
分配虛擬 PSM,避免端口沖突,增強協議棧靈活性。
1.4 SDP 記錄構建:HID_DevAddRecord
的核心作用
負責生成 HID 設備的 SDP 記錄,確保設備可被其他主機發現:
-
必選屬性:
-
服務 UUID:聲明支持 HID 服務(
UUID_SERVCLASS_HUMAN_INTERFACE
)。 -
協議描述符:指定 L2CAP 和 HIDP 協議,配置控制通道與中斷通道的 PSM。
-
配置文件版本:聲明 HID 協議版本(如 1.0),確保兼容性。
-
-
設備元數據:填充設備名稱、描述、提供商等信息,顯示于藍牙設備列表。
-
HID 特定屬性:
-
報告描述符:定義設備功能(如按鍵、坐標軸),遵循 HID 規范,是主機解析數據的關鍵。
-
子類與國家代碼:標識設備類型(如鍵盤、鼠標)和區域配置。
-
電源與連接策略:設置電池供電狀態、支持重新連接等,優化設備管理。
-
1.5 狀態回調與資源管理
-
事件傳遞:通過
bte_hd_evt
和btif_hd_upstreams_evt
完成協議棧(BTE)與接口層(BTIF)的事件上下文切換,確保回調在正確層處理。 -
內存釋放:
btif_hd_free_buf
清理注冊過程中分配的動態內存(如描述符、字符串),避免泄漏,保障系統穩定性。
二、源碼分析
btif_hd_service_registration
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
static bthd_callbacks_t* bt_hd_callbacks = NULL;/********************************************************************************* Function btif_hd_service_registration** Description Registers hid device service** Returns none*******************************************************************************/
void btif_hd_service_registration() {log::verbose("");/* enable HD */if (bt_hd_callbacks != NULL) {BTA_HdEnable(bte_hd_evt);}
}
注冊 HID(Human Interface Device,人機接口設備)設備服務。若滿足特定條件,會調用 BTA_HdEnable
函數來啟用 HID 設備服務。
BTA_HdEnable
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
static const tBTA_SYS_REG bta_hd_reg = {bta_hd_hdl_event, BTA_HdDisable};/********************************************************************************* Function BTA_HdEnable** Description Enables HID device** Returns void*******************************************************************************/
void BTA_HdEnable(tBTA_HD_CBACK* p_cback) {log::verbose("");// 1. 系統注冊bta_sys_register(BTA_ID_HD, &bta_hd_reg);// 2. 內存分配與初始化tBTA_HD_API_ENABLE* p_buf =(tBTA_HD_API_ENABLE*)osi_malloc((uint16_t)sizeof(tBTA_HD_API_ENABLE));memset(p_buf, 0, sizeof(tBTA_HD_API_ENABLE));// 3. 設置消息內容p_buf->hdr.event = BTA_HD_API_ENABLE_EVT;p_buf->p_cback = p_cback;// 4. 發送消息bta_sys_sendmsg(p_buf);
}
啟用 HID設備。在系統中注冊 HID 設備相關的處理信息,并且發送一個啟用事件消息,以此來觸發后續的處理流程。
bta_hd_hdl_event(BTA_HD_API_ENABLE_EVT)
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function bta_hd_hdl_event** Description HID device main event handling function.** Returns void*******************************************************************************/
bool bta_hd_hdl_event(const BT_HDR_RIGID* p_msg) {log::verbose("p_msg->event={}", p_msg->event);switch (p_msg->event) {case BTA_HD_API_ENABLE_EVT:bta_hd_api_enable((tBTA_HD_DATA*)p_msg);break;case BTA_HD_API_DISABLE_EVT:if (bta_hd_cb.state == BTA_HD_CONN_ST) {log::warn("host connected, disconnect before disabling");// unregister (and disconnect)bta_hd_cb.disable_w4_close = TRUE;bta_hd_better_state_machine(BTA_HD_API_UNREGISTER_APP_EVT,(tBTA_HD_DATA*)p_msg);} else {bta_hd_api_disable();}break;default:bta_hd_better_state_machine(p_msg->event, (tBTA_HD_DATA*)p_msg);}return (TRUE);
}
當接收到的事件類型為 BTA_HD_API_ENABLE_EVT
時,調用 bta_hd_api_enable
函數,將消息指針轉換為 tBTA_HD_DATA
類型后傳入該函數,用于處理 HID 設備的啟用操作。
bta_hd_api_enable
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function bta_hd_api_enable** Description Enables HID device** Returns void*******************************************************************************/
void bta_hd_api_enable(tBTA_HD_DATA* p_data) {tBTA_HD_STATUS status = BTA_HD_ERROR;tHID_STATUS ret;log::verbose("");HID_DevInit(); // HID 設備初始化memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB));/* store parameters */bta_hd_cb.p_cback = p_data->api_enable.p_cback; // 存儲回調函數指針ret = HID_DevRegister(bta_hd_cback); // HID 設備注冊if (ret == HID_SUCCESS) {status = BTA_HD_OK;} else {log::error("Failed to register HID device ({})", ret);}// 觸發回調函數/* signal BTA call back event */tBTA_HD bta_hd;bta_hd.status = status;(*bta_hd_cb.p_cback)(BTA_HD_ENABLE_EVT, &bta_hd);
}
啟用 HID設備。完成一系列的初始化和注冊操作,并在操作完成后通過回調函數通知調用者操作結果。
HID_DevInit
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
tHID_DEV_CTB hd_cb;/********************************************************************************* Function HID_DevInit** Description Initializes control block** Returns void*******************************************************************************/
void HID_DevInit(void) {log::verbose("");memset(&hd_cb, 0, sizeof(tHID_DEV_CTB));
}
對 HID設備的控制塊進行初始化操作。通過將控制塊的內存清零,可以確保控制塊中的各個成員變量處于初始狀態,避免因未初始化的變量值導致的潛在錯誤。
HID_DevRegister
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* Function HID_DevRegister** Description Registers HID device with lower layers** Returns tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK* host_cback) {tHID_STATUS st; // 用于存儲注冊過程中各個步驟的狀態log::verbose("");// 檢查設備是否已注冊if (hd_cb.reg_flag) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_ALREADY_REGISTERED,1);return HID_ERR_ALREADY_REGISTERED;}// 檢查回調函數指針是否為空if (host_cback == NULL) {log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_HOST_CALLBACK_NULL,1);return HID_ERR_INVALID_PARAM;}// 注冊到 L2CAP 層/* Register with L2CAP */st = hidd_conn_reg();if (st != HID_SUCCESS) return st;// 更新控制塊信息hd_cb.callback = host_cback;hd_cb.reg_flag = TRUE;// 清理待處理數據if (hd_cb.pending_data) {osi_free(hd_cb.pending_data);hd_cb.pending_data = NULL;}return (HID_SUCCESS);
}
將 HID設備注冊到下層協議棧中。通過一系列的檢查和操作,確保 HID 設備能夠正確地注冊到下層協議棧中。在注冊過程中,會檢查設備是否已注冊、回調函數指針是否有效,并將設備注冊到 L2CAP 層。注冊成功后,會更新控制塊信息并清理待處理數據。
hidd_conn_reg
packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
/********************************************************************************* Function hidd_conn_reg** Description Registers L2CAP channels** Returns void*******************************************************************************/
tHID_STATUS hidd_conn_reg(void) {log::verbose("");// 1. L2CAP 配置信息初始化memset(&hd_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));hd_cb.l2cap_cfg.mtu_present = TRUE;hd_cb.l2cap_cfg.mtu = HID_DEV_MTU_SIZE;memset(&hd_cb.l2cap_intr_cfg, 0, sizeof(tL2CAP_CFG_INFO));hd_cb.l2cap_intr_cfg.mtu_present = TRUE;hd_cb.l2cap_intr_cfg.mtu = HID_DEV_MTU_SIZE;// 2. 注冊控制通道if (!L2CA_Register2(HID_PSM_CONTROL, dev_reg_info, false /* enable_snoop */,nullptr, HID_DEV_MTU_SIZE, 0,BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) {log::error("HID Control (device) registration failed");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_CONTROL,1);return (HID_ERR_L2CAP_FAILED);}// 3. 注冊中斷通道if (!L2CA_Register2(HID_PSM_INTERRUPT, dev_reg_info, false /* enable_snoop */,nullptr, HID_DEV_MTU_SIZE, 0,BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) {L2CA_Deregister(HID_PSM_CONTROL);log::error("HID Interrupt (device) registration failed");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_INTERRUPT,1);return (HID_ERR_L2CAP_FAILED);}return (HID_SUCCESS);
}
注冊 HID 設備的 L2CAP(Logical Link Control and Adaptation Protocol,邏輯鏈路控制和適配協議)通道,包括控制通道和中斷通道。若注冊過程中出現錯誤,返回相應的錯誤狀態;若所有通道都成功注冊,則返回成功狀態。確保 HID 設備的 L2CAP 通道能夠正確注冊,為后續的數據傳輸奠定基礎。
L2CA_Register2
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
uint16_t L2CA_Register2(uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info,bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info,uint16_t my_mtu, uint16_t required_remote_mtu,uint16_t sec_level) {auto ret = L2CA_Register(psm, p_cb_info, enable_snoop, p_ertm_info, my_mtu,required_remote_mtu, sec_level);get_btm_client_interface().security.BTM_SetSecurityLevel(false, "", 0, sec_level, psm, 0, 0);return ret;
}
L2CAP層的注冊函數,用于將 HID 設備的控制通道或中斷通道注冊到藍牙協議棧中。在 L2CA_Register
基礎上增加了安全級別設置邏輯,確保注冊的通道具備指定的安全屬性(如認證、加密)。
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
/********************************************************************************* Function L2CA_Register** Description Other layers call this function to register for L2CAP* services.** Returns PSM to use or zero if error. Typically, the PSM returned* is the same as was passed in, but for an outgoing-only* connection to a dynamic PSM, a "virtual" PSM is returned* and should be used in the calls to L2CA_ConnectReq(),* L2CA_ErtmConnectReq() and L2CA_Deregister()*******************************************************************************/
uint16_t L2CA_Register(uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info,bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info,uint16_t my_mtu, uint16_t required_remote_mtu,uint16_t sec_level) {// 檢查必填回調函數是否存在const bool config_cfm_cb = (p_cb_info.pL2CA_ConfigCfm_Cb != nullptr);const bool config_ind_cb = (p_cb_info.pL2CA_ConfigInd_Cb != nullptr);const bool data_ind_cb = (p_cb_info.pL2CA_DataInd_Cb != nullptr);const bool disconnect_ind_cb = (p_cb_info.pL2CA_DisconnectInd_Cb != nullptr);tL2C_RCB* p_rcb;uint16_t vpsm = psm; // 虛擬 PSM,默認為傳入的 PSM/* Verify that the required callback info has been filled in** Note: Connection callbacks are required but not checked** for here because it is possible to be only a client** or only a server.*/// 驗證必填回調函數(配置確認、數據指示、斷開指示)if (!config_cfm_cb || !data_ind_cb || !disconnect_ind_cb) {log::error("L2CAP - no cb registering PSM: 0x{:04x} cfg_cfm:{} cfg_ind:{} ""data_ind:{} discon_int:{}",psm, config_cfm_cb, config_ind_cb, data_ind_cb, disconnect_ind_cb);return (0);}// 驗證 PSM 是否有效(非保留值)/* Verify PSM is valid */if (L2C_INVALID_PSM(psm)) {log::error("L2CAP - invalid PSM value, PSM: 0x{:04x}", psm);return (0);}/* Check if this is a registration for an outgoing-only connection to *//* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */// 處理僅出站連接的動態 PSM 場景(需傳入 ConnectInd 回調為 NULL)if ((psm >= 0x1001) && (p_cb_info.pL2CA_ConnectInd_Cb == NULL)) {for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2) {p_rcb = l2cu_find_rcb_by_psm(vpsm); // 找到未占用的 PSMif (p_rcb == NULL) break;}log::debug("L2CAP - Real PSM: 0x{:04x} Virtual PSM: 0x{:04x}", psm, vpsm);}/* If registration block already there, just overwrite it */// 查找或創建資源控制塊(RCB)p_rcb = l2cu_find_rcb_by_psm(vpsm);if (p_rcb == NULL) {p_rcb = l2cu_allocate_rcb(vpsm); // 分配新的 RCBif (p_rcb == NULL) {log::warn("L2CAP - no RCB available, PSM: 0x{:04x} vPSM: 0x{:04x}", psm,vpsm);return (0);}}log::info("L2CAP Registered service classic PSM: 0x{:04x}", psm);// 配置 RCB 參數p_rcb->log_packets = enable_snoop; // 是否啟用數據包監聽p_rcb->api = p_cb_info; // 應用層回調函數p_rcb->real_psm = psm; // 真實 PSM(用于區分虛擬 PSM)p_rcb->ertm_info = p_ertm_info ?: tL2CAP_ERTM_INFO{L2CAP_FCR_BASIC_MODE}; // ERTM 模式(默認基本模式)p_rcb->my_mtu = my_mtu; // 本地 MTUp_rcb->required_remote_mtu = std::max(required_remote_mtu, L2CAP_MIN_MTU); // 對端最小 MTU(不低于協議最小值)return (vpsm);
}
L2CAP 服務注冊的核心入口,為 HID 設備的控制通道和中斷通道注冊提供了底層支持,是藍牙協議棧中服務發現和數據傳輸的基礎。通過虛擬 PSM 機制,它還支持動態創建僅出站連接的服務,增強了協議棧的靈活性。主要完成以下任務:
-
確保應用層提供必要的回調函數;
-
管理 PSM 資源(包括虛擬 PSM 分配);
-
初始化通道配置(MTU、ERTM 等)。
BTM_SetSecurityLevel
packages/modules/Bluetooth/system/stack/btm/btm_sec.cc
/********************************************************************************* Function BTM_SetSecurityLevel** Description Register service security level with Security Manager** Parameters: is_originator - true if originating the connection* p_name - Name of the service relevant only if* authorization will show this name to user.* Ignored if BT_MAX_SERVICE_NAME_LEN is 0.* service_id - service ID for the service passed to* authorization callback* sec_level - bit mask of the security features* psm - L2CAP PSM* mx_proto_id - protocol ID of multiplexing proto below* mx_chan_id - channel ID of multiplexing proto below** Returns true if registered OK, else false*******************************************************************************/
bool BTM_SetSecurityLevel(bool is_originator, const char* p_name,uint8_t service_id, uint16_t sec_level, uint16_t psm,uint32_t mx_proto_id, uint32_t mx_chan_id) {return btm_sec_cb.AddService(is_originator, p_name, service_id, sec_level,psm, mx_proto_id, mx_chan_id);
}
BTM_SetSecurityLevel分析見???????【Bluedroid】藍牙 SDP(服務發現協議)模塊代碼解析與流程梳理-CSDN博客
處理結果回調:bte_hd_evt(BTA_HD_ENABLE_EVT)
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function bte_hd_evt** Description Switches context from BTE to BTIF for all BT-HD events** Returns void*******************************************************************************/static void bte_hd_evt(tBTA_HD_EVT event, tBTA_HD* p_data) {bt_status_t status;int param_len = 0;tBTIF_COPY_CBACK* p_copy_cback = NULL; // 數據復制回調,用于處理需要跨層傳遞的緩沖區log::verbose("event={}", event);// 事件類型與參數長度匹配switch (event) {case BTA_HD_ENABLE_EVT:case BTA_HD_DISABLE_EVT:case BTA_HD_UNREGISTER_APP_EVT:param_len = sizeof(tBTA_HD_STATUS);break;case BTA_HD_REGISTER_APP_EVT:param_len = sizeof(tBTA_HD_REG_STATUS);break;case BTA_HD_OPEN_EVT:case BTA_HD_CLOSE_EVT:case BTA_HD_VC_UNPLUG_EVT:case BTA_HD_CONN_STATE_EVT:param_len = sizeof(tBTA_HD_CONN);break;case BTA_HD_GET_REPORT_EVT:param_len += sizeof(tBTA_HD_GET_REPORT);break;case BTA_HD_SET_REPORT_EVT:param_len = sizeof(tBTA_HD_SET_REPORT) + p_data->set_report.len;p_copy_cback = set_report_copy_cb;break;case BTA_HD_SET_PROTOCOL_EVT:param_len += sizeof(p_data->set_protocol);break;case BTA_HD_INTR_DATA_EVT:param_len = sizeof(tBTA_HD_INTR_DATA) + p_data->intr_data.len;p_copy_cback = intr_data_copy_cb;break;}// 上下文切換status = btif_transfer_context(btif_hd_upstreams_evt, (uint16_t)event,(char*)p_data, param_len, p_copy_cback);ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
}
bte_hd_evt
是藍牙協議棧中 BTE(藍牙傳輸層)與 BTIF(藍牙接口層)之間的事件橋梁,主要職責是:
-
解析 HID 設備事件:根據事件類型確定參數長度和數據復制方式。
-
上下文切換:通過
btif_transfer_context
將事件從 BTE 層傳遞到 BTIF 層,確保事件在正確的任務上下文執行。 -
資源管理:處理需要復制的緩沖區(如報告數據、中斷數據),避免跨層訪問時的內存問題。
btif_hd_upstreams_evt(BTA_HD_ENABLE_EVT)
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function btif_hd_upstreams_evt** Description Executes events in btif context** Returns void*******************************************************************************/
static void btif_hd_upstreams_evt(uint16_t event, char* p_param) {tBTA_HD* p_data = (tBTA_HD*)p_param;log::verbose("event={}", dump_hd_event(event));switch (event) {case BTA_HD_ENABLE_EVT:log::verbose("status={}", p_data->status);if (p_data->status == BTA_HD_OK) {btif_storage_load_hidd(); // 從存儲中加載 HID 設備的相關信息btif_hd_cb.status = BTIF_HD_ENABLED;/* Register the app if not yet registered */if (!btif_hd_cb.app_registered) { // 檢查應用程序是否已經注冊BTA_HdRegisterApp(&app_info, &in_qos, &out_qos);btif_hd_free_buf();}} else { // 啟用失敗的處理btif_hd_cb.status = BTIF_HD_DISABLED;log::warn("Failed to enable BT-HD, status={}", p_data->status);}break;...
在 BTIF(Bluetooth Interface,藍牙接口)上下文環境中處理從 BTE傳遞過來的 HID相關事件。根據不同的事件類型執行相應的操作,以實現對 HID 設備狀態的管理和功能的控制。在處理 BTA_HD_ENABLE_EVT
事件時,會根據事件的狀態進行不同的處理,包括加載設備信息、注冊應用程序、釋放緩沖區等操作。
btif_storage_load_hidd
packages/modules/Bluetooth/system/btif/src/btif_profile_storage.cc
/******************************************************************************** Function btif_storage_load_hidd** Description Loads hidd bonded device and "plugs" it into hidd** Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise*******************************************************************************/
bt_status_t btif_storage_load_hidd(void) {// 遍歷所有已配對的藍牙設備地址for (const auto& bd_addr : btif_config_get_paired_devices()) {auto name = bd_addr.ToString();log::verbose("Remote device:{}", ADDRESS_TO_LOGGABLE_CSTR(bd_addr));int value;// 檢查設備是否為已綁定的 HID 設備,并獲取其有線狀態標記if (btif_in_fetch_bonded_device(name) == BT_STATUS_SUCCESS) { // 檢查設備是否為已綁定狀態(即是否完成安全認證)if (btif_config_get_int(name, BTIF_STORAGE_KEY_HID_DEVICE_CABLED, &value)) {// 若標記存在(value 非零),則認為是 HID 設備,添加到 HID 服務BTA_HdAddDevice(bd_addr); break; // 僅處理第一個匹配的設備(可能假設單設備場景)}}}return BT_STATUS_SUCCESS; // 無論是否找到設備,均返回成功(遍歷邏輯無失敗條件)
}
從本地存儲中加載已綁定的 HID 設備信息,并將其 “虛擬插入” 到 HID 設備服務(HIDD)中,實現設備的自動連接或狀態恢復。具體流程包括:
-
遍歷所有已配對的藍牙設備。
-
篩選出標記為 有線 HID 設備(通過存儲鍵
BTIF_STORAGE_KEY_HID_DEVICE_CABLED
識別)。 -
調用
BTA_HdAddDevice
將設備添加到 HID 服務中,觸發協議棧的連接或配置流程。
BTA_HdAddDevice
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function BTA_HdAddDevice** Description This function is called when a device is virtually cabled** Returns void*******************************************************************************/
void BTA_HdAddDevice(const RawAddress& addr) {log::verbose("");tBTA_HD_DEVICE_CTRL* p_buf =(tBTA_HD_DEVICE_CTRL*)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL));p_buf->hdr.event = BTA_HD_API_ADD_DEVICE_EVT;p_buf->addr = addr;bta_sys_sendmsg(p_buf);
}
將一個虛擬連接的 HID 設備添加到藍牙協議棧的 HID 服務(BTA_HD)中。這里的 “虛擬連接” 通常指設備通過存儲標記(如 BTIF_STORAGE_KEY_HID_DEVICE_CABLED
)被識別為需要自動管理的設備,而非物理有線連接。通過構造并發送特定事件消息,通知 HID 服務層執行設備添加流程,包括嘗試建立連接、配置通道等操作。該函數是 HID 設備從上層配置(如存儲標記)到底層協議棧交互的關鍵橋梁,確保設備狀態能夠被藍牙協議棧正確管理,提升用戶體驗的連貫性和設備連接的可靠性。
意義與應用場景
自動設備恢復:在系統啟動或藍牙服務重啟后,通過存儲的設備標記(如
BTIF_STORAGE_KEY_HID_DEVICE_CABLED
)自動添加已配對的 HID 設備,實現 “開機自動連接”。雙模設備支持:對于支持有線和無線模式的 HID 設備,當有線連接斷開時,自動切換到無線連接(通過虛擬插入觸發)。
批量設備管理:在多設備場景中,通過遍歷存儲的設備列表并調用
BTA_HdAddDevice
,實現多個 HID 設備的批量添加(盡管當前代碼僅處理第一個設備)。
bta_hd_hdl_event(BTA_HD_API_ADD_DEVICE_EVT)
當 BTA_HD_API_ADD_DEVICE_EVT
事件被 bta_hd_hdl_event
函數接收后,會觸發以下操作:
bta_hd_better_state_machine(BTA_HD_API_ADD_DEVICE_EVT)
bta_hd_add_device_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function bta_hd_add_device_act** Description** Returns void*******************************************************************************/
void bta_hd_add_device_act(tBTA_HD_DATA* p_data) {tBTA_HD_DEVICE_CTRL* p_ctrl = (tBTA_HD_DEVICE_CTRL*)p_data;log::verbose("");HID_DevPlugDevice(p_ctrl->addr);
}
將目標設備 “虛擬插入” 到 HID 服務中。通過調用底層函數 HID_DevPlugDevice
,觸發對指定藍牙設備的連接嘗試或狀態更新,從而完成設備添加的實際操作。
HID_DevPlugDevice
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
tHID_DEV_CTB hd_cb; // 全局控制塊,存儲 HID 設備的狀態信息/********************************************************************************* Function HID_DevPlugDevice** Description Establishes virtual cable to given host** Returns tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevPlugDevice(const RawAddress& addr) {hd_cb.device.in_use = TRUE;hd_cb.device.addr = addr;return HID_SUCCESS;
}
模擬 HID 設備與目標主機建立 “虛擬連接”(Virtual Cable),本質上是在協議棧層面標記設備為 “正在使用” 并記錄目標主機的藍牙地址。不涉及實際的物理連接或數據傳輸,而是通過更新全局控制塊 hd_cb
的狀態,為后續的連接請求、數據傳輸等操作提供上下文信息。
BTA_HdRegisterApp
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function BTA_HdRegisterApp** Description This function is called when application should be
*registered** Returns void*******************************************************************************/
void BTA_HdRegisterApp(tBTA_HD_APP_INFO* p_app_info, tBTA_HD_QOS_INFO* p_in_qos,tBTA_HD_QOS_INFO* p_out_qos) {log::verbose("");// 為注冊消息分配內存tBTA_HD_REGISTER_APP* p_buf =(tBTA_HD_REGISTER_APP*)osi_malloc(sizeof(tBTA_HD_REGISTER_APP));// 設置消息頭的事件類型為注冊應用事件p_buf->hdr.event = BTA_HD_API_REGISTER_APP_EVT;// 處理應用名稱if (p_app_info->p_name) {strlcpy(p_buf->name, p_app_info->p_name, BTA_HD_APP_NAME_LEN);} else {p_buf->name[0] = '\0';}// 處理應用描述if (p_app_info->p_description) {strlcpy(p_buf->description, p_app_info->p_description,BTA_HD_APP_DESCRIPTION_LEN);} else {p_buf->description[0] = '\0';}// 處理應用提供商if (p_app_info->p_provider) {strlcpy(p_buf->provider, p_app_info->p_provider, BTA_HD_APP_PROVIDER_LEN);} else {p_buf->provider[0] = '\0';}// 復制應用子類信息p_buf->subclass = p_app_info->subclass;// 處理應用描述符if (p_app_info->descriptor.dl_len > BTA_HD_APP_DESCRIPTOR_LEN) {p_app_info->descriptor.dl_len = BTA_HD_APP_DESCRIPTOR_LEN;}p_buf->d_len = p_app_info->descriptor.dl_len;memcpy(p_buf->d_data, p_app_info->descriptor.dsc_list,p_app_info->descriptor.dl_len);// 復制輸入和輸出的 QoS 信息memcpy(&p_buf->in_qos, p_in_qos, sizeof(tBTA_HD_QOS_INFO));memcpy(&p_buf->out_qos, p_out_qos, sizeof(tBTA_HD_QOS_INFO));// 發送注冊消息到藍牙協議棧bta_sys_sendmsg(p_buf);
}
將一個 HID應用程序注冊到藍牙協議棧的 HID 服務中。會收集應用程序的相關信息,包括應用名稱、描述、提供商、子類、描述符以及 QoS(Quality of Service,服務質量)信息等,然后將這些信息封裝成一個消息,通過 bta_sys_sendmsg
發送給藍牙協議棧進行處理。
bta_hd_better_state_machine(BTA_HD_API_REGISTER_APP_EVT)
bta_hd_register_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function bta_hd_register_act** Description Registers SDP record** Returns void*******************************************************************************/
void bta_hd_register_act(tBTA_HD_DATA* p_data) {tBTA_HD ret;tBTA_HD_REGISTER_APP* p_app_data = (tBTA_HD_REGISTER_APP*)p_data;bool use_report_id = FALSE;log::verbose("");ret.reg_status.in_use = FALSE;// 1. 描述符驗證與檢查/* Check if len doesn't exceed BTA_HD_APP_DESCRIPTOR_LEN and descriptor* itself is well-formed. Also check if descriptor has Report Id item so we* know if report will have prefix or not. */if (p_app_data->d_len > BTA_HD_APP_DESCRIPTOR_LEN ||!check_descriptor(p_app_data->d_data, p_app_data->d_len,&use_report_id)) {log::error("Descriptor is too long or malformed");ret.reg_status.status = BTA_HD_ERROR;(*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); // 通知上層注冊失敗bluetooth::shim::CountCounterMetrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_REGISTER_DESCRIPTOR_MALFORMED,1);return;}// 2. 注冊成功處理ret.reg_status.status = BTA_HD_OK;/* Remove old record if for some reason it's already registered */if (bta_hd_cb.sdp_handle != 0) {get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(bta_hd_cb.sdp_handle);}// 更新控制塊狀態bta_hd_cb.use_report_id = use_report_id;bta_hd_cb.sdp_handle = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord(); // 創建新的 SDP 記錄句柄// 向 SDP 記錄中添加 HID 設備信息HID_DevAddRecord(bta_hd_cb.sdp_handle, p_app_data->name,p_app_data->description, p_app_data->provider,p_app_data->subclass, p_app_data->d_len, p_app_data->d_data);// 添加 HID 服務 UUIDbta_sys_add_uuid(UUID_SERVCLASS_HUMAN_INTERFACE);// 3. QoS 參數配置HID_DevSetIncomingQos(p_app_data->in_qos.service_type, p_app_data->in_qos.token_rate,p_app_data->in_qos.token_bucket_size, p_app_data->in_qos.peak_bandwidth,p_app_data->in_qos.access_latency, p_app_data->in_qos.delay_variation);// 設置輸出 QoS 參數(如控制數據傳輸的服務質量)HID_DevSetOutgoingQos(p_app_data->out_qos.service_type, p_app_data->out_qos.token_rate,p_app_data->out_qos.token_bucket_size, p_app_data->out_qos.peak_bandwidth,p_app_data->out_qos.access_latency, p_app_data->out_qos.delay_variation);// 4. 啟用設備連接策略// application is registered so we can accept incoming connectionsHID_DevSetIncomingPolicy(TRUE);// 5. 獲取設備地址并通知上層if (HID_DevGetDevice(&ret.reg_status.bda) == HID_SUCCESS) {ret.reg_status.in_use = TRUE;}(*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); // 回調通知注冊結果
}
bta_hd_register_act
函數是 HID 設備應用注冊的執行核心,主要負責:
-
驗證設備描述符:檢查 HID 描述符的格式和長度是否合法。
-
管理 SDP 記錄:創建、更新或刪除服務發現協議(SDP)記錄,確保設備信息可被其他藍牙設備發現。
-
配置 QoS 參數:設置輸入 / 輸出服務質量參數,保障數據傳輸的穩定性。
-
QoS 作用:
-
輸入 QoS:定義設備接收數據時的帶寬、延遲等參數(如鍵盤按鍵數據的實時性要求)。
-
輸出 QoS:定義設備發送數據時的服務質量(如主機響應控制命令的延遲限制)。
-
-
-
更新設備狀態:通過回調函數通知上層應用注冊結果,并啟用設備的連接接收策略。
意義與最佳實踐
設備發現的基礎:正確的 SDP 記錄是其他設備發現 HID 服務的前提,若描述符錯誤或 SDP 記錄缺失,設備將無法被搜索到。
實時性保障:QoS 參數的合理配置(如低延遲、高優先級)對鍵盤、鼠標等實時性要求高的設備至關重要,否則可能導致輸入滯后。
狀態機管理:通過
in_use
標志和sdp_handle
確保設備注冊狀態的唯一性,避免重復注冊或資源泄漏。
SDP_CreateRecord
SDP_DeleteRecord和SDP_CreateRecord分析見??????????????【Bluedroid】藍牙 SDP(服務發現協議)模塊代碼解析與流程梳理-CSDN博客
HID_DevAddRecord
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* Function HID_DevAddRecord** Description Creates SDP record for HID device** Returns tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, char* p_description,char* p_provider, uint16_t subclass,uint16_t desc_len, uint8_t* p_desc_data) {bool result = TRUE;log::verbose("");// 1. 添加服務類 UUID(必選)// Service Class ID Listif (result) {uint16_t uuid = UUID_SERVCLASS_HUMAN_INTERFACE; // HID 服務 UUIDresult &= get_legacy_stack_sdp_api()->handle.SDP_AddServiceClassIdList(handle, 1, &uuid); // 添加 UUID 到服務類列表}// 2. 添加協議描述符列表(必選)// Protocol Descriptor Listif (result) {tSDP_PROTOCOL_ELEM proto_list[2];// L2CAP 協議(PSM=BT_PSM_HIDC,控制通道)proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP;proto_list[0].num_params = 1;proto_list[0].params[0] = BT_PSM_HIDC; // HID 控制通道 PSM(0x1124)// HIDP 協議(無參數)proto_list[1].protocol_uuid = UUID_PROTOCOL_HIDP;proto_list[1].num_params = 0;// 添加協議列表到 SDP 記錄result &= get_legacy_stack_sdp_api()->handle.SDP_AddProtocolList(handle, 2, proto_list);}// 3. 添加語言屬性(可選)// Language Base Attribute ID Listif (result) {result &= get_legacy_stack_sdp_api()->handle.SDP_AddLanguageBaseAttrIDList(handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8,LANGUAGE_BASE_ID); // 設置語言為英語(UTF-8 編碼)}// 4. 添加附加協議描述符(可選)// Additional Protocol Descriptor Listif (result) {tSDP_PROTO_LIST_ELEM add_proto_list;add_proto_list.num_elems = 2;// L2CAP 協議(PSM=BT_PSM_HIDI,中斷通道)add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP;add_proto_list.list_elem[0].num_params = 1;add_proto_list.list_elem[0].params[0] = BT_PSM_HIDI;// HIDP 協議(無參數)add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_HIDP;add_proto_list.list_elem[1].num_params = 0;// 添加附加協議列表(用于中斷數據傳輸)result &= get_legacy_stack_sdp_api()->handle.SDP_AddAdditionProtoLists(handle, 1, &add_proto_list);}// 5. 添加設備元數據(可選) // Service Name (O)// Service Description (O)// Provider Name (O)if (result) {const char* srv_name = p_name;const char* srv_desc = p_description;const char* provider_name = p_provider;// 添加服務名稱result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, strlen(srv_name) + 1,(uint8_t*)srv_name);// 添加服務描述result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE,strlen(srv_desc) + 1, (uint8_t*)srv_desc);// 添加提供商名稱result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE,strlen(provider_name) + 1, (uint8_t*)provider_name);}// 6. 添加配置文件描述符(必選)// Bluetooth Profile Descriptor Listif (result) {const uint16_t profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE; // HID 配置文件 UUIDconst uint16_t version = 0x0100; // HID 配置文件版本 1.0result &= get_legacy_stack_sdp_api()->handle.SDP_AddProfileDescriptorList(handle, profile_uuid, version);}// 7. 添加 HID 特定屬性(核心配置)// HID Parser Versionif (result) {uint8_t* p;const uint16_t rel_num = 0x0100;const uint16_t parser_version = 0x0111;const uint16_t prof_ver = 0x0100;const uint8_t dev_subclass = subclass; // 設備子類(如鍵盤=1,鼠標=2)const uint8_t country_code = 0x21; // 美國(0x21),可根據地區調整const uint8_t bool_false = 0x00;const uint8_t bool_true = 0x01;uint16_t temp;p = (uint8_t*)&temp;UINT16_TO_BE_STREAM(p, rel_num);result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_RELNUM, UINT_DESC_TYPE, 2, (uint8_t*)&temp);p = (uint8_t*)&temp;UINT16_TO_BE_STREAM(p, parser_version);result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_PARSER_VERSION, UINT_DESC_TYPE, 2, (uint8_t*)&temp);// 設備子類result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_SUBCLASS, UINT_DESC_TYPE, 1,(uint8_t*)&dev_subclass);// 國家/地區代碼result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 1,(uint8_t*)&country_code);// 虛擬電纜支持(始終為 TRUE) result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_VIRTUAL_CABLE, BOOLEAN_DESC_TYPE, 1,(uint8_t*)&bool_true);// 支持重新連接result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_RECONNECT_INITIATE, BOOLEAN_DESC_TYPE, 1,(uint8_t*)&bool_true);{static uint8_t cdt = 0x22;uint8_t* p_buf;uint8_t seq_len = 4 + desc_len;if (desc_len > HIDD_APP_DESCRIPTOR_LEN) {log::error("descriptor length = {}, larger than max {}", desc_len,HIDD_APP_DESCRIPTOR_LEN);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_DUE_TO_DESCRIPTOR_LENGTH,1);return HID_ERR_NOT_REGISTERED;};// 8. 添加報告描述符(核心配置)p_buf = (uint8_t*)osi_malloc(HIDD_APP_DESCRIPTOR_LEN + 6);if (p_buf == NULL) {log::error("Buffer allocation failure for size = 2048");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_DUE_TO_BUFFER_ALLOCATION,1);return HID_ERR_NOT_REGISTERED;}// 構建描述符序列(大端字節序)p = p_buf;UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); // 序列類型UINT8_TO_BE_STREAM(p, seq_len); // 序列總長度UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); // 描述符類型(CDT=0x22)UINT8_TO_BE_STREAM(p, cdt); // 通用描述符類型(Generic Descriptor Type)UINT8_TO_BE_STREAM(p, (TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); // 報告描述符數據UINT8_TO_BE_STREAM(p, desc_len);ARRAY_TO_BE_STREAM(p, p_desc_data, (int)desc_len);// 添加到 SDP 記錄result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_DESCRIPTOR_LIST, DATA_ELE_SEQ_DESC_TYPE,p - p_buf, p_buf);osi_free(p_buf);}// 9. 其他 HID 屬性(可選){uint8_t lang_buf[8];p = lang_buf;uint8_t seq_len = 6;uint16_t lang_english = 0x0409;UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);UINT8_TO_BE_STREAM(p, seq_len);UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);UINT16_TO_BE_STREAM(p, lang_english);UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);UINT16_TO_BE_STREAM(p, LANGUAGE_BASE_ID);result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_LANGUAGE_ID_BASE, DATA_ELE_SEQ_DESC_TYPE,p - lang_buf, lang_buf);}result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_BATTERY_POWER, BOOLEAN_DESC_TYPE, 1,(uint8_t*)&bool_true);result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_REMOTE_WAKE, BOOLEAN_DESC_TYPE, 1,(uint8_t*)&bool_false);result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, BOOLEAN_DESC_TYPE, 1,(uint8_t*)&bool_true);result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_BOOT_DEVICE, BOOLEAN_DESC_TYPE, 1,(uint8_t*)&bool_true);p = (uint8_t*)&temp;UINT16_TO_BE_STREAM(p, prof_ver);result &= get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(handle, ATTR_ID_HID_PROFILE_VERSION, UINT_DESC_TYPE, 2,(uint8_t*)&temp);}if (result) {uint16_t browse_group = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;result &= get_legacy_stack_sdp_api()->handle.SDP_AddUuidSequence(handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse_group);}if (!result) {log::error("failed to complete SDP record");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_SDP,1);return HID_ERR_NOT_REGISTERED;}return HID_SUCCESS;
}
負責為 HID 設備生成并填充 服務發現協議(SDP)記錄,使設備能夠被其他藍牙設備發現和連接。其核心任務包括:
-
定義服務類型:聲明設備支持 HID 服務(通過 UUID 標識)。
-
配置協議棧:指定使用的協議(L2CAP、HIDP)及對應的 PSM(協議服務復用器)。
-
填充設備元數據:包括設備名稱、描述、提供商等信息。
-
設置 HID 特定屬性:如設備子類、報告描述符、語言支持、電源管理等。
-
定義設備的功能(如按鍵、坐標軸)、報告格式和數據含義,是 HID 設備的核心配置。
-
必須符合 HID 描述符規范,否則主機無法正確解析數據。
-
-
驗證與錯誤處理:確保記錄格式正確,處理內存分配失敗等異常情況。
-
SDP 屬性 ID
屬性 ID | 描述 |
ATTR_ID_SERVICE_NAME | 服務名稱 |
ATTR_ID_HID_DEVICE_SUBCLASS | HID 設備子類 |
ATTR_ID_HID_DESCRIPTOR_LIST | 報告描述符列表 |
UUID_SERVCLASS_HUMAN_INTERFACE | HID 服務 UUID |
? ?7. PSM 值
-
控制通道:
BT_PSM_HIDC
(0x1124),用于傳輸控制命令。 -
中斷通道:
BT_PSM_HIDI
(0x1125),用于傳輸實時數據(如按鍵、鼠標移動)
意義與最佳實踐
設備發現的基石:正確的 SDP 記錄是藍牙設備掃描和連接的前提,缺失或錯誤的 UUID 或協議配置會導致設備無法被識別。
兼容性關鍵:報告描述符必須嚴格遵循 HID 規范,否則主機(如手機、電腦)無法解析設備功能,導致連接后無法正常使用。
性能優化:通過分離控制通道和中斷通道,確保實時數據(如鼠標移動)通過獨立通道傳輸,減少延遲,提升用戶體驗。
btif_hd_free_buf()
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
static void btif_hd_free_buf() {if (app_info.descriptor.dsc_list) osi_free(app_info.descriptor.dsc_list);if (app_info.p_description) osi_free(app_info.p_description);if (app_info.p_name) osi_free(app_info.p_name);if (app_info.p_provider) osi_free(app_info.p_provider);app_info.descriptor.dsc_list = NULL;app_info.p_description = NULL;app_info.p_name = NULL;app_info.p_provider = NULL;
}
釋放 HID 應用注冊過程中分配的動態內存,避免內存泄漏。主要用于清理 app_info
結構體中指向字符串和描述符數據的指針所占用的內存,確保在應用注銷或資源釋放時正確回收內存資源。
三、時序圖
四、總結
HID 設備服務注冊是藍牙協議棧中設備發現與連接的基礎,涉及多層協議交互與狀態管理:
-
初始化階段:通過 L2CAP 通道注冊和安全配置,建立數據傳輸鏈路。
-
發現階段:通過 SDP 記錄的精確構建,使設備可被主機掃描和識別,其中報告描述符的合規性是兼容性的關鍵。
-
資源管理:動態內存的分配與釋放、狀態標志的維護(如
in_use
、sdp_handle
)確保服務的可靠性與穩定性。
藍牙 HID 設備服務的注冊與啟用流程涉及多個關鍵環節,包括服務注冊、SDP 記錄創建、L2CAP 通道配置以及 HID 描述符處理等。通過對 Bluedroid 協議棧中相關代碼的詳細分析,揭示了這些環節之間的內在聯系和協作機制。對于藍牙協議棧開發者而言,深入理解這些流程有助于優化 HID 設備的實現,提升設備的兼容性和穩定性。同時,對于嵌入式系統工程師以及對 HID 協議實現感興趣的讀者而言,本文也提供了寶貴的技術參考。