本文围绕蓝牙 HID(人机接口设备)服务注册流程,详细解析从 HID 服务启用、设备初始化、L2CAP 通道注册到 SDP(服务发现协议)记录构建的全流程。通过分析关键函数如btif_hd_service_registration
、BTA_HdEnable
、HID_DevRegister
及HID_DevAddRecord
的逻辑,揭示了 HID 设备如何通过协议栈完成注册、配置及发现功能,确保设备可被其他蓝牙主机识别和连接。结合Android Bluedroid代码逻辑与协议规范,阐述了状态机管理、内存分配、安全配置及数据传输的核心机制。
一、概述
蓝牙 HID 设备(如键盘、鼠标)的注册与启用流程是蓝牙协议栈中实现人机交互功能的核心部分。通过分析 Bluedroid 协议栈中的相关代码,详细阐述 HID 设备如何通过 SDP(服务发现协议)暴露自身服务、如何配置 L2CAP 通道以确保数据传输的可靠性,以及如何管理 HID 描述符以实现与主机的兼容通信。
1.1 HID 服务注册入口:btif_hd_service_registration
作为 HID 服务注册的入口,通过调用BTA_HdEnable
触发协议栈的 HID 服务初始化。核心逻辑包括:
-
检查回调函数指针
bt_hd_callbacks
是否有效,确保上层应用可接收状态通知。 -
向 BTA(蓝牙协议栈适配层)发送启用事件
BTA_HD_API_ENABLE_EVT
,启动后续注册流程。
1.2 协议栈初始化:BTA_HdEnable
与bta_hd_api_enable
-
系统注册:通过
bta_sys_register
将 HID 服务注册到蓝牙系统层,关联事件处理函数bta_hd_hdl_event
和禁用函数BTA_HdDisable
。 -
消息传递:构造并发送启用事件消息,触发
bta_hd_api_enable
执行初始化:-
调用
HID_DevInit
清零全局控制块hd_cb
,确保初始状态正确。 -
通过
HID_DevRegister
将 HID 设备注册到 L2CAP 层,完成控制通道(PSM=0x1124)和中断通道(PSM=0x1125)的注册,配置 MTU 及安全级别(认证与加密)。
-
1.3 L2CAP 通道注册:hidd_conn_reg
与协议配置
-
双通道注册:
-
控制通道:用于传输配置命令,注册时指定安全级别
BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT
。 -
中断通道:用于实时数据传输(如按键事件),确保低延迟。
-
-
虚拟 PSM 机制:对于动态 PSM 场景,通过
L2CA_Register
分配虚拟 PSM,避免端口冲突,增强协议栈灵活性。
1.4 SDP 记录构建:HID_DevAddRecord
的核心作用
负责生成 HID 设备的 SDP 记录,确保设备可被其他主机发现:
-
必选属性:
-
服务 UUID:声明支持 HID 服务(
UUID_SERVCLASS_HUMAN_INTERFACE
)。 -
协议描述符:指定 L2CAP 和 HIDP 协议,配置控制通道与中断通道的 PSM。
-
配置文件版本:声明 HID 协议版本(如 1.0),确保兼容性。
-
-
设备元数据:填充设备名称、描述、提供商等信息,显示于蓝牙设备列表。
-
HID 特定属性:
-
报告描述符:定义设备功能(如按键、坐标轴),遵循 HID 规范,是主机解析数据的关键。
-
子类与国家代码:标识设备类型(如键盘、鼠标)和区域配置。
-
电源与连接策略:设置电池供电状态、支持重新连接等,优化设备管理。
-
1.5 状态回调与资源管理
-
事件传递:通过
bte_hd_evt
和btif_hd_upstreams_evt
完成协议栈(BTE)与接口层(BTIF)的事件上下文切换,确保回调在正确层处理。 -
内存释放:
btif_hd_free_buf
清理注册过程中分配的动态内存(如描述符、字符串),避免泄漏,保障系统稳定性。
二、源码分析
btif_hd_service_registration
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
static bthd_callbacks_t* bt_hd_callbacks = NULL;
/*******************************************************************************
*
* Function btif_hd_service_registration
*
* Description Registers hid device service
*
* Returns none
*
******************************************************************************/
void btif_hd_service_registration() {
log::verbose("");
/* enable HD */
if (bt_hd_callbacks != NULL) {
BTA_HdEnable(bte_hd_evt);
}
}
注册 HID(Human Interface Device,人机接口设备)设备服务。若满足特定条件,会调用 BTA_HdEnable
函数来启用 HID 设备服务。
BTA_HdEnable
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
static const tBTA_SYS_REG bta_hd_reg = {bta_hd_hdl_event, BTA_HdDisable};
/*******************************************************************************
*
* Function BTA_HdEnable
*
* Description Enables HID device
*
* Returns void
*
******************************************************************************/
void BTA_HdEnable(tBTA_HD_CBACK* p_cback) {
log::verbose("");
// 1. 系统注册
bta_sys_register(BTA_ID_HD, &bta_hd_reg);
// 2. 内存分配与初始化
tBTA_HD_API_ENABLE* p_buf =
(tBTA_HD_API_ENABLE*)osi_malloc((uint16_t)sizeof(tBTA_HD_API_ENABLE));
memset(p_buf, 0, sizeof(tBTA_HD_API_ENABLE));
// 3. 设置消息内容
p_buf->hdr.event = BTA_HD_API_ENABLE_EVT;
p_buf->p_cback = p_cback;
// 4. 发送消息
bta_sys_sendmsg(p_buf);
}
启用 HID设备。在系统中注册 HID 设备相关的处理信息,并且发送一个启用事件消息,以此来触发后续的处理流程。
bta_hd_hdl_event(BTA_HD_API_ENABLE_EVT)
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/*******************************************************************************
*
* Function bta_hd_hdl_event
*
* Description HID device main event handling function.
*
* Returns void
*
******************************************************************************/
bool bta_hd_hdl_event(const BT_HDR_RIGID* p_msg) {
log::verbose("p_msg->event={}", p_msg->event);
switch (p_msg->event) {
case BTA_HD_API_ENABLE_EVT:
bta_hd_api_enable((tBTA_HD_DATA*)p_msg);
break;
case BTA_HD_API_DISABLE_EVT:
if (bta_hd_cb.state == BTA_HD_CONN_ST) {
log::warn("host connected, disconnect before disabling");
// unregister (and disconnect)
bta_hd_cb.disable_w4_close = TRUE;
bta_hd_better_state_machine(BTA_HD_API_UNREGISTER_APP_EVT,
(tBTA_HD_DATA*)p_msg);
} else {
bta_hd_api_disable();
}
break;
default:
bta_hd_better_state_machine(p_msg->event, (tBTA_HD_DATA*)p_msg);
}
return (TRUE);
}
当接收到的事件类型为 BTA_HD_API_ENABLE_EVT
时,调用 bta_hd_api_enable
函数,将消息指针转换为 tBTA_HD_DATA
类型后传入该函数,用于处理 HID 设备的启用操作。
bta_hd_api_enable
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/*******************************************************************************
*
* Function bta_hd_api_enable
*
* Description Enables HID device
*
* Returns void
*
******************************************************************************/
void bta_hd_api_enable(tBTA_HD_DATA* p_data) {
tBTA_HD_STATUS status = BTA_HD_ERROR;
tHID_STATUS ret;
log::verbose("");
HID_DevInit(); // HID 设备初始化
memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB));
/* store parameters */
bta_hd_cb.p_cback = p_data->api_enable.p_cback; // 存储回调函数指针
ret = HID_DevRegister(bta_hd_cback); // HID 设备注册
if (ret == HID_SUCCESS) {
status = BTA_HD_OK;
} else {
log::error("Failed to register HID device ({})", ret);
}
// 触发回调函数
/* signal BTA call back event */
tBTA_HD bta_hd;
bta_hd.status = status;
(*bta_hd_cb.p_cback)(BTA_HD_ENABLE_EVT, &bta_hd);
}
启用 HID设备。完成一系列的初始化和注册操作,并在操作完成后通过回调函数通知调用者操作结果。
HID_DevInit
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
tHID_DEV_CTB hd_cb;
/*******************************************************************************
*
* Function HID_DevInit
*
* Description Initializes control block
*
* Returns void
*
******************************************************************************/
void HID_DevInit(void) {
log::verbose("");
memset(&hd_cb, 0, sizeof(tHID_DEV_CTB));
}
对 HID设备的控制块进行初始化操作。通过将控制块的内存清零,可以确保控制块中的各个成员变量处于初始状态,避免因未初始化的变量值导致的潜在错误。
HID_DevRegister
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/*******************************************************************************
*
* Function HID_DevRegister
*
* Description Registers HID device with lower layers
*
* Returns tHID_STATUS
*
******************************************************************************/
tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK* host_cback) {
tHID_STATUS st; // 用于存储注册过程中各个步骤的状态
log::verbose("");
// 检查设备是否已注册
if (hd_cb.reg_flag) {
log_counter_metrics(
android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_ALREADY_REGISTERED,
1);
return HID_ERR_ALREADY_REGISTERED;
}
// 检查回调函数指针是否为空
if (host_cback == NULL) {
log_counter_metrics(
android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_HOST_CALLBACK_NULL,
1);
return HID_ERR_INVALID_PARAM;
}
// 注册到 L2CAP 层
/* Register with L2CAP */
st = hidd_conn_reg();
if (st != HID_SUCCESS) return st;
// 更新控制块信息
hd_cb.callback = host_cback;
hd_cb.reg_flag = TRUE;
// 清理待处理数据
if (hd_cb.pending_data) {
osi_free(hd_cb.pending_data);
hd_cb.pending_data = NULL;
}
return (HID_SUCCESS);
}
将 HID设备注册到下层协议栈中。通过一系列的检查和操作,确保 HID 设备能够正确地注册到下层协议栈中。在注册过程中,会检查设备是否已注册、回调函数指针是否有效,并将设备注册到 L2CAP 层。注册成功后,会更新控制块信息并清理待处理数据。
hidd_conn_reg
packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
/*******************************************************************************
*
* Function hidd_conn_reg
*
* Description Registers L2CAP channels
*
* Returns void
*
******************************************************************************/
tHID_STATUS hidd_conn_reg(void) {
log::verbose("");
// 1. L2CAP 配置信息初始化
memset(&hd_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));
hd_cb.l2cap_cfg.mtu_present = TRUE;
hd_cb.l2cap_cfg.mtu = HID_DEV_MTU_SIZE;
memset(&hd_cb.l2cap_intr_cfg, 0, sizeof(tL2CAP_CFG_INFO));
hd_cb.l2cap_intr_cfg.mtu_present = TRUE;
hd_cb.l2cap_intr_cfg.mtu = HID_DEV_MTU_SIZE;
// 2. 注册控制通道
if (!L2CA_Register2(HID_PSM_CONTROL, dev_reg_info, false /* enable_snoop */,
nullptr, HID_DEV_MTU_SIZE, 0,
BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) {
log::error("HID Control (device) registration failed");
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDD_ERR_L2CAP_FAILED_CONTROL,
1);
return (HID_ERR_L2CAP_FAILED);
}
// 3. 注册中断通道
if (!L2CA_Register2(HID_PSM_INTERRUPT, dev_reg_info, false /* enable_snoop */,
nullptr, HID_DEV_MTU_SIZE, 0,
BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) {
L2CA_Deregister(HID_PSM_CONTROL);
log::error("HID Interrupt (device) registration failed");
log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::
HIDD_ERR_L2CAP_FAILED_INTERRUPT,
1);
return (HID_ERR_L2CAP_FAILED);
}
return (HID_SUCCESS);
}
注册 HID 设备的 L2CAP(Logical Link Control and Adaptation Protocol,逻辑链路控制和适配协议)通道,包括控制通道和中断通道。若注册过程中出现错误,返回相应的错误状态;若所有通道都成功注册,则返回成功状态。确保 HID 设备的 L2CAP 通道能够正确注册,为后续的数据传输奠定基础。
L2CA_Register2
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
uint16_t L2CA_Register2(uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info,
bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info,
uint16_t my_mtu, uint16_t required_remote_mtu,
uint16_t sec_level) {
auto ret = L2CA_Register(psm, p_cb_info, enable_snoop, p_ertm_info, my_mtu,
required_remote_mtu, sec_level);
get_btm_client_interface().security.BTM_SetSecurityLevel(
false, "", 0, sec_level, psm, 0, 0);
return ret;
}
L2CAP层的注册函数,用于将 HID 设备的控制通道或中断通道注册到蓝牙协议栈中。在 L2CA_Register
基础上增加了安全级别设置逻辑,确保注册的通道具备指定的安全属性(如认证、加密)。
packages/modules/Bluetooth/system/stack/l2cap/l2c_api.cc
/*******************************************************************************
*
* Function L2CA_Register
*
* Description Other layers call this function to register for L2CAP
* services.
*
* Returns PSM to use or zero if error. Typically, the PSM returned
* is the same as was passed in, but for an outgoing-only
* connection to a dynamic PSM, a "virtual" PSM is returned
* and should be used in the calls to L2CA_ConnectReq(),
* L2CA_ErtmConnectReq() and L2CA_Deregister()
*
******************************************************************************/
uint16_t L2CA_Register(uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info,
bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info,
uint16_t my_mtu, uint16_t required_remote_mtu,
uint16_t sec_level) {
// 检查必填回调函数是否存在
const bool config_cfm_cb = (p_cb_info.pL2CA_ConfigCfm_Cb != nullptr);
const bool config_ind_cb = (p_cb_info.pL2CA_ConfigInd_Cb != nullptr);
const bool data_ind_cb = (p_cb_info.pL2CA_DataInd_Cb != nullptr);
const bool disconnect_ind_cb = (p_cb_info.pL2CA_DisconnectInd_Cb != nullptr);
tL2C_RCB* p_rcb;
uint16_t vpsm = psm; // 虚拟 PSM,默认为传入的 PSM
/* Verify that the required callback info has been filled in
** Note: Connection callbacks are required but not checked
** for here because it is possible to be only a client
** or only a server.
*/
// 验证必填回调函数(配置确认、数据指示、断开指示)
if (!config_cfm_cb || !data_ind_cb || !disconnect_ind_cb) {
log::error(
"L2CAP - no cb registering PSM: 0x{:04x} cfg_cfm:{} cfg_ind:{} "
"data_ind:{} discon_int:{}",
psm, config_cfm_cb, config_ind_cb, data_ind_cb, disconnect_ind_cb);
return (0);
}
// 验证 PSM 是否有效(非保留值)
/* Verify PSM is valid */
if (L2C_INVALID_PSM(psm)) {
log::error("L2CAP - invalid PSM value, PSM: 0x{:04x}", psm);
return (0);
}
/* Check if this is a registration for an outgoing-only connection to */
/* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */
// 处理仅出站连接的动态 PSM 场景(需传入 ConnectInd 回调为 NULL)
if ((psm >= 0x1001) && (p_cb_info.pL2CA_ConnectInd_Cb == NULL)) {
for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2) {
p_rcb = l2cu_find_rcb_by_psm(vpsm); // 找到未占用的 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 机制,它还支持动态创建仅出站连接的服务,增强了协议栈的灵活性。主要完成以下任务:
-
确保应用层提供必要的回调函数;
-
管理 PSM 资源(包括虚拟 PSM 分配);
-
初始化通道配置(MTU、ERTM 等)。
BTM_SetSecurityLevel
packages/modules/Bluetooth/system/stack/btm/btm_sec.cc
/*******************************************************************************
*
* Function BTM_SetSecurityLevel
*
* Description Register service security level with Security Manager
*
* Parameters: is_originator - true if originating the connection
* p_name - Name of the service relevant only if
* authorization will show this name to user.
* Ignored if BT_MAX_SERVICE_NAME_LEN is 0.
* service_id - service ID for the service passed to
* authorization callback
* sec_level - bit mask of the security features
* psm - L2CAP PSM
* mx_proto_id - protocol ID of multiplexing proto below
* mx_chan_id - channel ID of multiplexing proto below
*
* Returns true if registered OK, else false
*
******************************************************************************/
bool BTM_SetSecurityLevel(bool is_originator, const char* p_name,
uint8_t service_id, uint16_t sec_level, uint16_t psm,
uint32_t mx_proto_id, uint32_t mx_chan_id) {
return btm_sec_cb.AddService(is_originator, p_name, service_id, sec_level,
psm, mx_proto_id, mx_chan_id);
}
BTM_SetSecurityLevel分析见【Bluedroid】蓝牙 SDP(服务发现协议)模块代码解析与流程梳理-CSDN博客
处理结果回调:bte_hd_evt(BTA_HD_ENABLE_EVT)
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/*******************************************************************************
*
* Function bte_hd_evt
*
* Description Switches context from BTE to BTIF for all BT-HD events
*
* Returns void
*
******************************************************************************/
static void bte_hd_evt(tBTA_HD_EVT event, tBTA_HD* p_data) {
bt_status_t status;
int param_len = 0;
tBTIF_COPY_CBACK* p_copy_cback = NULL; // 数据复制回调,用于处理需要跨层传递的缓冲区
log::verbose("event={}", event);
// 事件类型与参数长度匹配
switch (event) {
case BTA_HD_ENABLE_EVT:
case BTA_HD_DISABLE_EVT:
case BTA_HD_UNREGISTER_APP_EVT:
param_len = sizeof(tBTA_HD_STATUS);
break;
case BTA_HD_REGISTER_APP_EVT:
param_len = sizeof(tBTA_HD_REG_STATUS);
break;
case BTA_HD_OPEN_EVT:
case BTA_HD_CLOSE_EVT:
case BTA_HD_VC_UNPLUG_EVT:
case BTA_HD_CONN_STATE_EVT:
param_len = sizeof(tBTA_HD_CONN);
break;
case BTA_HD_GET_REPORT_EVT:
param_len += sizeof(tBTA_HD_GET_REPORT);
break;
case BTA_HD_SET_REPORT_EVT:
param_len = sizeof(tBTA_HD_SET_REPORT) + p_data->set_report.len;
p_copy_cback = set_report_copy_cb;
break;
case BTA_HD_SET_PROTOCOL_EVT:
param_len += sizeof(p_data->set_protocol);
break;
case BTA_HD_INTR_DATA_EVT:
param_len = sizeof(tBTA_HD_INTR_DATA) + p_data->intr_data.len;
p_copy_cback = intr_data_copy_cb;
break;
}
// 上下文切换
status = btif_transfer_context(btif_hd_upstreams_evt, (uint16_t)event,
(char*)p_data, param_len, p_copy_cback);
ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
}
bte_hd_evt
是蓝牙协议栈中 BTE(蓝牙传输层)与 BTIF(蓝牙接口层)之间的事件桥梁,主要职责是:
-
解析 HID 设备事件:根据事件类型确定参数长度和数据复制方式。
-
上下文切换:通过
btif_transfer_context
将事件从 BTE 层传递到 BTIF 层,确保事件在正确的任务上下文执行。 -
资源管理:处理需要复制的缓冲区(如报告数据、中断数据),避免跨层访问时的内存问题。
btif_hd_upstreams_evt(BTA_HD_ENABLE_EVT)
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/*******************************************************************************
*
* Function btif_hd_upstreams_evt
*
* Description Executes events in btif context
*
* Returns void
*
******************************************************************************/
static void btif_hd_upstreams_evt(uint16_t event, char* p_param) {
tBTA_HD* p_data = (tBTA_HD*)p_param;
log::verbose("event={}", dump_hd_event(event));
switch (event) {
case BTA_HD_ENABLE_EVT:
log::verbose("status={}", p_data->status);
if (p_data->status == BTA_HD_OK) {
btif_storage_load_hidd(); // 从存储中加载 HID 设备的相关信息
btif_hd_cb.status = BTIF_HD_ENABLED;
/* Register the app if not yet registered */
if (!btif_hd_cb.app_registered) { // 检查应用程序是否已经注册
BTA_HdRegisterApp(&app_info, &in_qos, &out_qos);
btif_hd_free_buf();
}
} else { // 启用失败的处理
btif_hd_cb.status = BTIF_HD_DISABLED;
log::warn("Failed to enable BT-HD, status={}", p_data->status);
}
break;
...
在 BTIF(Bluetooth Interface,蓝牙接口)上下文环境中处理从 BTE传递过来的 HID相关事件。根据不同的事件类型执行相应的操作,以实现对 HID 设备状态的管理和功能的控制。在处理 BTA_HD_ENABLE_EVT
事件时,会根据事件的状态进行不同的处理,包括加载设备信息、注册应用程序、释放缓冲区等操作。
btif_storage_load_hidd
packages/modules/Bluetooth/system/btif/src/btif_profile_storage.cc
/*******************************************************************************
* Function btif_storage_load_hidd
*
* Description Loads hidd bonded device and "plugs" it into hidd
*
* Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise
*
******************************************************************************/
bt_status_t btif_storage_load_hidd(void) {
// 遍历所有已配对的蓝牙设备地址
for (const auto& bd_addr : btif_config_get_paired_devices()) {
auto name = bd_addr.ToString();
log::verbose("Remote device:{}", ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
int value;
// 检查设备是否为已绑定的 HID 设备,并获取其有线状态标记
if (btif_in_fetch_bonded_device(name) == BT_STATUS_SUCCESS) { // 检查设备是否为已绑定状态(即是否完成安全认证)
if (btif_config_get_int(name, BTIF_STORAGE_KEY_HID_DEVICE_CABLED, &value)) {
// 若标记存在(value 非零),则认为是 HID 设备,添加到 HID 服务
BTA_HdAddDevice(bd_addr);
break; // 仅处理第一个匹配的设备(可能假设单设备场景)
}
}
}
return BT_STATUS_SUCCESS; // 无论是否找到设备,均返回成功(遍历逻辑无失败条件)
}
从本地存储中加载已绑定的 HID 设备信息,并将其 “虚拟插入” 到 HID 设备服务(HIDD)中,实现设备的自动连接或状态恢复。具体流程包括:
-
遍历所有已配对的蓝牙设备。
-
筛选出标记为 有线 HID 设备(通过存储键
BTIF_STORAGE_KEY_HID_DEVICE_CABLED
识别)。 -
调用
BTA_HdAddDevice
将设备添加到 HID 服务中,触发协议栈的连接或配置流程。
BTA_HdAddDevice
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/*******************************************************************************
*
* Function BTA_HdAddDevice
*
* Description This function is called when a device is virtually cabled
*
* Returns void
*
******************************************************************************/
void BTA_HdAddDevice(const RawAddress& addr) {
log::verbose("");
tBTA_HD_DEVICE_CTRL* p_buf =
(tBTA_HD_DEVICE_CTRL*)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL));
p_buf->hdr.event = BTA_HD_API_ADD_DEVICE_EVT;
p_buf->addr = addr;
bta_sys_sendmsg(p_buf);
}
将一个虚拟连接的 HID 设备添加到蓝牙协议栈的 HID 服务(BTA_HD)中。这里的 “虚拟连接” 通常指设备通过存储标记(如 BTIF_STORAGE_KEY_HID_DEVICE_CABLED
)被识别为需要自动管理的设备,而非物理有线连接。通过构造并发送特定事件消息,通知 HID 服务层执行设备添加流程,包括尝试建立连接、配置通道等操作。该函数是 HID 设备从上层配置(如存储标记)到底层协议栈交互的关键桥梁,确保设备状态能够被蓝牙协议栈正确管理,提升用户体验的连贯性和设备连接的可靠性。
意义与应用场景
自动设备恢复:在系统启动或蓝牙服务重启后,通过存储的设备标记(如
BTIF_STORAGE_KEY_HID_DEVICE_CABLED
)自动添加已配对的 HID 设备,实现 “开机自动连接”。双模设备支持:对于支持有线和无线模式的 HID 设备,当有线连接断开时,自动切换到无线连接(通过虚拟插入触发)。
批量设备管理:在多设备场景中,通过遍历存储的设备列表并调用
BTA_HdAddDevice
,实现多个 HID 设备的批量添加(尽管当前代码仅处理第一个设备)。
bta_hd_hdl_event(BTA_HD_API_ADD_DEVICE_EVT)
当 BTA_HD_API_ADD_DEVICE_EVT
事件被 bta_hd_hdl_event
函数接收后,会触发以下操作:
bta_hd_better_state_machine(BTA_HD_API_ADD_DEVICE_EVT)
bta_hd_add_device_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/*******************************************************************************
*
* Function bta_hd_add_device_act
*
* Description
*
* Returns void
*
******************************************************************************/
void bta_hd_add_device_act(tBTA_HD_DATA* p_data) {
tBTA_HD_DEVICE_CTRL* p_ctrl = (tBTA_HD_DEVICE_CTRL*)p_data;
log::verbose("");
HID_DevPlugDevice(p_ctrl->addr);
}
将目标设备 “虚拟插入” 到 HID 服务中。通过调用底层函数 HID_DevPlugDevice
,触发对指定蓝牙设备的连接尝试或状态更新,从而完成设备添加的实际操作。
HID_DevPlugDevice
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
tHID_DEV_CTB hd_cb; // 全局控制块,存储 HID 设备的状态信息
/*******************************************************************************
*
* Function HID_DevPlugDevice
*
* Description Establishes virtual cable to given host
*
* Returns tHID_STATUS
*
******************************************************************************/
tHID_STATUS HID_DevPlugDevice(const RawAddress& addr) {
hd_cb.device.in_use = TRUE;
hd_cb.device.addr = addr;
return HID_SUCCESS;
}
模拟 HID 设备与目标主机建立 “虚拟连接”(Virtual Cable),本质上是在协议栈层面标记设备为 “正在使用” 并记录目标主机的蓝牙地址。不涉及实际的物理连接或数据传输,而是通过更新全局控制块 hd_cb
的状态,为后续的连接请求、数据传输等操作提供上下文信息。
BTA_HdRegisterApp
packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/*******************************************************************************
*
* Function BTA_HdRegisterApp
*
* Description This function is called when application should be
*registered
*
* Returns void
*
******************************************************************************/
void BTA_HdRegisterApp(tBTA_HD_APP_INFO* p_app_info, tBTA_HD_QOS_INFO* p_in_qos,
tBTA_HD_QOS_INFO* p_out_qos) {
log::verbose("");
// 为注册消息分配内存
tBTA_HD_REGISTER_APP* p_buf =
(tBTA_HD_REGISTER_APP*)osi_malloc(sizeof(tBTA_HD_REGISTER_APP));
// 设置消息头的事件类型为注册应用事件
p_buf->hdr.event = BTA_HD_API_REGISTER_APP_EVT;
// 处理应用名称
if (p_app_info->p_name) {
strlcpy(p_buf->name, p_app_info->p_name, BTA_HD_APP_NAME_LEN);
} else {
p_buf->name[0] = '\0';
}
// 处理应用描述
if (p_app_info->p_description) {
strlcpy(p_buf->description, p_app_info->p_description,
BTA_HD_APP_DESCRIPTION_LEN);
} else {
p_buf->description[0] = '\0';
}
// 处理应用提供商
if (p_app_info->p_provider) {
strlcpy(p_buf->provider, p_app_info->p_provider, BTA_HD_APP_PROVIDER_LEN);
} else {
p_buf->provider[0] = '\0';
}
// 复制应用子类信息
p_buf->subclass = p_app_info->subclass;
// 处理应用描述符
if (p_app_info->descriptor.dl_len > BTA_HD_APP_DESCRIPTOR_LEN) {
p_app_info->descriptor.dl_len = BTA_HD_APP_DESCRIPTOR_LEN;
}
p_buf->d_len = p_app_info->descriptor.dl_len;
memcpy(p_buf->d_data, p_app_info->descriptor.dsc_list,
p_app_info->descriptor.dl_len);
// 复制输入和输出的 QoS 信息
memcpy(&p_buf->in_qos, p_in_qos, sizeof(tBTA_HD_QOS_INFO));
memcpy(&p_buf->out_qos, p_out_qos, sizeof(tBTA_HD_QOS_INFO));
// 发送注册消息到蓝牙协议栈
bta_sys_sendmsg(p_buf);
}
将一个 HID应用程序注册到蓝牙协议栈的 HID 服务中。会收集应用程序的相关信息,包括应用名称、描述、提供商、子类、描述符以及 QoS(Quality of Service,服务质量)信息等,然后将这些信息封装成一个消息,通过 bta_sys_sendmsg
发送给蓝牙协议栈进行处理。
bta_hd_better_state_machine(BTA_HD_API_REGISTER_APP_EVT)
bta_hd_register_act
packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/*******************************************************************************
*
* Function bta_hd_register_act
*
* Description Registers SDP record
*
* Returns void
*
******************************************************************************/
void bta_hd_register_act(tBTA_HD_DATA* p_data) {
tBTA_HD ret;
tBTA_HD_REGISTER_APP* p_app_data = (tBTA_HD_REGISTER_APP*)p_data;
bool use_report_id = FALSE;
log::verbose("");
ret.reg_status.in_use = FALSE;
// 1. 描述符验证与检查
/* Check if len doesn't exceed BTA_HD_APP_DESCRIPTOR_LEN and descriptor
* itself is well-formed. Also check if descriptor has Report Id item so we
* know if report will have prefix or not. */
if (p_app_data->d_len > BTA_HD_APP_DESCRIPTOR_LEN ||
!check_descriptor(p_app_data->d_data, p_app_data->d_len,
&use_report_id)) {
log::error("Descriptor is too long or malformed");
ret.reg_status.status = BTA_HD_ERROR;
(*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); // 通知上层注册失败
bluetooth::shim::CountCounterMetrics(
android::bluetooth::CodePathCounterKeyEnum::
HIDD_REGISTER_DESCRIPTOR_MALFORMED,
1);
return;
}
// 2. 注册成功处理
ret.reg_status.status = BTA_HD_OK;
/* Remove old record if for some reason it's already registered */
if (bta_hd_cb.sdp_handle != 0) {
get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(bta_hd_cb.sdp_handle);
}
// 更新控制块状态
bta_hd_cb.use_report_id = use_report_id;
bta_hd_cb.sdp_handle = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord(); // 创建新的 SDP 记录句柄
// 向 SDP 记录中添加 HID 设备信息
HID_DevAddRecord(bta_hd_cb.sdp_handle, p_app_data->name,
p_app_data->description, p_app_data->provider,
p_app_data->subclass, p_app_data->d_len, p_app_data->d_data);
// 添加 HID 服务 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 设备应用注册的执行核心,主要负责:
-
验证设备描述符:检查 HID 描述符的格式和长度是否合法。
-
管理 SDP 记录:创建、更新或删除服务发现协议(SDP)记录,确保设备信息可被其他蓝牙设备发现。
-
配置 QoS 参数:设置输入 / 输出服务质量参数,保障数据传输的稳定性。
-
QoS 作用:
-
输入 QoS:定义设备接收数据时的带宽、延迟等参数(如键盘按键数据的实时性要求)。
-
输出 QoS:定义设备发送数据时的服务质量(如主机响应控制命令的延迟限制)。
-
-
-
更新设备状态:通过回调函数通知上层应用注册结果,并启用设备的连接接收策略。
意义与最佳实践
设备发现的基础:正确的 SDP 记录是其他设备发现 HID 服务的前提,若描述符错误或 SDP 记录缺失,设备将无法被搜索到。
实时性保障:QoS 参数的合理配置(如低延迟、高优先级)对键盘、鼠标等实时性要求高的设备至关重要,否则可能导致输入滞后。
状态机管理:通过
in_use
标志和sdp_handle
确保设备注册状态的唯一性,避免重复注册或资源泄漏。
SDP_CreateRecord
SDP_DeleteRecord和SDP_CreateRecord分析见【Bluedroid】蓝牙 SDP(服务发现协议)模块代码解析与流程梳理-CSDN博客
HID_DevAddRecord
packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/*******************************************************************************
*
* Function HID_DevAddRecord
*
* Description Creates SDP record for HID device
*
* Returns tHID_STATUS
*
******************************************************************************/
tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, char* p_description,
char* p_provider, uint16_t subclass,
uint16_t desc_len, uint8_t* p_desc_data) {
bool result = TRUE;
log::verbose("");
// 1. 添加服务类 UUID(必选)
// Service Class ID 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)记录,使设备能够被其他蓝牙设备发现和连接。其核心任务包括:
-
定义服务类型:声明设备支持 HID 服务(通过 UUID 标识)。
-
配置协议栈:指定使用的协议(L2CAP、HIDP)及对应的 PSM(协议服务复用器)。
-
填充设备元数据:包括设备名称、描述、提供商等信息。
-
设置 HID 特定属性:如设备子类、报告描述符、语言支持、电源管理等。
-
定义设备的功能(如按键、坐标轴)、报告格式和数据含义,是 HID 设备的核心配置。
-
必须符合 HID 描述符规范,否则主机无法正确解析数据。
-
-
验证与错误处理:确保记录格式正确,处理内存分配失败等异常情况。
-
SDP 属性 ID
属性 ID | 描述 |
ATTR_ID_SERVICE_NAME | 服务名称 |
ATTR_ID_HID_DEVICE_SUBCLASS | HID 设备子类 |
ATTR_ID_HID_DESCRIPTOR_LIST | 报告描述符列表 |
UUID_SERVCLASS_HUMAN_INTERFACE | HID 服务 UUID |
7. PSM 值
-
控制通道:
BT_PSM_HIDC
(0x1124),用于传输控制命令。 -
中断通道:
BT_PSM_HIDI
(0x1125),用于传输实时数据(如按键、鼠标移动)
意义与最佳实践
设备发现的基石:正确的 SDP 记录是蓝牙设备扫描和连接的前提,缺失或错误的 UUID 或协议配置会导致设备无法被识别。
兼容性关键:报告描述符必须严格遵循 HID 规范,否则主机(如手机、电脑)无法解析设备功能,导致连接后无法正常使用。
性能优化:通过分离控制通道和中断通道,确保实时数据(如鼠标移动)通过独立通道传输,减少延迟,提升用户体验。
btif_hd_free_buf()
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
static void btif_hd_free_buf() {
if (app_info.descriptor.dsc_list) osi_free(app_info.descriptor.dsc_list);
if (app_info.p_description) osi_free(app_info.p_description);
if (app_info.p_name) osi_free(app_info.p_name);
if (app_info.p_provider) osi_free(app_info.p_provider);
app_info.descriptor.dsc_list = NULL;
app_info.p_description = NULL;
app_info.p_name = NULL;
app_info.p_provider = NULL;
}
释放 HID 应用注册过程中分配的动态内存,避免内存泄漏。主要用于清理 app_info
结构体中指向字符串和描述符数据的指针所占用的内存,确保在应用注销或资源释放时正确回收内存资源。
三、时序图
四、总结
HID 设备服务注册是蓝牙协议栈中设备发现与连接的基础,涉及多层协议交互与状态管理:
-
初始化阶段:通过 L2CAP 通道注册和安全配置,建立数据传输链路。
-
发现阶段:通过 SDP 记录的精确构建,使设备可被主机扫描和识别,其中报告描述符的合规性是兼容性的关键。
-
资源管理:动态内存的分配与释放、状态标志的维护(如
in_use
、sdp_handle
)确保服务的可靠性与稳定性。
蓝牙 HID 设备服务的注册与启用流程涉及多个关键环节,包括服务注册、SDP 记录创建、L2CAP 通道配置以及 HID 描述符处理等。通过对 Bluedroid 协议栈中相关代码的详细分析,揭示了这些环节之间的内在联系和协作机制。对于蓝牙协议栈开发者而言,深入理解这些流程有助于优化 HID 设备的实现,提升设备的兼容性和稳定性。同时,对于嵌入式系统工程师以及对 HID 协议实现感兴趣的读者而言,本文也提供了宝贵的技术参考。