【Bluedroid】蓝牙 HID 设备服务注册流程源码解析:从初始化到 SDP 记录构建

news2025/5/12 20:35:14

本文围绕蓝牙 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); // 找到未占用的 PSM
      if (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); // 分配新的 RCB
    if (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;                    // 本地 MTU
  p_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 服务 UUID
  bta_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 connections
  HID_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 List
  if (result) {
    uint16_t uuid = UUID_SERVCLASS_HUMAN_INTERFACE; // HID 服务 UUID
    result &= get_legacy_stack_sdp_api()->handle.SDP_AddServiceClassIdList(
        handle, 1, &uuid); // 添加 UUID 到服务类列表
  }

  // 2. 添加协议描述符列表(必选)
  // Protocol Descriptor List
  if (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 List
  if (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 List
  if (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 List
  if (result) {
    const uint16_t profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE; // HID 配置文件 UUID
    const uint16_t version = 0x0100; // HID 配置文件版本 1.0

    result &= get_legacy_stack_sdp_api()->handle.SDP_AddProfileDescriptorList(
        handle, profile_uuid, version);
  }

  // 7. 添加 HID 特定属性(核心配置)
  // HID Parser Version
  if (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.coloradmin.cn/o/2373331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TWAS、GWAS、FUSION

全基因组关联研究&#xff08;GWAS&#xff0c;Genome-Wide Association Study&#xff09;是一种统计学方法&#xff0c;用于在全基因组水平上识别与特定性状或疾病相关的遗传变异。虽然GWAS可以识别与性状相关的遗传信号&#xff0c;但它并不直接揭示这些遗传变异如何影响生物…

测试一下多模态提取图片中文字的能力

测试一下多模态提取图片中文字的能力 原图片&#xff0c; 提取结果&#xff0c; 各位嘉宾&#xff0c;大家好&#xff01;明天&#xff08;5月11日&#xff09;9:00-12:00将在大连市高新园区星火亚庭32号A座一层二楼设置咖啡厅等六个小水活动。请大家积极安排时间&#xff0c;…

(51单片机)LCD显示红外遥控相关数字(Delay延时函数)(LCD1602教程)(Int0和Timer0外部中断教程)(IR红外遥控模块教程)

前言&#xff1a; 本次Timer0模块改装了一下&#xff0c;注意&#xff01;&#xff01;&#xff01;今天只是简单的实现一下&#xff0c;明天用次功能显示遥控密码锁 演示视频&#xff1a; 在审核 源代码&#xff1a; 如上图将9个文放在Keli5 中即可&#xff0c;然后烧录在…

物品识别 树莓派4 YOLO v11

让树莓派可以识别身边的一些物品 python3 -m venv --system-site-packages yolooo source yolooo/bin/activate 树莓派换清华源&#xff0c;bookworm 下面这条命令将安装 OpenCV 以及运行 YOLO 所需的基础设施 pip install ultralytics 还会安装大量其他软件包&#xff0c…

【计算机视觉】3DDFA_V2中表情与姿态解耦及多任务平衡机制深度解析

3DDFA_V2中表情与姿态解耦及多任务平衡机制深度解析 1. 表情与姿态解耦的技术实现1.1 参数化建模基础1.2 解耦的核心机制1.2.1 基向量正交化设计1.2.2 网络架构设计1.2.3 损失函数设计 1.3 实现代码解析 2. 多任务联合学习的权重平衡2.1 任务定义与损失函数2.2 动态权重平衡策略…

istio in action之流量控制与路由

当流量进入集群后&#xff0c;我们如何确保它能被精确地路由到正确的服务&#xff1f;特别是当我们需要发布新版本时&#xff0c;如何在不中断服务的前提下&#xff0c;安全地将用户引入到新版本&#xff1f;这正是我们今天要深入探讨的精细化流量控制&#xff0c;看看 Istio 如…

优选算法——前缀和

目录 1. 数组的中心下标 2. 除自身以外数组的乘积 3. 和为k的子数组 4. 和可被K整除的子数组 5. 连续数组 6. 矩阵区域和 1. 数组的中心下标 题目链接&#xff1a;724. 寻找数组的中心下标 - 力扣&#xff08;LeetCode&#xff09; 题目展示&#xff1a; 题目分析&am…

用AI写简历是否可行?

让AI批量写简历然后投简历是绝对不行的&#xff01;&#xff01;&#xff01; 为什么不行&#xff0c;按照 "招聘经理" 工作经历举例&#xff1a; ai提示词&#xff1a;请帮我写一份招聘经理的工作经历内容&#xff1a; 招聘经理 | XXX科技有限公司 | 2020年…

力扣题解:2、两数相加

个人认为&#xff0c;该题目可以看作合并两个链表的变种题&#xff0c;本题与21题不同的是&#xff0c;再处理两个结点时&#xff0c;对比的不是两者的大小&#xff0c;而是两者和是否大于10&#xff0c;加法计算中大于10要进位&#xff0c;所以我们需要声明一个用来标记是否进…

IPM IMI111T-026H 高效风扇控制板

概述&#xff1a; REF-MHA50WIMI111T 是一款专为风扇驱动设计的参考开发板&#xff0c;搭载了英飞凌的IMI111T-026H iMOTION™智能功率模块(IPM)。这个模块集成了运动控制引擎(MCE)、三相栅极驱动器和基于IGBT的功率级&#xff0c;全部封装在一个紧凑的DSO22封装中。REF-MHA50…

武汉火影数字|数字科技馆打造:开启科技探索新大门

足不出户&#xff0c;就能畅游科技的奇幻世界&#xff0c;你相信吗&#xff1f;数字科技馆就能帮你实现&#xff01;在这个数字化的时代&#xff0c;数字科技馆如同一颗璀璨的新星&#xff0c;照亮了我们探索科学的道路。 那么&#xff0c;数字科技馆究竟是什么呢&#xff1f; …

suricata之日志截断

一、背景 在suricata的调试过程中&#xff0c;使用SCLogXXX api进行信息的输出&#xff0c;发现输出的日志被截断了&#xff0c;最开始以为是解析逻辑有问题&#xff0c;没有解析完整&#xff0c;经过排查后&#xff0c;发现SCLogXXX api内部进行了长度限制&#xff0c;最长2K…

简易图片编辑工具,支持抠图和替换背景

软件介绍 Photo Retouch是一款由微软官方商店推出的免费图片处理软件&#xff0c;具有抠图、换背景、修复等功能&#xff0c;操作便捷且效率极高&#xff0c;非常值得尝试。 功能详解 这款软件提供五大功能&#xff0c;包括删除物体、快速修复、一键抠图、背景调整和裁剪…

Java Bean容器详解:核心功能与最佳使用实践

在Java企业级开发中&#xff0c;Bean容器是框架的核心组件之一&#xff0c;它通过管理对象&#xff08;Bean&#xff09;的生命周期、依赖关系等&#xff0c;显著提升了代码的可维护性和扩展性。主流的框架如Spring、Jakarta EE&#xff08;原Java EE&#xff09;均提供了成熟的…

Spring Security 深度解析:打造坚不可摧的用户认证与授权系统

Spring Security 深度解析&#xff1a;打造坚不可摧的用户认证与授权系统 一、引言 在当今数字化时代&#xff0c;构建安全可靠的用户认证与授权系统是软件开发中的关键任务。Spring Security 作为一款功能强大的 Java 安全框架&#xff0c;为开发者提供了全面的解决方案。本…

Selenium模拟人类行为,操作网页的方法(全)

看到有朋友评论问&#xff0c;用selenium怎么模仿人类行为&#xff0c;去操作网页的页面呢&#xff1f; 我想了想&#xff0c;这确实是一个很大的点&#xff0c;不应该是一段代码能解决的&#xff0c; 就像是,如果让程序模拟人类的行为。例如模拟人类买菜&#xff0c;做饭&am…

右值引用的剖析

引入&#xff1a;为什么要有右值引用&#xff1f; 右值引用的存在&#xff0c;就是为了解决左值引用解决不了的问题&#xff01; 左值引用的问题&#xff1a; 我们知道&#xff0c;左值引用在做参数和做返回值都可以提高效率&#xff1b;但是有时候&#xff0c;我们无法用左…

高效Python开发:uv包管理器全面解析

目录 uv简介亮点与 pip、pip-tools、pipx、poetry、pyenv、virtualenv 对比 安装uv快速开始uv安装pythonuv运行脚本运行无依赖的脚本运行有依赖的脚本创建带元数据的 Python 脚本使用 shebang 创建可执行文件使用其他package indexes锁定依赖提高可复现性指定不同的 Python 版本…

【Linux系统编程】进程属性--进程状态

1.进程的状态 1.1进程的状态在PCB中就是一个变量 一般用宏来定义&#xff0c;例如&#xff1a; #define RUNNING 1 #define BLOCK 2 struct task_struct中的int status 1.2并行和并发 CPU执行代码&#xff0c;不是把进程代码执行完毕&#xff0c;才执行下一个&#xff0…

高精度之加减乘除之多解总结(加与减篇)

开篇总述&#xff1a;精度计算的教学比较杂乱&#xff0c;无系统的学习&#xff0c;且存在同法多线的方式进行同一种运算&#xff0c;所以我写此篇的目的只是为了直指本质&#xff0c;不走教科书方式&#xff0c;步骤冗杂。 一&#xff0c;加法 我在此讲两种方法&#xff1a; …