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

【Bluedroid】HID DEVICE 连接的源码分析

本文分析Android Bluetooth协议栈中HID device设备连接流程的完整实现,从应用层接口到协议栈底层的交互细节。通过关键函数(如connect()BTA_HdConnect()HID_DevConnect()等)的代码解析,重点关注btifbtaHID协议栈三层的协同机制,揭示BTA_HD_CONN_STATE_EVT事件传递和HAL_CBACK回调触发原理,为蓝牙HID开发提供系统性技术参考。

一、概述

1.1 连接触发与初始校验

  • 应用层入口connect()函数作为上层接口,首先校验应用注册状态(app_registered)和HID模块启用状态(BTIF_HD_ENABLED),确保基础条件满足后调用底层BTA_HdConnect()

  • BTA层封装BTA_HdConnect()分配内存缓冲区,封装连接事件类型(BTA_HD_API_CONNECT_EVT)和目标地址,通过bta_sys_sendmsg()将消息传递至协议栈。

1.2 协议栈状态机驱动

  • 状态机入口bta_hd_connect_act()处理连接请求,首先调用HID_DevPlugDevice()建立虚拟线缆(标记设备为in_use),随后通过HID_DevConnect()发起物理连接。

  • 严格状态检查

    • 注册状态:若设备未注册(!hd_cb.reg_flag),直接返回HID_ERR_NOT_REGISTERED

    • 设备占用:检查虚拟线缆是否已建立(hd_cb.device.in_use)。

    • 连接状态:防止重复连接(hd_cb.device.state != HIDD_DEV_NO_CONN)。

1.3 L2CAP连接初始化

  • 底层连接hidd_conn_initiate()初始化控制/中断通道CID,调用L2CA_ConnectReq2()启动L2CAP连接,参数包含HID协议PSM(HID_PSM_CONTROL)和安全要求(认证/加密)。

  • 连接状态跟踪:根据L2CAP返回结果更新连接状态(HID_CONN_STATE_CONNECTING_CTRL),失败时通过回调通知上层。

1.4 事件上报与回调通知

  • 上下文切换bte_hd_evt()将协议栈事件(如BTA_HD_CONN_STATE_EVT)通过btif_transfer_context()切换至BTIF线程。

  • HAL回调btif_hd_upstreams_evt()最终触发connection_state_cb,向应用层传递连接状态(如BTHD_CONN_STATE_CONNECTING)。

二、源码分析

connect

packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function         connect** Description      Connects to host** Returns          bt_status_t*******************************************************************************/
static bt_status_t connect(RawAddress* bd_addr) {log::verbose("");if (!btif_hd_cb.app_registered) {log::warn("application not yet registered");return BT_STATUS_NOT_READY;}if (btif_hd_cb.status != BTIF_HD_ENABLED) {log::warn("BT-HD not enabled, status={}", btif_hd_cb.status);return BT_STATUS_NOT_READY;}BTA_HdConnect(*bd_addr); // 发起连接请求return BT_STATUS_SUCCESS;
}

尝试连接到指定的蓝牙主机设备。在进行连接操作之前,先检查应用程序是否已经注册以及蓝牙 HID 设备(BT - HD)是否已经启用。若条件满足,则调用 BTA_HdConnect 函数发起连接请求。

BTA_HdConnect

packages/modules/Bluetooth/system/bta/hd/bta_hd_api.cc
/********************************************************************************* Function         BTA_HdConnect** Description      This function is called when connection to host shall be
*made** Returns          void*******************************************************************************/
void BTA_HdConnect(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_CONNECT_EVT;p_buf->addr = addr;bta_sys_sendmsg(p_buf);
}

发起与hid host的连接请求。分配一块内存用于存储连接事件的相关信息,设置事件类型和目标设备地址,然后将该消息发送出去,以触发后续的连接操作。

bta_hd_better_state_machine(BTA_HD_API_CONNECT_EVT)

bta_hd_connect_act

packages/modules/Bluetooth/system/bta/hd/bta_hd_act.cc
/********************************************************************************* Function         bta_hd_connect_act** Description      Connect to device (must be virtually plugged)** Returns          void*******************************************************************************/
void bta_hd_connect_act(tBTA_HD_DATA* p_data) {tHID_STATUS ret;tBTA_HD_DEVICE_CTRL* p_ctrl = (tBTA_HD_DEVICE_CTRL*)p_data;tBTA_HD cback_data;log::verbose("");// 设备插入操作ret = HID_DevPlugDevice(p_ctrl->addr);if (ret != HID_SUCCESS) {log::warn("HID_DevPlugDevice returned {}", ret);return;}// 设备连接操作ret = HID_DevConnect();if (ret != HID_SUCCESS) {log::warn("HID_DevConnect returned {}", ret);return;}//设置回调数据cback_data.conn.bda = p_ctrl->addr;cback_data.conn.status = BTHD_CONN_STATE_CONNECTING;// 调用回调函数bta_hd_cb.p_cback(BTA_HD_CONN_STATE_EVT, &cback_data);
}

连接到一个蓝牙设备,前提是该设备必须处于虚拟插入状态。首先尝试对设备进行插入操作,接着发起连接请求,若这两个步骤都成功,会调用回调函数通知连接状态变为 “正在连接”。

HID_DevPlugDevice

packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* 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_DevConnect

