【Bluedroid】藍牙 HID 設備服務注冊流程源碼解析:從初始化到 SDP 記錄構建

本文圍繞藍牙 HID(人機接口設備)服務注冊流程,詳細解析從 HID 服務啟用、設備初始化、L2CAP 通道注冊到 SDP(服務發現協議)記錄構建的全流程。通過分析關鍵函數如btif_hd_service_registrationBTA_HdEnableHID_DevRegisterHID_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_HdEnablebta_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_evtbtif_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 機制,它還支持動態創建僅出站連接的服務,增強了協議棧的靈活性。主要完成以下任務:

  1. 確保應用層提供必要的回調函數;

  2. 管理 PSM 資源(包括虛擬 PSM 分配);

  3. 初始化通道配置(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(藍牙接口層)之間的事件橋梁,主要職責是:

  1. 解析 HID 設備事件:根據事件類型確定參數長度和數據復制方式。

  2. 上下文切換:通過 btif_transfer_context 將事件從 BTE 層傳遞到 BTIF 層,確保事件在正確的任務上下文執行。

  3. 資源管理:處理需要復制的緩沖區(如報告數據、中斷數據),避免跨層訪問時的內存問題。

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)中,實現設備的自動連接或狀態恢復。具體流程包括:

  1. 遍歷所有已配對的藍牙設備。

  2. 篩選出標記為 有線 HID 設備(通過存儲鍵 BTIF_STORAGE_KEY_HID_DEVICE_CABLED識別)。

  3. 調用 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 設備從上層配置(如存儲標記)到底層協議棧交互的關鍵橋梁,確保設備狀態能夠被藍牙協議棧正確管理,提升用戶體驗的連貫性和設備連接的可靠性。

意義與應用場景

  1. 自動設備恢復:在系統啟動或藍牙服務重啟后,通過存儲的設備標記(如 BTIF_STORAGE_KEY_HID_DEVICE_CABLED)自動添加已配對的 HID 設備,實現 “開機自動連接”。

  2. 雙模設備支持:對于支持有線和無線模式的 HID 設備,當有線連接斷開時,自動切換到無線連接(通過虛擬插入觸發)。

  3. 批量設備管理:在多設備場景中,通過遍歷存儲的設備列表并調用 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 設備應用注冊的執行核心,主要負責:

  1. 驗證設備描述符:檢查 HID 描述符的格式和長度是否合法。

  2. 管理 SDP 記錄:創建、更新或刪除服務發現協議(SDP)記錄,確保設備信息可被其他藍牙設備發現。

  3. 配置 QoS 參數:設置輸入 / 輸出服務質量參數,保障數據傳輸的穩定性。

    1. QoS 作用:

      • 輸入 QoS:定義設備接收數據時的帶寬、延遲等參數(如鍵盤按鍵數據的實時性要求)。

      • 輸出 QoS:定義設備發送數據時的服務質量(如主機響應控制命令的延遲限制)。

  4. 更新設備狀態:通過回調函數通知上層應用注冊結果,并啟用設備的連接接收策略。

意義與最佳實踐

  1. 設備發現的基礎:正確的 SDP 記錄是其他設備發現 HID 服務的前提,若描述符錯誤或 SDP 記錄缺失,設備將無法被搜索到。

  2. 實時性保障:QoS 參數的合理配置(如低延遲、高優先級)對鍵盤、鼠標等實時性要求高的設備至關重要,否則可能導致輸入滯后。

  3. 狀態機管理:通過 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)記錄,使設備能夠被其他藍牙設備發現和連接。其核心任務包括:

  1. 定義服務類型:聲明設備支持 HID 服務(通過 UUID 標識)。

  2. 配置協議棧:指定使用的協議(L2CAP、HIDP)及對應的 PSM(協議服務復用器)。

  3. 填充設備元數據:包括設備名稱、描述、提供商等信息。

  4. 設置 HID 特定屬性:如設備子類、報告描述符、語言支持、電源管理等。

    1. 定義設備的功能(如按鍵、坐標軸)、報告格式和數據含義,是 HID 設備的核心配置。

    2. 必須符合 HID 描述符規范,否則主機無法正確解析數據。

  5. 驗證與錯誤處理:確保記錄格式正確,處理內存分配失敗等異常情況。

  6. SDP 屬性 ID

屬性 ID描述
ATTR_ID_SERVICE_NAME服務名稱
ATTR_ID_HID_DEVICE_SUBCLASSHID 設備子類
ATTR_ID_HID_DESCRIPTOR_LIST報告描述符列表
UUID_SERVCLASS_HUMAN_INTERFACEHID 服務 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_usesdp_handle)確保服務的可靠性與穩定性。

