在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_SetLinkPolicy
和BTM_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_DmSearch
和BTA_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 */