packages/modules/Bluetooth/system/stack/hid/hidd_api.cc
/********************************************************************************* Function         HID_DevConnect** Description      Connects to device** Returns          tHID_STATUS*******************************************************************************/
tHID_STATUS HID_DevConnect(void) {if (!hd_cb.reg_flag) { // 检查设备注册状态log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_CONNECT,1);return HID_ERR_NOT_REGISTERED;}if (!hd_cb.device.in_use) { // 检查设备使用状态log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DEVICE_NOT_IN_USE_AT_CONNECT,1);return HID_ERR_INVALID_PARAM;}if (hd_cb.device.state != HIDD_DEV_NO_CONN) { //  检查设备连接状态log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_ALREADY_CONN, 1);return HID_ERR_ALREADY_CONN;}return hidd_conn_initiate(); // 发起连接操作
}

在真正发起连接之前,会对设备的注册状态、使用状态以及连接状态进行检查,若不符合要求则记录相应的错误指标并返回对应的错误状态码;若所有检查都通过,则调用 hidd_conn_initiate 函数来实际发起连接。

hidd_conn_initiate

packages/modules/Bluetooth/system/stack/hid/hidd_conn.cc
/********************************************************************************* Function         hidd_conn_initiate** Description      Initiates HID connection to plugged device** Returns          HID_SUCCESS*******************************************************************************/
tHID_STATUS hidd_conn_initiate(void) {tHID_DEV_DEV_CTB* p_dev = &hd_cb.device;log::verbose("");// 1. 设备使用状态检查if (!p_dev->in_use) {log::warn("no virtual cable established");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_INITIATE,1);return (HID_ERR_NOT_REGISTERED);}// 2. 连接状态检查if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) {log::warn("connection already in progress");log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_CONN_IN_PROCESS,1);return (HID_ERR_CONN_IN_PROCESS);}// 3. 连接参数初始化p_dev->conn.ctrl_cid = 0;p_dev->conn.intr_cid = 0;p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL;p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG;// 4. 启动 L2CAP 连接请求/* Check if L2CAP started the connection process */if ((p_dev->conn.ctrl_cid =L2CA_ConnectReq2(HID_PSM_CONTROL, p_dev->addr,BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) == 0) {log::warn("could not start L2CAP connection");hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED,NULL);log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_INITIATE,1);} else {p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL;}return (HID_SUCCESS);
}

发起与已插入的 HID的连接。在发起连接前,会对设备的使用状态和连接状态进行检查,确保设备处于可连接的状态。若检查通过,会初始化连接相关的参数,尝试启动 L2CAP连接请求,并根据连接结果更新设备的连接状态。

bte_hd_evt(BTA_HD_CONN_STATE_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);
}

根据不同的蓝牙 HID事件,计算相关参数的长度,并调用 btif_transfer_context 函数进行上下文切换,将事件从 BTE 切换到 BTIF。

btif_hd_upstreams_evt(BTA_HD_CONN_STATE_EVT)

/********************************************************************************* 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:...case BTA_HD_CONN_STATE_EVT:HAL_CBACK(bt_hd_callbacks, connection_state_cb,(RawAddress*)&p_data->conn.bda,(bthd_connection_state_t)p_data->conn.status);break;default:log::warn("unknown event ({})", event);break;}
}

BTA_HD_CONN_STATE_EVT 事件处理部分的核心是调用 HAL_CBACK 宏,将蓝牙设备的地址和连接状态信息传递给 connection_state_cb 回调函数。这样,上层应用程序可以根据连接状态的变化做出相应的处理,例如更新 UI 显示、处理设备连接或断开等操作。

三、时序图

四、总结

蓝牙 HID 设备连接的实现涉及多个函数的协同工作,每个函数都承担着特定的任务,如连接请求的发起、设备状态的检查、连接参数的初始化以及连接状态变化的处理等。通过对设备注册、使用和连接状态的严格检查,确保了连接操作的正确性和稳定性。上下文切换和回调函数的运用,使得上层应用能够及时感知连接状态的变化并做出相应处理,提高了系统的可扩展性和用户体验。

相关文章:

  • LWIP的ICMP协议
  • C++模板梳理
  • 网络编程(一)网络编程入门
  • go-gin
  • Redis--常见数据类型List列表
  • LOJ 6346 线段树:关于时间 Solution
  • 深入解析STM32中断机制:从原理到外部中断实战
  • 轻量级高性能推理引擎MNN 学习笔记 01.初识MNN
  • AJAX原理
  • jenkins built-in节点如何删除
  • python 新闻 api + react js 客户端。
  • 使用Visual Studio将C#程序发布为.exe文件
  • MiniMind:3块钱成本 + 2小时!训练自己的0.02B的大模型。minimind源码解读、MOE架构
  • 《Redis应用实例》学习笔记,第二章:缓存二进制数据
  • OceanBase 4.3版本向量数据库部署
  • OceanBase性能关键参数配置最佳实践
  • 《Effective Python》第1章 Pythonic 思维详解——深入理解 Python 条件表达式(Conditional Expressions)
  • BuildAdmin-v2.3.0 发布
  • 鸿蒙跨平台开发教程之Uniapp布局基础
  • WEBSTORM前端 —— 第2章:CSS —— 第8节:网页制作2(小兔鲜儿)
  • 人民日报钟声:通过平等对话协商解决分歧的重要一步
  • 石家庄推动城市能级与民生福祉并进
  • 贵州省总工会党组成员、副主席梁伟接受审查调查
  • 邯郸一酒店办婚宴发生火灾,新郎母亲:饭没吃成酒店还要收费
  • 华泰柏瑞基金总经理韩勇因工作调整卸任,董事长贾波代为履职
  • 明查|哈佛大学批改美教育部长来信,红笔标出语法错误?