藍牙 HID 設備服務的注冊與啟用流程涉及多個關鍵環節,包括服務注冊、SDP 記錄創建、L2CAP 通道配置以及 HID 描述符處理等。通過對 Bluedroid 協議棧中相關代碼的詳細分析,揭示了這些環節之間的內在聯系和協作機制。對于藍牙協議棧開發者而言,深入理解這些流程有助于優化 HID 設備的實現,提升設備的兼容性和穩定性。同時,對于嵌入式系統工程師以及對 HID 協議實現感興趣的讀者而言,本文也提供了寶貴的技術參考。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/82486.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82486.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/82486.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Win10無法上網:Windows 無法訪問指定設備、路徑或文件。你可能沒有適當的權限訪問該項目找不到域 TEST 的域控制器DNS 解析存在問題

目錄 一.先看問題 二.解決問題 三.補充備用 一.先看問題 Win08有網且已經加入域 Win10無網并且找不到域&#xff08;說明&#xff1a;Win10我之前已經加入過域的&#xff0c;并且能夠上網&#xff0c;但每次在宿舍和教室切換校園網&#xff0c;就會導致只有Win10無網&#…

M0基礎篇之ADC

本節課使用到的例程 一、Single模式例程基本配置的解釋 在例程中我們只使用到了PA25這一個通道&#xff0c;因此我們使用的是Single這個模式&#xff0c;也就是我們在配置模式的時候使用的是單一轉換。 進行多個通道的測量我們可以使用Sequence這個模式。 二、Single模式例程基…

淺談裝飾模式

一、前言 hello大家好&#xff0c;本次打算簡單聊一下裝飾者模式&#xff0c;其實寫有關設計模式的內容還是蠻有挑戰性的&#xff0c;首先呢就是小永哥實力有限擔心說不明白&#xff0c;其次設計模式是為了解決某些問題場景&#xff0c;在當前技術生態圈如此完善的情況下&#…

04 mysql 修改端口和重置root密碼

當我們過了一段時間&#xff0c;忘了自己當初創建的數據庫密碼和端口&#xff0c;或者端口被占用了&#xff0c;要怎么處理呢 首先&#xff0c;我們先停止mysql。 一、修改端口 打開my.ini文件&#xff0c;搜索port&#xff0c;默認是3306&#xff0c;根據你的需要修改為其他…

【基于 LangChain 的異步天氣查詢1】異步調用 Open-Meteo API 查詢該城市當前氣溫

目錄 一、功能概述 二、文件結構 三、城市天氣實時查詢&#xff08;運行代碼&#xff09; weather_runnable.py main.py 運行結果 四、技術亮點 五、使用場景 一、功能概述 它實現了以下主要功能&#xff1a; 用戶輸入地點&#xff08;城市名&#xff09; 構造提示詞…

Spark的三種部署模式及其特點與區別

Spark支持多種集群部署模式&#xff0c;主要分為以下三類&#xff1a; 部署模式特點適用場景資源管理依賴Local模式單機運行&#xff0c;所有進程&#xff08;Driver、Executor&#xff09;在同一個JVM中開發調試、小規模數據測試無集群資源管理&#xff0c;僅本地線程模擬無需…

再度深入理解PLC的輸入輸出接線

本文再次重新梳理&#xff1a; 兩線式/三線式傳感器的原理及接線、PLC的輸入和輸出接線&#xff0c;深入其內部原理&#xff0c;按照自己熟悉的方式去理解該知識 在此之前&#xff0c;需要先統一幾個基礎知識點&#xff1a; 在看任何電路的時候&#xff0c;需要有高低電壓差&…

dockerfile編寫入門

dockerfile 入門 前提已經知道常用的docker和linux命令 如容器的創建,運行, linux的文件命令,會上傳文件到linux等等 dockerfile簡介 之前我們所使用的鏡像都是別人構建好的&#xff0c;但是別人構建好的鏡像不一定能滿足我們的需求。為了滿足我們自己的某一些需求&#xff…

jenkins 啟動報錯

java.lang.UnsatisfiedLinkError: /opt/application/jdk-17.0.11/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory。 解決方案&#xff1a; yum install freetype-devel 安裝完成之后重啟jenkins。

Harness: 全流程 DevOps 解決方案,讓持續集成如吃飯般簡單

