Android12蓝牙框架

参考:
https://evilpan.com/2021/07/11/android-bt/
https://source.android.com/docs/core/connect/bluetooth?hl=zh-cn
https://developer.android.com/guide/topics/connectivity/bluetooth?hl=zh-cn
https://developer.android.com/guide/components/intents-filters?hl=zh-cn

文章目录

    • AIDL Server
        • btm_ble_set_discoverability
        • btsnd_hcic_write_cur_iac_lap
    • HCI 子系统
      • 接收数据
      • 发送数据

  • BTIF: Bluetooth Interface
  • BTU : Bluetooth Upper Layer
  • BTM: Bluetooth Manager
  • BTE: Bluetooth embedded system
  • BTA :Blueetooth application layer
  • CO: call out
  • CI: call in
  • HF : Handsfree Profile
  • HH: HID Host Profile
  • HL: Health Device Profile
  • av: audio/vidio
  • ag: audio gateway
  • ar: audio/video registration
  • gattc: GATT client
  • HIDL: HAL Interface Definition Language
    Android4.2 之后采用 bluedroid 作为协议;整体由 bluetooth.apk,bluedroid ,libbt-vendor 三个 部 分 组 成 。
    在这里插入图片描述
    蓝牙协议栈一方面是以系统服务的方式提供接口,另一方面也以client的方式给应用程序提供SDK,不管怎样,最终都是需要经过HCI协议去与Controller进行交互

对于BlueDroid而言,协议栈是在用户层实现的,内核只暴露出HCI(USB/UART)的接口。因此,我们可以从HCI出发,自底向上进行分析,也可以参考上面的框架图,从用户应用程序开始,自顶向下进行分析

AIDL Server

 bool SetScanMode(int scan_mode) 				system/bt/service/adapter.ccSetAdapterPropertyhal::BluetoothInterface::Get()->GetHALInterface()->set_adapter_propertyset_adapter_property				system/bt/btif/src/bluetooth.ccdo_in_main_thread btif_set_adapter_property		system/bt/btif/src/btif_core.ccBTA_DmSetVisibility			system/bt/bta/dm/bta_dm_act.cc

android12/system/bt/stack/btm/btm_inq.cc

这其中涉及了几个API:
btm_ble_set_discoverability
btsnd_hcic_write_cur_iac_lap
btsnd_hcic_write_inqscan_cfg
btsnd_hcic_write_scan_enable
btm_ble_set_discoverability

第一个API是BLE相关,内部实际上最终也调用了btsnd_hcic_xxx的类似接口。IAC意为Inquiry Access Code,蓝牙baseband定义了几个固定IAC,分别是LIAC和GIAC(见baseband)。LAP是蓝牙地址的一部分,如下图所示:

BDADDR

  • NAP: Non-significant Address Part, NAP的值在跳频同步帧中会用到
  • UAP: Upper Address Part,UAP的值会参与对蓝牙协议算法的选择
  • LAP: Lower Address Part,由设备厂商分配,LAP的值作为Access Code的一部分,唯一确定某个蓝牙设备
  • SAP (significant address part) = UAP + LAP
btsnd_hcic_write_cur_iac_lap
// system/bt/stack/hcic/hcicmds.cc
void btsnd_hcic_write_cur_iac_lap(uint8_t num_cur_iac, LAP* const iac_lap) {btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
}
/UINTx_TO_STREAM(pp, n)的作用是将整数以小端的形式写入p->data中,最终调用btu_hcif_send_cmd函数发送数据// system/btstack/btu/btu_hcif.cc
void btu_hcif_send_cmd(UNUSED_ATTR uint8_t controller_id, BT_HDR* p_buf) {uint8_t* stream = p_buf->data + p_buf->offset;stream++;btu_hcif_log_command_metrics(opcode, stream,android::bluetooth::hci::STATUS_UNKNOWN, false);hci_layer_get_interface()->transmit_command(p_buf, btu_hcif_command_complete_evt, btu_hcif_command_status_evt,vsc_callback);
}
//可见p_buf->data中保存的就是HCI数据,前16位为opcode,其中高6字节为ogf,低10字节为ocf,也就是我们平时使用hcitool cmd时的前两个参数

