【Bluedroid】蓝牙 HID DEVICE 初始化流程源码解析

本文深入剖析Android蓝牙协议栈中HID设备(BT-HD)服务的初始化与启用流程,从接口初始化、服务掩码管理、服务请求路由到属性回调通知,完整展现蓝牙HID服务激活的技术路径。通过代码逻辑梳理,揭示服务启用的核心机制,包括服务状态掩码更新、跨模块调用链及线程安全的属性传递设计。

一、概述

蓝牙 HID 设备(如键盘、鼠标)的服务启用与状态同步是蓝牙协议栈的核心功能之一。整个流程涉及多个模块协作,包括服务初始化、协议栈请求分发、属性获取及跨线程通知,具体可分为以下阶段:

1.1 服务初始化与启用

  1. 初始化接口:通过init函数完成 BT-HD 接口初始化,配置回调函数并调用btif_enable_service(BTA_HIDD_SERVICE_ID)启用 HID 服务。

  2. 服务掩码更新btif_enable_service将目标服务(BTA_HIDD_SERVICE_ID)添加到全局服务掩码(btif_enabled_services),若蓝牙已激活(btif_is_enabled()),则触发服务启用请求。

1.2 协议栈请求分发

  1. 服务请求执行btif_dm_enable_service调用btif_in_execute_service_request执行服务启用请求。若成功,触发属性变更通知流程。

  2. 请求路由btif_in_execute_service_request根据服务类型分发请求:SDP 服务特殊处理,其他服务通过toggleProfile路由到具体服务模块(如 HID 服务由btif_hd_execute_service处理)。

1.3 服务状态同步与属性通知

  1. 属性填充与获取

    1. 通过宏BTIF_STORAGE_FILL_PROPERTY初始化bt_property_t结构体,指定属性类型(如BT_PROPERTY_UUIDS)。

    2. btif_storage_get_adapter_property根据属性类型从控制器或存储中获取具体值(如蓝牙地址、已配对设备列表、已启用服务的 UUID 列表)。

  2. 跨线程回调通知invoke_adapter_properties_cb通过深拷贝(property_deep_copy_array)确保属性数据线程安全,结合do_in_jni_thread将变更事件异步传递到 JNI 线程,触发上层回调(如 Java 层)并释放资源。

二、源码解析

init

packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function        init** Description     Initializes BT-HD interface** Returns         BT_STATUS_SUCCESS*******************************************************************************/
btif_hd_cb_t btif_hd_cb;static bt_status_t init(bthd_callbacks_t* callbacks) {log::verbose("");bt_hd_callbacks = callbacks;memset(&btif_hd_cb, 0, sizeof(btif_hd_cb));btif_enable_service(BTA_HIDD_SERVICE_ID); // 启用 HID 服务return BT_STATUS_SUCCESS;
}

初始化蓝牙 HID 设备(BT-HD)接口,配置回调函数并启用 HID 服务。

btif_enable_service(BTA_HIDD_SERVICE_ID)

packages/modules/Bluetooth/system/btif/src/btif_core.cc
/********************************************************************************* Function         btif_enable_service** Description      Enables the service 'service_ID' to the service_mask.*                  Upon BT enable, BTIF core shall invoke the BTA APIs to*                  enable the profiles*******************************************************************************/
void btif_enable_service(tBTA_SERVICE_ID service_id) {btif_enabled_services |= (1 << service_id); // 服务掩码更新log::verbose("current services:0x{:x}", btif_enabled_services);if (btif_is_enabled()) {btif_dm_enable_service(service_id, true);}
}

将指定的蓝牙服务添加到已启用服务掩码中,并在蓝牙已激活时立即启动该服务。

btif_dm_enable_service

