Android蓝牙协议栈fluoride(六) - 设备管理(bt application)

在Android蓝牙协议栈fluoride(五) - 设备管理(bt application)中描述了设备管理中的API、状态机以及事件处理,接下来将描述设备管理中的功耗管理和上报到上层的事件。

功耗管理

连接策略

蓝牙设备有很大比例都是带电池的产品,那么功耗的高低直接决定了使用时间的长短,蓝牙在工作时有时候需要连续传输数据或者实时的传输数据(如播放音乐/通话等),有时仅仅建立了连接并没有业务处理(如音乐暂停但连接并未断开),那么就希望有业务时能实时的传输数据,没有业务时保持连接的同时有较低的功耗,因此就产生以下模式(策略):
在这里插入图片描述

  • active模式
    有业务时实时(宏观上)传输数据。
    在这里插入图片描述
  • sniff模式
    没有业务时定期发送心跳保持连接,待有业务时切换到active模式。
    在这里插入图片描述
  • hold模式
    该模式下,不支持ACL通道上的数据传输(SCO通道可以传输数据),因此设备可以利用这个时间段完成其他业务,如scan、page、inquiry等。

所以在在蓝牙设备使用过程中,在有业务时将链接策略设置为active模式,在没有业务的时候设置为sniff模式或者hold模式节省功耗。

在代码中,系统管理器提供了以下几个接口设置链接策略:

// 设置/清除策略
void bta_sys_set_policy(uint8_t id, uint8_t policy, const RawAddress& peer_addr);
void bta_sys_clear_policy(uint8_t id, uint8_t policy, const RawAddress& peer_addr);
// 设置/清除默认设置
void bta_sys_set_default_policy(uint8_t id, uint8_t policy);
void bta_sys_clear_default_policy(uint8_t id, uint8_t policy);

以上接口都是调用设备管理中的bta_dm_policy_cback回调,该回调通过 bta_sys_policy_register注册。这个回调中分别调用BTM_SetLinkPolicyBTM_SetDefaultLinkPolicy实现。

参数设置

各个的profile在不同状态下设置不同参数,在设备管理器中按照生成以下几个表,每个表之间建立映射关系,设置参数时查表设置。

typedef struct {
  uint8_t id;  //模块ID
  uint8_t app_id; //App ID
  uint8_t spec_idx; // tBTA_DM_PM_SPEC表中的index
} tBTA_DM_PM_CFG;

typedef struct {
  tBTA_DM_PM_ACTION power_mode; // 连接策略
  uint16_t timeout; // 当前策略的超时时间
} tBTA_DM_PM_ACTN;

typedef struct {
  uint8_t allow_mask; //运行的策略的mask
  uint8_t ssr; 
  tBTA_DM_PM_ACTN actn_tbl[BTA_DM_PM_NUM_EVTS][2];  // 各个profile不同状态下的策略和超时时间,策略的参数在tBTM_PM_PWR_MD中
} tBTA_DM_PM_SPEC;

typedef struct {
  uint16_t max;
  uint16_t min;
  uint16_t attempt;
  uint16_t timeout;
  tBTM_PM_MODE mode;
} tBTM_PM_PWR_MD;

在这里插入图片描述

注: Core 5.3删除了park模式,本系列文章不再介绍,有文章中有出现请忽略

代码中这三个表格实现如下:

tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_CFG bta_dm_pm_cfg[BTA_DM_NUM_PM_ENTRY + 1] = {
   {BTA_ID_SYS, BTA_DM_NUM_PM_ENTRY, 0}, /* reserved: specifies length of this table. */
   {BTA_ID_AG, BTA_ALL_APP_ID, 0},               /* ag uses first spec table for app id 0 */
   {BTA_ID_CT, 1, 1}, /* ct (BTA_ID_CT,APP ID=1) spec table */
   {BTA_ID_CG, BTA_ALL_APP_ID, 1},   /* cg resue ct spec table */
   ...
};
tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = {
    /* AG : 0 */
    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
     (BTA_DM_PM_SSR2), /* the SSR entry */
     {
         {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000},
          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff  */
         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
         {{BTA_DM_PM_SNIFF_SCO_OPEN_IDX, 7000},
          {BTA_DM_PM_NO_ACTION, 0}}, /* sco open, active */
         {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000},
          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close sniff  */
         {{BTA_DM_PM_SNIFF, 7000}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* busy */
         {{BTA_DM_PM_RETRY, 7000},
          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
     }},

    /* CT, CG : 1 */
    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
     (BTA_DM_PM_SSR2), /* the SSR entry */
     {
         {{BTA_DM_PM_PARK, 5000},
          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  park */
         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
         {{BTA_DM_PM_SNIFF_A2DP_IDX, 5000},
          {BTA_DM_PM_NO_ACTION, 0}}, /* sco open sniff */
         {{BTA_DM_PM_PARK, 5000},
          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close  park */
         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
         {{BTA_DM_PM_RETRY, 5000},
          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
     }},
     ...
};
tBTA_DM_PM_TYPE_QUALIFIER tBTM_PM_PWR_MD bta_dm_pm_md[] = {
    /* sniff modes: max interval, min interval, attempt, timeout */
    {BTA_DM_PM_SNIFF_MAX, BTA_DM_PM_SNIFF_MIN, BTA_DM_PM_SNIFF_ATTEMPT,
     BTA_DM_PM_SNIFF_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF - A2DP */
    {BTA_DM_PM_SNIFF1_MAX, BTA_DM_PM_SNIFF1_MIN, BTA_DM_PM_SNIFF1_ATTEMPT,
     BTA_DM_PM_SNIFF1_TIMEOUT, BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF1 */
    {BTA_DM_PM_SNIFF2_MAX, BTA_DM_PM_SNIFF2_MIN, BTA_DM_PM_SNIFF2_ATTEMPT,
     BTA_DM_PM_SNIFF2_TIMEOUT,
     BTM_PM_MD_SNIFF}, /* for BTA_DM_PM_SNIFF2- HD idle */
     ...
};


const tBTA_DM_PM_CFG* p_bta_dm_pm_cfg = &bta_dm_pm_cfg[0];
const tBTA_DM_PM_SPEC* p_bta_dm_pm_spec = &bta_dm_pm_spec[0];
const tBTM_PM_PWR_MD* p_bta_dm_pm_md = &bta_dm_pm_md[0];

触发修改参数的动作有3个:

  • bta_sys
    初始化时会调用bta_sys_pm_register(bta_dm_pm_cback)向系统管理注册回调,系统管理中对应的API以及状态如下:
extern void bta_sys_conn_open(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_conn_close(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_app_open(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_app_close(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_sco_open(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_sco_close(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_sco_use(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_sco_unuse(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_idle(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);
extern void bta_sys_busy(uint8_t id, uint8_t app_id, const RawAddress& peer_addr);

#define BTA_SYS_CONN_OPEN 0x00
#define BTA_SYS_CONN_CLOSE 0x01
#define BTA_SYS_APP_OPEN 0x02
#define BTA_SYS_APP_CLOSE 0x03
#define BTA_SYS_SCO_OPEN 0x04
#define BTA_SYS_SCO_CLOSE 0x05
#define BTA_SYS_CONN_IDLE 0x06
#define BTA_SYS_CONN_BUSY 0x07

设备管理器中的处理函数是bta_dm_pm_cback,首先根据id和app id在bta_dm_pm_cfg中查找到bta_dm_pm_spec的index,根据index从bta_dm_pm_spec获取到当前状态的策略,如果是BTA_DM_PM_NO_ACTION则直接返回,否则根据id、app id、对端地址查找连接信息;根据index从bta_dm_pm_spec获取当前状态的策略,如果是BTA_DM_PM_NO_PREF则删除连接信息,否则判断是否找到连接信息,如果找到则更新状态,否则添加新的连接信息;最后调用bta_dm_pm_set_mode更新连接策略。
在这里插入图片描述
功耗管理是按设备划分的,不是按照连接的profile划分,即一个设备可能连接多个profile(通过id区分),只要有一个profile是active则整个通信都是active,所以当某个profile的power_mode为BTA_DM_PM_NO_PREF时会删除这个profile记录的信息,即这个profile不再决定通信的策略。

  • btm
    初始化时会调用BTM_PmRegister((BTM_PM_REG_SET | BTM_PM_REG_NOTIF), &bta_dm_cb.pm_id,bta_dm_pm_btm_cback)向BTM注册回调,当策略发生变化时通过bta_dm_pm_btm_cback回调通知设备管理器。根据当前状态判断是否需要调用bta_dm_pm_set_mode更新策略。
    在这里插入图片描述

  • 定时器
    bta_dm_pm_set_mode中会启动一些定时器,当定时器超时后会触发调用bta_dm_pm_timer_cback,回调中首先会清除不使用的定时器,然后根据定时器记录的策略调用bta_dm_pm_set_mode

bta_dm_pm_set_mode是链接策略更新的核心函数,首先找到所有连接的profile中策略优先级最高的策略(ACTIVE > SNIFF > NO_PREF > NO_ACTIVE),如果需要启动定时器且定时器的超时时间大于0,则启动一个定时器(超时时间在bta_dm_pm_spec中获取)。如果设置的策略优先级低于当前的策略则直接返回,否则根据设置相应的策略。
在这里插入图片描述
设置Park策略和Sniff策略时获取bta_dm_pm_md表中的策略参数。

上报事件

在fluoride中上报事件大多是通过回调上报,启动蓝牙时(BTA_EnableBluetooth)注册了tBTA_DM_SEC_CBACK原型的回调,bta中设备管理的大多数事件都是通过这个回调向上层上报事件,此外BTA_DmSearchBTA_DmDiscover时也注册了tBTA_DM_SEARCH_CBACK原型的回调,用于上报发现设备/服务的事件。

// 原型
typedef void(tBTA_DM_SEC_CBACK)(tBTA_DM_SEC_EVT event, tBTA_DM_SEC* p_data);
/* 上报的事件 */
#define BTA_DM_ENABLE_EVT 0    /* Enable Event */
#define BTA_DM_DISABLE_EVT 1   /* Disable Event */
#define BTA_DM_PIN_REQ_EVT 2   /* PIN request. */
#define BTA_DM_AUTH_CMPL_EVT 3 /* Authentication complete indication. */
#define BTA_DM_AUTHORIZE_EVT 4 /* Authorization request. */
#define BTA_DM_LINK_UP_EVT 5   /* Connection UP event */
#define BTA_DM_LINK_DOWN_EVT 6 /* Connection DOWN event */
#define BTA_DM_SIG_STRENGTH_EVT 7 /* Signal strength for bluetooth connection */
#define BTA_DM_BUSY_LEVEL_EVT 8 /* System busy level */
#define BTA_DM_BOND_CANCEL_CMPL_EVT 9 /* Bond cancel complete indication */
#define BTA_DM_SP_CFM_REQ_EVT 10 /* Simple Pairing User Confirmation request. */
#define BTA_DM_SP_KEY_NOTIF_EVT 11 /* Simple Pairing Passkey Notification */
#define BTA_DM_SP_RMT_OOB_EVT 12   /* Simple Pairing Remote OOB Data request. */
#define BTA_DM_SP_KEYPRESS_EVT 13  /* Key press notification event. */
#define BTA_DM_ROLE_CHG_EVT 14     /* Role Change event. */
#define BTA_DM_BLE_KEY_EVT 15      /* BLE SMP key event for peer device keys */
#define BTA_DM_BLE_SEC_REQ_EVT 16  /* BLE SMP security request */
#define BTA_DM_BLE_PASSKEY_NOTIF_EVT 17 /* SMP passkey notification event */
#define BTA_DM_BLE_PASSKEY_REQ_EVT 18   /* SMP passkey request event */
#define BTA_DM_BLE_OOB_REQ_EVT 19       /* SMP OOB request event */
#define BTA_DM_BLE_LOCAL_IR_EVT 20      /* BLE local IR event */
#define BTA_DM_BLE_LOCAL_ER_EVT 21      /* BLE local ER event */
#define BTA_DM_BLE_NC_REQ_EVT 22 /* SMP Numeric Comparison request event */
#define BTA_DM_SP_RMT_OOB_EXT_EVT 23 /* Simple Pairing Remote OOB Extended Data request. */
#define BTA_DM_BLE_AUTH_CMPL_EVT 24 /* BLE Auth complete */
#define BTA_DM_DEV_UNPAIRED_EVT 25
#define BTA_DM_HW_ERROR_EVT 26 /* BT Chip H/W error */
#define BTA_DM_LE_FEATURES_READ 27 /* Cotroller specific LE features are read */
#define BTA_DM_ENER_INFO_READ 28 /* Energy info read */
#define BTA_DM_BLE_SC_OOB_REQ_EVT 29 /* SMP SC OOB request event */
#define BTA_DM_BLE_CONSENT_REQ_EVT 30 /* SMP consent request event */

// 原型
typedef void(tBTA_DM_SEARCH_CBACK)(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* p_data);
/* 上报的事件 */
#define BTA_DM_INQ_RES_EVT 0  /* Inquiry result for a peer device. */
#define BTA_DM_INQ_CMPL_EVT 1 /* Inquiry complete. */
#define BTA_DM_DISC_RES_EVT 2 /* Discovery result for a peer device. */
#define BTA_DM_DISC_BLE_RES_EVT 3 /* Discovery result for BLE GATT based servoce on a peer device. */
#define BTA_DM_DISC_CMPL_EVT 4          /* Discovery complete. */
#define BTA_DM_DI_DISC_CMPL_EVT 5       /* Discovery complete. */
#define BTA_DM_SEARCH_CANCEL_CMPL_EVT 6 /* Search cancelled */

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

位1的个数

题目链接 位1的个数 题目描述 注意点 输入必须是长度为 32 的 二进制串 解答思路 位运算判断每一位是否为1 代码 public class Solution {// you need to treat n as an unsigned valuepublic int hammingWeight(int n) {int res 0;for (int i 0; i < 32; i) {res …

Java并发编程基础总结

进程和线程概念 什么进程 进程是系统运行的基本单位&#xff0c;通俗的理解我们计算机启动的每一个应用程序都是一个进程。如下图所示&#xff0c;在Windows中这一个个exe文件&#xff0c;都是一个进程。而在JVM下&#xff0c;每一个启动的Main方法都可以看作一个进程。 什么…

.Net Reactor 使用心得

主密钥是干嘛的&#xff1f; 1 若要创建有效的许可证文件&#xff0c;必须使用与用于生成受.NET Reactor保护的输出相同的主密钥来创建许可证。 2 主密钥是在创建项目时生成的&#xff01;必须保存该项目才能保留原始密钥。 dll而不是exe 由于使用的是.net6 生成的代码。 …

极智项目 | 实战烟雾火焰检测

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多项目分享 大家好&#xff0c;我是极智视界&#xff0c;本文来介绍 实战烟雾火焰检测。 本文介绍的 实战烟雾火焰检测项目&#xff0c;提供完整的可以一键执行的项目工程源码&#xff0c;获取方式有两个&#xff1a; (1…

【离散数学】——期末刷题题库(欧拉图和哈密顿图)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

Springboot整合阿里云短信服务

目录 1.注册登录用户 2.点击AccessKey管理&#xff0c;开通使用子用户AccessKey 2.1点击进入AccessKey管理 2.2点击用户创建用户 2.3选择控制台创建 2.4权限修改 3.短信服务 4.创建Springboot项目使用SDK 4.1创建一个springboot项目 4.2导入阿里云短信Maven依赖 4.3…

唇彩行业分析:我国彩妆细分品类市场占比63%

唇部彩妆是指在唇部起到化妆修饰作用的产品&#xff0c;包括口红/唇膏、唇蜜/唇彩/唇釉、唇笔/唇线笔、唇泥四大类。总体来看&#xff0c;目前我国唇部彩妆细分品类主要集中在唇膏/口红、唇蜜/唇彩/唇釉。唇笔/唇线笔市场接受程度较低&#xff0c;这是由于唇笔/唇线笔的主要成分…

shell脚本定时自动备份mysql数据库和mysql恢复数据

1、设置一些测试的数据 创建一个database&#xff0c;一些tables和一些数据 create database test_bom default charset utf8 collate utf8_general_ci; use test_bom;create table users( id int not null primary key auto_increment, name varchar(64) not null, password…

通俗易懂:插入排序算法全解析(C++)

插入排序算法是一种简单直观的排序算法&#xff0c;它的原理就像我们玩扑克牌时整理手中的牌一样。下面我将用通俗易懂的方式来解释插入排序算法的工作原理。 假设我们手上有一副无序的扑克牌&#xff0c;我们的目标是将它们从小到大排列起来。插入排序算法的思想是&#xff0…

web实习三_JavaScript编程

编写 JavaScript 程序实现 输出“九九乘法表”&#xff08; 左下三角形形式 &#xff09;。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, …

qiankun中子系统变化透传到主系统调用主系统方法

1、首先在主系统中qiankun启动前把变动的参数初始化 2、初始化之后就可以通过全局状态通信把参数透传为全局 3、在微应用子系统main.js的qiankun的mount中获取到全局设备参数属性并是设置为子系统全局 4、在微应用子系统中需要去调主系统方法时就在那个地方改变透传过来的参数 …

如何性能测试中进行业务验证?

在性能测试过程中&#xff0c;验证HTTP code和响应业务code码是比较基础的&#xff0c;但是在一些业务中&#xff0c;这些参数并不能保证接口正常响应了&#xff0c;很可能返回了错误信息&#xff0c;所以这个时候对接口进行业务验证就尤其重要。下面分享一个对某个资源进行业务…

ros2+在Ubuntu上安装gazebo

Binary Installation on Ubuntu(Ubuntu上binary方式安装gazebo) Harmonic binaries are provided for Ubuntu Jammy (22.04) and Ubuntu 24.04 (when its released). &#xff08;在Ubuntu22.04或者24.04上都是安装Harmonic版本的gazebo&#xff09;The Harmonic binaries are…

issue unit

The Issue Unit issue queue用来hold住&#xff0c;已经dispatched&#xff0c;但是还没有执行的uops&#xff1b; 当一条uop的所有的operands已经ready之后&#xff0c;request请求会被拉起来&#xff1b;然后issue select logic将会从request bit 1的slot中&#xff0c;选择…

指令寻址(顺序寻址和跳跃寻址)

目录 一. 顺序寻址1.1 定长指令字结构1.2 变长指令字结构 二. 跳跃寻址 \quad 指令寻址:如何确定下一条指令的存放地址? \quad 一. 顺序寻址 \quad 1.1 定长指令字结构 \quad 主存按字编址 \quad 按字节编址 1.2 变长指令字结构 \quad 同种颜色代表一条指令 由于无法判断当前…

制衣厂生产ERP系统怎么样?制衣厂生产ERP软件哪个好

有很多的制衣厂在订单处理、物料、仓储、销售、仓储、物料编码、车间成本核算、计件工资核算等方面还存在不少改进空间。 而经过多年的发展&#xff0c;现如今制衣行业的竞争比较激烈&#xff0c;如何提升各业务部门协同效率&#xff0c;减少车间物料损耗&#xff0c;简化生产…

idea的快捷键

1.调整字体的大小 文件夹的循序:setting-Editor-Font 界面: 2.删除当前行 文件夹的循序:setting-Keymap-DeleteLine 界面: 3.导入该行需要的类 文件夹的循序:setting-Editor-General-Auto import 界面: 4.格式化代码 文件夹的循序:setting-keymap-Reformat 界面: 5.快速…

【MySQL】——数据类型及字符集

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

loki 如何格式化日志

部署 grafana-loki 首先介绍一下如何部署 官方文档&#xff1a;部署 grafana-loki 部署命令 设置集群的存储类&#xff0c;如果有默认可以不设置设置命名空间 helm install loki oci://registry-1.docker.io/bitnamicharts/grafana-loki --set global.storageClasslocal -n …

程序员退一步的海阔天空,是考公还是烤冷面?

打败一个志向坚定的程序员只需要一个简单的年龄危机、身体预警.......钱难挣、屎难吃。996的钱更是伤身体&#xff0c;或者是被裁员、劝退的无力。算了~这份工作也不是非要不可&#xff0c;劳资不干了&#xff01;&#xff08;hahahahaha....bushi)人生在世&#xff0c;进可攻、…