HCI 子系统

继续跟踪transmit_command,就来到了HCI子系统中

transmit_command		system/bt/hci/src/hci_layer.ccenqueue_command		

process_command_credits

其调用链路为:

BluetoothHciCallbacks::hciEventReceived 		system/bt/hci/src/hci_layer_android.cchci_event_received							system/bt/hci/src/hci_layer.ccfilter_incoming_eventprocess_command_credits

接收数据

BluetoothHciCallbacks::hciEventReceived 这个函数回调是在HCI初始化的时候调用的

BluetoothHci::initialize(system/bt/vendor_libs/linux/interface/bluetooth_hci.cc):

Return<void> BluetoothHci::initialize		system/bt/vendor_libs/linux/interface/bluetooth_hci.ccopenBtHci								

fd_watcher_本质上是针对hci_fd文件句柄的读端事件监控,后者由openBtHci函数产生,该函数由厂商实现,接口文件是hardware/interfaces/bluetooth/1.0/IBluetoothHci.hal。在Linux中的参考实现如下:

发送数据

继续回头接着上节之前的内容讲,我们的任务队列是在process_command_credits中被消费的,取出来之后需要进入到hci_thread线程中执行。从接收数据一节中也能看出,hci接口本身使用的是串行总线,因此不能并发地发送数据,所有命令都是在之前的命令响应后再发送。

值得一提的是,enqueue_command实际上绑定的是函数event_command_ready,以包含我们命令内容和对应回调的类型waiting_command_t为参数:

static void enqueue_command(waiting_command_t* wait_entry) {base::Closure callback = base::Bind(&event_command_ready, wait_entry);//...command_queue.push(std::move(callback));
}