packages/modules/Bluetooth/system/btif/src/btif_dm.cc
void btif_dm_enable_service(tBTA_SERVICE_ID service_id, bool enable) {// 执行服务请求bt_status_t status = btif_in_execute_service_request(service_id, enable);if (status == BT_STATUS_SUCCESS) {// 准备 UUID 属性bt_property_t property;Uuid local_uuids[BT_MAX_NUM_UUIDS]; // 存储设备支持的 UUID 列表/* Now send the UUID_PROPERTY_CHANGED event to the upper layer */// 从存储中获取当前设备的 UUID 属性BTIF_STORAGE_FILL_PROPERTY(&property, BT_PROPERTY_UUIDS,sizeof(local_uuids), local_uuids);btif_storage_get_adapter_property(&property);// 通过回调通知上层应用适配器属性已变更GetInterfaceToProfiles()->events->invoke_adapter_properties_cb(BT_STATUS_SUCCESS, 1, &property);}return;
}

通过蓝牙协议栈执行服务启用 / 禁用请求,并在成功时通知上层应用。

btif_in_execute_service_request

packages/modules/Bluetooth/system/btif/src/btif_dm.cc
bt_status_t btif_in_execute_service_request(tBTA_SERVICE_ID service_id,bool b_enable) {log::verbose("service_id:{}", service_id);if (service_id == BTA_SDP_SERVICE_ID) { // SDP 服务特殊处理btif_sdp_execute_service(b_enable);return BT_STATUS_SUCCESS;}// 通用服务处理return GetInterfaceToProfiles()->toggleProfile(service_id, b_enable);
}

将上层的服务启用 / 禁用请求路由到对应的处理模块。

toggleProfile(BTA_HIDD_SERVICE_ID)
packages/modules/Bluetooth/system/btif/src/bluetooth.ccbt_status_t toggleProfile(tBTA_SERVICE_ID service_id, bool enable) override {/* Check the service_ID and invoke the profile's BT state changed API */switch (service_id) {case BTA_HFP_SERVICE_ID:case BTA_HSP_SERVICE_ID: {bluetooth::headset::ExecuteService(enable);} break;case BTA_A2DP_SOURCE_SERVICE_ID: {btif_av_source_execute_service(enable);} break;case BTA_A2DP_SINK_SERVICE_ID: {btif_av_sink_execute_service(enable);} break;case BTA_HID_SERVICE_ID: {btif_hh_execute_service(enable);} break;case BTA_HFP_HS_SERVICE_ID: {btif_hf_client_execute_service(enable);} break;case BTA_HIDD_SERVICE_ID: {btif_hd_execute_service(enable);} break;case BTA_PBAP_SERVICE_ID:case BTA_PCE_SERVICE_ID:case BTA_MAP_SERVICE_ID:case BTA_MN_SERVICE_ID: {/*** Do nothing; these services were started elsewhere. However, we need* to flow through this codepath in order to properly report back the* local UUIDs back to adapter properties in Java. To achieve this, we* need to catch these service IDs in order for {@link* btif_in_execute_service_request} to return {@code BT_STATUS_SUCCESS},* so that in {@link btif_dm_enable_service} the check passes and the* UUIDs are allowed to be passed up into the Java layer.*/} break;default:log::error("Unknown service {} being {}", service_id,(enable) ? "enabled" : "disabled");return BT_STATUS_FAIL;}return BT_STATUS_SUCCESS;}

将通用的服务启用 / 禁用请求转发到具体的服务处理模块。

btif_hd_execute_service
packages/modules/Bluetooth/system/btif/src/btif_hd.cc
/********************************************************************************* Function         btif_hd_execute_service** Description      Enabled/disables BT-HD service** Returns          BT_STATUS_SUCCESS*******************************************************************************/
bt_status_t btif_hd_execute_service(bool b_enable) {log::verbose("b_enable={}", b_enable);if (!b_enable) BTA_HdDisable();return BT_STATUS_SUCCESS;
}

控制蓝牙 HID 设备服务的开启与关闭。

BTIF_STORAGE_FILL_PROPERTY

/modules/Bluetooth/system/btif/include/btif_storage.h
#define BTIF_STORAGE_FILL_PROPERTY(p_prop, t, l, p_v) \do {                                                \(p_prop)->type = (t);                             \(p_prop)->len = (l);                              \(p_prop)->val = (p_v);                            \} while (0)

初始化bt_property_t结构体的三个核心字段。

btif_storage_get_adapter_property

packages/modules/Bluetooth/system/btif/src/btif_storage.cc
/********************************************************************************* Function         btif_storage_get_adapter_property** Description      BTIF storage API - Fetches the adapter property->type*                  from NVRAM and fills property->val.*                  Caller should provide memory for property->val and*                  set the property->val** Returns          BT_STATUS_SUCCESS if the fetch was successful,*                  BT_STATUS_FAIL otherwise*******************************************************************************/
bt_status_t btif_storage_get_adapter_property(bt_property_t* property) {/* Special handling for adapter address and BONDED_DEVICES */if (property->type == BT_PROPERTY_BDADDR) { // 蓝牙地址获取RawAddress* bd_addr = (RawAddress*)property->val;/* Fetch the local BD ADDR */const controller_t* controller = controller_get_interface();if (!controller->get_is_ready()) {log::error("Controller not ready! Unable to return Bluetooth Address");*bd_addr = RawAddress::kEmpty;return BT_STATUS_FAIL;} else {log::info("Controller ready!");*bd_addr = *controller->get_address(); // 从蓝牙控制器获取本地设备地址}property->len = RawAddress::kLength;return BT_STATUS_SUCCESS;} else if (property->type == BT_PROPERTY_ADAPTER_BONDED_DEVICES) { // 已配对设备列表获取btif_bonded_devices_t bonded_devices;// 获取已配对设备列表btif_in_fetch_bonded_devices(&bonded_devices, 0);log::verbose("BT_PROPERTY_ADAPTER_BONDED_DEVICES: Number of bonded devices={}",bonded_devices.num_devices);property->len = bonded_devices.num_devices * RawAddress::kLength;// 将设备地址复制到属性值缓冲区memcpy(property->val, bonded_devices.devices, property->len);/* if there are no bonded_devices, then length shall be 0 */return BT_STATUS_SUCCESS;} else if (property->type == BT_PROPERTY_UUIDS) { // UUID 列表生成/* publish list of local supported services */Uuid* p_uuid = reinterpret_cast<Uuid*>(property->val);uint32_t num_uuids = 0;uint32_t i;tBTA_SERVICE_MASK service_mask = btif_get_enabled_services_mask();log::info("Service_mask=0x{:x}", service_mask);// 根据已启用服务掩码生成对应的 UUID 列表for (i = 0; i < BTA_MAX_SERVICE_ID; i++) {/* This should eventually become a function when more services are enabled*/if (service_mask & (tBTA_SERVICE_MASK)(1 << i)) {switch (i) {case BTA_HFP_SERVICE_ID: {*(p_uuid + num_uuids) =Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE);num_uuids++;}FALLTHROUGH_INTENDED; /* FALLTHROUGH *//* intentional fall through: Send both BFP & HSP UUIDs if HFP is* enabled */case BTA_HSP_SERVICE_ID: {*(p_uuid + num_uuids) =Uuid::From16Bit(UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY);num_uuids++;} break;case BTA_A2DP_SOURCE_SERVICE_ID: {*(p_uuid + num_uuids) =Uuid::From16Bit(UUID_SERVCLASS_AUDIO_SOURCE);num_uuids++;} break;case BTA_A2DP_SINK_SERVICE_ID: {*(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_AUDIO_SINK);num_uuids++;} break;case BTA_PBAP_SERVICE_ID: {*(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PSE);num_uuids++;} break;case BTA_HFP_HS_SERVICE_ID: {*(p_uuid + num_uuids) =Uuid::From16Bit(UUID_SERVCLASS_HF_HANDSFREE);num_uuids++;} break;case BTA_MAP_SERVICE_ID: {*(p_uuid + num_uuids) =Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_ACCESS);num_uuids++;} break;case BTA_MN_SERVICE_ID: {*(p_uuid + num_uuids) =Uuid::From16Bit(UUID_SERVCLASS_MESSAGE_NOTIFICATION);num_uuids++;} break;case BTA_PCE_SERVICE_ID: {*(p_uuid + num_uuids) = Uuid::From16Bit(UUID_SERVCLASS_PBAP_PCE);num_uuids++;} break;}}}property->len = (num_uuids) * sizeof(Uuid);return BT_STATUS_SUCCESS;}/* fall through for other properties */// 通用属性处理if (!cfg2prop(NULL, property)) {return btif_dm_get_adapter_property(property);}return BT_STATUS_SUCCESS;
}

根据属性类型从 NVRAM 或控制器中获取适配器属性值。

invoke_adapter_properties_cb

/packages/modules/Bluetooth/system/btif/src/bluetooth.cc
void invoke_adapter_properties_cb(bt_status_t status, int num_properties,bt_property_t* properties) {do_in_jni_thread(FROM_HERE,base::BindOnce( // 创建一次性回调(避免重复执行)[](bt_status_t status, int num_properties,bt_property_t* properties) {// 步骤1:触发上层回调(HAL层注册的回调)HAL_CBACK(bt_hal_cbacks, adapter_properties_cb, status,num_properties, properties);if (properties) {// 步骤2:释放深拷贝的内存(避免泄漏)osi_free(properties);}},status, num_properties,property_deep_copy_array(num_properties, properties))); //深拷贝属性数组
}// callback reporting helpersbt_property_t* property_deep_copy_array(int num_properties,bt_property_t* properties) {bt_property_t* copy = nullptr;if (num_properties > 0) {// 1:计算所有属性值的总长度(用于分配内存)size_t content_len = 0;for (int i = 0; i < num_properties; i++) {auto len = properties[i].len;if (len > 0) {content_len += len;}}//2:一次性分配连续内存(结构体数组 + 属性值内容)copy = (bt_property_t*)osi_calloc((sizeof(bt_property_t) * num_properties) + content_len);ASSERT(copy != nullptr);// 3:复制属性结构体,并将属性值指向新分配的内容区uint8_t* content = (uint8_t*)(copy + num_properties);    for (int i = 0; i < num_properties; i++) {auto len = properties[i].len;copy[i].type = properties[i].type;copy[i].len = len;if (len <= 0) {continue;}copy[i].val = content;  // 指向新内容区memcpy(content, properties[i].val, len);content += len; // 移动到下一个属性值位置}}return copy;
}

蓝牙适配器属性回调的跨线程调用机制,确保从 HAL 层到 JNI 层的属性通知能够正确处理。

三、时序图

四、总结

①模块化设计

  • 流程严格分层:接口初始化→服务管理→请求路由→具体实现→属性通知,各模块职责单一(如btif_hd专注HID逻辑,btif_dm处理通用服务管理)。

②状态驱动机制

  • 通过服务掩码btif_enabled_services全局追踪服务状态,结合btif_is_enabled()判断实时激活状态,避免无效操作。

③线程安全与内存管理

  • 属性回调采用深拷贝(property_deep_copy_array)与osi_free释放,确保跨线程(如JNI)数据传递安全性。

④扩展性设计

  • 服务ID与UUID映射表(如BTA_HFP_SERVICE_IDUUID_SERVCLASS_AG_HANDSFREE)支持快速扩展新服务,仅需修改映射逻辑。

通过服务掩码与回调机制的协同,实现蓝牙服务的动态启用与上层应用的实时同步,为HID设备(如键盘、游戏手柄)的即插即用提供底层支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/80621.shtml

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

相关文章

2025年项目管理软件革命:中国技术主权与全球创新浪潮的交锋

全球项目管理软件市场正在经历一场由多重技术叠加引发的结构性变革。根据Gartner最新预测&#xff0c;到2025年项目管理工具市场规模将突破220亿美元&#xff0c;其中中国市场增速达38%&#xff0c;远超全球平均水平。这场变革不仅关乎工具功能迭代&#xff0c;更深刻影响着企业…

计算机组成与体系结构:组相联映射(Set-Associative Mapping)

目录 &#x1f9e9; 映射方式问题回顾 &#x1f3d7;️ 组相联映射 工作流程 地址结构 ♻️ 替换策略 示例&#xff1a; 优点 ⚖️ 与其他映射方式对比 &#x1f9e9; 映射方式问题回顾 直接映射的问题&#xff1a; 优点&#xff1a;实现简单&#xff0c;查找速度快…

机器学习第八讲:向量/矩阵 → 数据表格的数学表达,如Excel表格转数字阵列

机器学习第八讲&#xff1a;向量/矩阵 → 数据表格的数学表达&#xff0c;如Excel表格转数字阵列 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章&#xff1a;DeepSeek R1本地与线上满血版部署&#xff1a;…

基于Spring AI实现多轮对话系统架构设计

文章目录 基于Spring AI实现多轮对话系统架构设计 前言 一、多轮对话系统核心架构 1.1 架构概览 1.2 Spring AI核心优势 二、ChatClient与多轮对话设计 2.1 ChatClient的特性与角色 2.2 实现多轮对话方法 三、Advisors拦截器机制 3.1 Advisors概念与工作原理 3.2 对…

C++中的虚表和虚表指针的原理和示例

一、基本概念 1. 什么是虚函数&#xff08;virtual function&#xff09;&#xff1f; 虚函数是用 virtual 关键字修饰的成员函数&#xff0c;支持运行时多态&#xff08;dynamic polymorphism&#xff09;。通过基类指针或引用调用派生类重写的函数。 class Base { public:…

FPGA:XILINX FPGA产品线以及器件选型建议

本文将详细介绍Xilinx&#xff08;现为AMD的一部分&#xff09;当前的FPGA产品线及其主要特点&#xff0c;并提供器件选型的建议。以下内容基于Xilinx FPGA的最新信息&#xff0c;涵盖产品系列、特性及选型指导。由于Xilinx已被AMD收购&#xff0c;产品线以AMD Xilinx品牌为主&…

【C++】多线程和多进程

在C++中,多线程通信(同一进程内的线程间交互)和进程间通信(IPC,不同进程间的数据交换)是构建并发系统的核心技术。以下是两种通信机制的详细介绍和典型实现: 一、多线程通信(线程间同步与数据共享) 1. 共享内存与同步原语 通过全局变量或对象成员变量实现数据共享,…

PC Cleaner软件,它能帮助用户轻松清理和优化电脑,提升系统性能。

不用破解就能用&#xff01;这款超神的电脑清理 Pro 版&#xff0c;绝了&#xff01; 宝子们&#xff0c;我是你们的数码小助手蓝木云&#xff01;不知道大家有没有这种感觉&#xff0c;电脑用久了&#xff0c;就像住久了没打扫的屋子&#xff0c;越来越 “乱”&#xff0c;运…

linux中fork()函数的小问题

问题描述&#xff1a;分析下列代码&#xff0c;分别能产生多少a // 1 for(int i0; i<3; i){ printf("a\n"); fork(); }// 2 for(int i0; i<3; i){ fork(); printf("a\n"); }// 3 for(int i0; i<3; i){ fork(); printf("a"); } fflus…

阿克曼-幻宇机器人系列教程2- 机器人交互实践(Topic)

在上一篇文章中&#xff0c;我们介绍了两种登录机器人的方式&#xff0c;接下来我们介绍登录机器人之后&#xff0c;我们如何通过topic操作命令实现与机器人的交互。 1. 启动 & 获取topic 在一个终端登录树莓派后&#xff0c;执行下列命令运行机器人 roslaunch huanyu_r…

51c嵌入式~电路~合集27

我自己的原文哦~ 一、7805应用电路 简介 如上图&#xff0c;7805 集成稳压电路。 7805是串联式三端稳压器&#xff0c;三个端口分别是电压输入端&#xff08;IN&#xff09;&#xff0c;地线&#xff08;GND&#xff09;&#xff0c;稳压输出&#xff08;OUT&#xff09;…

Vitrualbox完美显示系统界面(只需三步)

目录 1.使用vitrualbox的增强功能&#xff1a;​编辑 2.安装增强功能&#xff08;安装完后要重启虚拟机&#xff09;&#xff1a; 3. 调整界面尺寸&#xff08;如果一个选项不行的话&#xff0c;就多试试其他不同的百分比&#xff09;&#xff1a; 先看看原来的&#xff0c;…

2025年第十六届蓝桥杯软件赛省赛C/C++大学A组个人解题

文章目录 题目A题目C&#xff1a;抽奖题目D&#xff1a;红黑树题目E&#xff1a;黑客题目F&#xff1a;好串的数目 https://www.dotcpp.com/oj/train/1166/ 题目A 找到第2025个素数 #include <iostream> #include <vector> using namespace std; vector<i…

电机控制储备知识学习(一) 电机驱动的本质分析以及与磁相关的使用场景

目录 电机控制储备知识学习&#xff08;一&#xff09;一、电机驱动的本质分析以及与磁相关的使用场景1&#xff09;电机为什么能够旋转2&#xff09;电磁原理的学习重要性 二、电磁学理论知识1&#xff09;磁场基础知识2&#xff09;反电动势的公式推导 附学习参考网址欢迎大家…

JMeter同步定时器 模拟多用户并发访问场景

同步定时器 JMter同步定时器的作用主要在于模拟多用户并发访问的场景&#xff0c;确保多个线程能够同时执行某个操作&#xff0c;达到真正的并发效果。 当多个线程同时启动时&#xff0c;它们可能会在不同的时间间隔内执行&#xff0c;这样就无法达到真正的并发效果。&#xff…

C++11异步编程 --- async

C11异步编程 — async和future C11引入了async和future机制&#xff0c;用于简化异步编程和并发操作。这两个组件位于<future>头文件中&#xff0c;提供了高级的异步任务管理接口。 一、async 1.定义 std::async std::async是一个函数模板&#xff0c;用于启动一个异…

(七)深度学习---神经网络原理与实现

分类问题回归问题聚类问题各种复杂问题决策树√线性回归√K-means√神经网络√逻辑回归√岭回归密度聚类深度学习√集成学习√Lasso回归谱聚类条件随机场贝叶斯层次聚类隐马尔可夫模型支持向量机高斯混合聚类LDA主题模型 一.神经网络原理概述 二.神经网络的训练方法 三.基于Ker…

[Java实战]Spring Boot 整合 Swagger2 (十六)

[Java实战]Spring Boot 整合 Swagger2 &#xff08;十六&#xff09; 一、Swagger 的价值与痛点 为什么需要 API 文档工具&#xff1f; 开发阶段&#xff1a;前后端高效协作&#xff0c;实时验证接口测试阶段&#xff1a;提供标准化测试用例维护阶段&#xff1a;降低新人理解…

系统稳定性之上线三板斧

&#x1f4d5;我是廖志伟&#xff0c;一名Java开发工程师、《Java项目实战——深入理解大型互联网企业通用技术》&#xff08;基础篇&#xff09;、&#xff08;进阶篇&#xff09;、&#xff08;架构篇&#xff09;清华大学出版社签约作家、Java领域优质创作者、CSDN博客专家、…

题海拾贝:P1833 樱花

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞&#xff0c;关注&am…