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

【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.xdnf.cn/news/5297.html

相关文章:

  • Docker基础入门
  • C++学习之模板初阶学习
  • 金丝雀/灰度/蓝绿发布的详解
  • 【免费工具】图吧工具箱2025.02正式版
  • 【比赛真题解析】篮球迷
  • 链表头插法的优化补充、尾插法完结!
  • 【数据结构与算法】——图(一)
  • anaconda部分基本指令
  • JavaWeb基础
  • Docker容器网络连接失败与镜像拉取异常全解析
  • 【RT-Thread Studio】nor flash配置Fal分区
  • “睿思 BI” 系统介绍
  • 2025年大模型RAG技术的实践总结
  • 2025-05-10-渗透测试:MS14-068漏洞利用、复现黄金票据(随笔)
  • 如何修改进程优先级?
  • 【漫话机器学习系列】250.异或函数(XOR Function)
  • Java游戏服务器开发流水账(4)游戏的数据持久化
  • Google Earth Pro(谷歌地球)2025大陆版安装教程
  • C# WinForm DataGridView 非常频繁地更新或重新绘制慢问题及解决
  • Docker、Docker-compose、K8s、Docker swarm之间的区别
  • 渠道销售简历模板范文
  • 【金仓数据库征文】从生产车间到数据中枢:金仓数据库助力MES系统国产化升级之路
  • ev_loop_fork函数
  • TGRS | FSVLM: 用于遥感农田分割的视觉语言模型
  • bash shell中readarray和mapfile的用法
  • json格式不合法情况下,如何尽量保证数据可用性
  • 用tree.js渲染立方体 关闭msedge同时关闭node进程 compounds同时关闭
  • 企业安全 - 理论基础
  • [ctfshow web入门] web69
  • 湖南(源点咨询)市场调研 商业综合体定位调研分享(下篇)