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 */
  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值