因此,负责执行HCI发送命令的是event_command_ready函数:

  static void event_command_ready(waiting_command_t* wait_entry) {{/// Move it to the list of commands awaiting responsestd::lock_guard<std::recursive_timed_mutex> lock(commands_pending_response_mutex);wait_entry->timestamp = std::chrono::steady_clock::now();list_append(commands_pending_response, wait_entry);}// Send it offpacket_fragmenter->fragment_and_dispatch(wait_entry->command);update_command_response_timer();}

首先将command放到一个等待响应的队列里,然后分片发送:

static void fragment_and_dispatch(BT_HDR* packet) {CHECK(packet != NULL);uint16_t event = packet->event & MSG_EVT_MASK;uint8_t* stream = packet->data + packet->offset;// We only fragment ACL packetsif (event != MSG_STACK_TO_HC_HCI_ACL) {callbacks->fragmented(packet, true);return;}// ACL/L2CAP fragment...
}

实现中只对ACL类型的HCI数据进行分片发送,不管是不是分片,都对最后一个packet调用callbacks->fragmented(),callbacks的类型是packet_fragmenter_callbacks_t,在packet_fragmenter_t->init中初始化并设置。而packet_fragmenter的初始化发生在hci_module_start_up()中,HCI层定义的回调如下:

static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks = {  transmit_fragment, dispatch_reassembled, fragmenter_transmit_finished }; 

fragmented即对应transmit_fragment,对应定义如下:

// Callback for the fragmenter to send a fragment static void transmit_fragment(BT_HDR* packet, bool send_transmit_finished) {    btsnoop->capture(packet, false);     // HCI command packets are freed on a different thread when the matching    // event is received. Check packet->event before sending to avoid a race.    bool free_after_transmit =        (packet->event & MSG_EVT_MASK) != MSG_STACK_TO_HC_HCI_CMD &&        send_transmit_finished;     hci_transmit(packet);     if (free_after_transmit) {      buffer_allocator->free(packet);    } } 

hci_transmit有不同平台的实现,分别在:

  • hci/src/hci_layer_linux.c
  • hci/src/hci_layer_android.c

前者是通过write直接向HCI socket的fd写入,后者是调用IBluetoothHci::sendHciCommand去实现,接口定义同样是在hardware/interfaces/bluetooth/1.0/IBluetoothHci.hal文件中。

因为不同手机厂商的SoC中集成蓝牙芯片的接口不同,有的是使用USB连接,有的是使用UART连接,因此需要给安卓提供一个统一的操作接口,这个接口就很适合由HAL(HIDL)来进行抽象。这部分实现通常是使用Linux中已有的UART/USB驱动进行操作,以提高代码的复用性。

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

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

相关文章

FL Studio Producer Edition21.0.3中文版安装详解(附下载链接)

fl studio 21中文版是Image-Line公司继20版本之后更新的水果音乐制作软件&#xff0c;很多用户不太理解&#xff0c;为什么新版本不叫fl studio 21或fl studio2024&#xff0c;非得直接跳到21.2版本&#xff0c;其实该版本是为了纪念该公司22周年&#xff0c;所以该版本也是推出…

使用gcloud SDK 管理和部署 Cloud run service

查看cloud run 上的service 列表&#xff1a; gcloud run services list > gcloud run services listSERVICE REGION URL LAST DEPLOYED BY LAST DEPL…

小米秒享3--非小米电脑

小米妙享中心是小米最新推出的一款功能&#xff0c;能够为用户们提供更加舒适便利的操作体验。简单的说可以让你的笔记本和你的小米手机联动&#xff0c;比如你在手机的文档&#xff0c;连接小米共享后&#xff0c;可以通过电脑进行操作。 对于非小米电脑想要体验终版秒享AIOT…

使用java批量生成Xshell session(*.xsh)文件

背景 工作中需要管理多套环境, 有时需要同时登陆多个节点, 且每个环境用户名密码都一样, 因此需要一个方案来解决动态的批量登录问题. XShell Xshell有session管理功能: 提供了包括记住登录主机、用户名、密码及登录时执行命令或脚本(js,py,vbs)的功能 session被存储在xsh文…

计算机组成学习-数据的表示和运算总结

1、进制与编码 1.1 进位计数法 常用的进位计数法有十进制、二进制、八进制、十六进制等。十六进制每个 数位可取0〜9、A、B、C、D、E、F中的任意一个&#xff0c;其中A、B、C、D、E、F分别表示 10〜15。 八进制数字通常以前缀 "0"&#xff08;零&#xff09;加上数…

GoLong的学习之路,进阶,RabbitMQ (消息队列)

快有一周没有写博客了。前面几天正在做项目。正好&#xff0c;项目中需要MQ&#xff08;消息队列&#xff09;&#xff0c;这里我就补充一下我对mq的理解。其实在学习java中的时候&#xff0c;自己也仿照RabbitMQ自己实现了一个单机的mq&#xff0c;但是mq其中一个特点也就是&a…

基于ResNet18网络完成图像分类任务

目录 1 数据处理 1.1 数据集介绍 1.2 数据读取 1.3 构造Dataset类 2 模型构建 3 模型训练 4 模型评价 5 模型预测 6 什么是预训练模型和迁移学习 7 比较“使用预训练模型”和“不使用预训练模型”的效果。 总结 在本实践中&#xff0c;我们实践一个更通用的图像分类任务…

rust-flexi_logger

flexi_logger 是字节开源的rust日志库。目前有log4rs、env_log 等库&#xff0c;综合比较下来&#xff0c;还是flexi_logger简单容易上手&#xff0c;而且自定义很方便&#xff0c;以及在效率方面感觉也会高&#xff0c;下篇文章我们来测试下。 下面来看下怎么使用 关注 vx gol…

探索未来能源:可控核聚变的挑战与希望

探索未来能源:可控核聚变的挑战与希望 引言 随着人类社会的不断发展,对能源的需求也在持续增长。传统的化石燃料能源在燃烧过程中会产生大量的二氧化碳和其他温室气体,导致全球气候变暖,对环境产生了重大威胁。因此,寻找一种清洁、可持续、高效的能源成为了当务之急。在…

Redis hash表源码解析

整体数据结构&#xff1a;链式hash解决hash冲突、采用渐进式hash来完成扩容过程。 /** 哈希表节点*/ typedef struct dictEntry {// 键void *key;// 值union {void *val;uint64_t u64;int64_t s64;} v;// 指向下个哈希表节点&#xff0c;形成链表struct dictEntry *next;} dict…

ubuntu22.04离线手动安装openstack yoga和ceph quincy

目录 写在前面材料准备一. OpenStack部1. 创建虚拟网络和虚拟机2. 配置离线环境3. 环境准备3.1 配置网络3.2 配置主机名并配置解析3.3 时间调整3.4 安装openstack客户端3.5 安装部署MariaDB3.6 安装部署RabbitMQ控制节点操作3.7 安装部署Memcache控制节点操作 4. 部署配置keyst…

TwinCAT3一个PLC设备里多个程序工程之间通讯

目录 1、创建TwinCAT3工程&#xff0c;再分别创建两个PLC程序工程 2、PLC1工程中添加如下代码&#xff0c;然后编译重新生成PLC1工程 3、PLC2工程中添加如下代码&#xff0c;然后编译重新生成PLC2工程 4、变量关联 5、一个PLC运行多个PLC工程设置 7、工程下载链接 1、创建…

配置中心--Spring Cloud Config

目录 概述 环境说明 步骤 创建远端git仓库 准备配置文件 配置中心--服务端 配置中心--客户端 配置中心的高可用 配置中心--服务端 配置中心--客户端 消息总线刷新配置 配置中心--服务端 配置中心--客户端 概述 因为微服务架构有很多个服务&#xff0c;手动一个一…

wireshark自定义协议插件开发

目录 脚本代码 报文显示 脚本代码 local NAME "test" test_proto Proto("test", "test Protocol") task_id ProtoField.uint16("test.task_id", "test id", base.DEC) cn ProtoField.uint8("test.cn", &qu…

【Java 基础】15 注解

文章目录 1.什么是注解2.元注解1&#xff09;定义2&#xff09;分类 3.内置注解4.自定义注解5.注解的基本语法6.验证注解是否生效7.注解的使用场景8.注解的注意事项结语 1.什么是注解 注解&#xff08;Annotation&#xff09;可以理解成一种特殊的 “注释” 注解定义时以 符号…

多线程06 单例模式,阻塞队列以及模拟实现

前言 上篇文章我们讲了wait和notify两个方法的使用.至此,多线程的一些基本操作就已经结束了,今天我们来谈谈多线程的一些简单应用场景. 单例模式 单例模式,顾名思义,只有一个实例的模式,我们有两种实现方式,分别是懒汉式和饿汉式,我们来分别给出代码. 饿汉式(此处的饿表示创建实…

详解Spring中的Aop编程原理JDK动态代理和CGLIB动态代理

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

yolov8模型 onnxruntime推理及可视化

参考:https://github.com/ultralytics/ultralytics/blob/main/examples/YOLOv8-ONNXRuntime/main.py 1、yolov8 onnxruntime推理代码 1)导出参考:https://blog.csdn.net/weixin_42357472/article/details/131412851 2)查看保存的模型onnx的输入格式等信息 登录https://n…

使用PCReg.PyTorch项目训练自己的数据集进行点云配准

项目地址&#xff1a; https://github.com/zhulf0804/PCReg.PyTorch/tree/main 网络简介&#xff1a; 网络是基于PointNet Concat FC的&#xff0c;它没有其它复杂的结构&#xff0c;易于复现。因其简洁性&#xff0c;这里暂且把其称作点云配准的Benchmark。因作者源码中复杂…

剑指 Offer(第2版)面试题 14:剪绳子

剑指 Offer&#xff08;第2版&#xff09;面试题 14&#xff1a;剪绳子 剑指 Offer&#xff08;第2版&#xff09;面试题 14&#xff1a;剪绳子解法1&#xff1a;动态规划解法2&#xff1a;数学 剑指 Offer&#xff08;第2版&#xff09;面试题 14&#xff1a;剪绳子 题目来源…