引言 在當今快速發展的軟件開發世界中,高效的 DevOps 工具變得越來越重要。Harness 作為一個開源的運維平臺,為開發和運維團隊提供了從代碼托管到 CI/CD 的全流程解決方案,同時實現自動化的開發環境和制品管理。這種集中化的工具可以顯著減少運維難度,提高團隊效率,真正解…

Kubernetes生產實戰(十七):負載均衡流量分發管理實戰指南

在Kubernetes集群中&#xff0c;負載均衡是保障應用高可用、高性能的核心機制。本文將從生產環境視角&#xff0c;深入解析Kubernetes負載均衡的實現方式、最佳實踐及常見問題解決方案。 一、Kubernetes負載均衡的三大核心組件 1&#xff09;Service資源&#xff1a;集群內流…

單脈沖前視成像多目標分辨算法——論文閱讀

單脈沖前視成像多目標分辨算法 1. 論文的研究目標及實際意義1.1 研究目標1.2 實際問題與產業意義2. 論文的創新方法及公式解析2.1 核心思路2.2 關鍵公式與模型2.2.1 單脈沖雷達信號模型2.2.2 匹配濾波輸出模型2.2.3 多目標聯合觀測模型2.2.4 對數似然函數與優化2.2.5 MDL準則目…

Java后端程序員學習前端之JavaScript

1.什么是JavaScript 1.1.概述 JavaScript是一門世界上最流行的腳本語言javaScript 一個合格的后端人員&#xff0c;必須要精通JavaScript 1.2.歷史 JavaScript的起源故事-CSDN博客 2.快速入門 2.1.引入JavaScript 1.內部標簽 <script>//.......</script> --…

AI編程: 使用Trae1小時做成的音視頻工具,提取音頻并識別文本

背景 在上個月&#xff0c;有網頁咨詢我怎么才能獲取視頻中的音頻并識別成文本&#xff0c;我當時給他的回答是去問一下AI&#xff0c;讓AI來給你答案。 他覺得我在敷衍他&#xff0c;大罵了我一頓&#xff0c;大家覺得我的回答對嗎&#xff1f; 小編心里委屈&#xff0c;我…

AI日報 · 2025年5月10日|OpenAI“Stargate”超級數據中心項目掀起美國各州爭奪戰

1、OpenAI“Stargate”超級數據中心項目掀起美國各州爭奪戰 《華盛頓郵報》披露&#xff0c;OpenAI 與 Oracle、SoftBank 合作推進的“Stargate”項目&#xff08;首期投資 1000?億美元&#xff0c;四年內總投資 5000?億美元&#xff09;已收到超過 250 份選址提案&#xff…

Windows系統Jenkins企業級實戰

目標 在Windows操作系統上使用Jenkins完成代碼的自動拉取、編譯、打包、發布工作。 實施 1.安裝Java開發工具包&#xff08;JDK&#xff09; Jenkins是基于Java的應用程序&#xff0c;因此需要先安裝JDK。可以從Oracle官網或OpenJDK下載適合的JDK版本。推薦java17版本&#x…

MySQL 索引和事務

目錄 一、MySQL 索引介紹 1、索引概述 2、索引作用 3、索引的分類 &#xff08;1&#xff09;普通索引 &#xff08;2&#xff09;唯一索引 &#xff08;3&#xff09;主鍵索引 &#xff08;4&#xff09;組合索引&#xff08;最左前綴&#xff09; &#xff08;5&…

Block Styler——字符串控件

字符串控件的應用 參考官方幫助案例&#xff1a;&#xff08;這個方式感覺更好&#xff0c;第二種方式也可以&#xff09;E:\NX1980\UGOPEN\SampleNXOpenApplications\C\BlockStyler\ColoredBlock 普通格式&#xff1a; 讀取&#xff1a; //方法一 string0->GetProperti…

P2572 [SCOI2010] 序列操作 Solution

Description 給定 01 01 01 序列 a ( a 1 , a 2 , ? , a n ) a(a_1,a_2,\cdots,a_n) a(a1?,a2?,?,an?)&#xff0c;并定義 f ( l , r ) [ ( ∑ i l r a i ) r ? l 1 ] f(l,r)[(\sum\limits_{il}^r a_i)r-l1] f(l,r)[(il∑r?ai?)r?l1]. 執行 m m m 個操作&am…

RAG 2.0 深入解讀

作者&#xff1a;阿里云開發者 原文&#xff1a;https://zhuanlan.zhihu.com/p/1903437079603545114? 一、Introduction 過去一年可謂是RAG元年&#xff0c;檢索增強生成技術迅速發展與深刻變革&#xff0c;其創新與應用已深刻重塑了大模型落地的技術范式。站在2025年&#x…