Power management (PM) is an event-driven state machine, tickled by various
bta/sys events via a callback. The actual state switching calls are handled by the BTM HCI interfacing code, with results being posted back to the PM code via the BTA workqueue thread.
Power states are managed per-device, per-profile, so every incoming event includes a profile ID, app ID, and a
The events fired to drive the state machine at the time of this writing are:
Each of these correspond to a function name in
bta/sys/bta_sys_conn.cc, which are called by each profile definition in
The PM code makes calls into the BTM module to set various power states. Responses are handled in an asynchronous fashion, primarily via the callbacks
bta_dm_pm_timer_cback. Responses are handled through the BTA workqueue thread and the
bta_dm_pm_btm_status function. Since we might possibly get into a bad state where we never hear back from the controller, timers are used to post messages to the BTA workqueue thread as well, which filters down through the same status function.
Overall power states are managed per device, not per connection, but the power policy is determined by the greatest allowable power action defined across all currently known connections to a given device. Thus, if RFCOMM specifies that it's willing to go to into SNIFF and specifies that as an action, and say, a PAN connection is up which specifies it is willing to go into SNIFF, but its action states it wants ACTIVE, the power management code will change to ACTIVE.
The tables that determine which power levels are acceptable for which profiles and what actions to take for the above events are defined in the
bta/dm/bta_dm_cfg.cc file, as
During a lookup attempt, the code iterates over the
bta_dm_pm_cfg array, looking for a match between the profile and app IDs. When it finds one, it uses the
spec_idx field to index into
bta_dm_pm_spec array to determine which power modes are acceptable and what actions to take for each event.
The action constants are defined in
bta_api.h and are defined as a series of hex bitfields. The actual actions taken are determined by the
bta_dm_pm_set_mode function, but a few of the actions listed deserve some additional description:
BTA_DM_PM_NO_ACTIONis effectively a no-op and has a value of zero, so any other profile will override this.
BTA_DM_PM_NO_ACTIONand if selected as the action that
bta_dm_pm_set_modewill take, the connection will be removed from
bta_dm_conn_srvcsand no longer be considered for power management decisions.
BTA_DM_PM_SNIFF6are special, in that each level specifies a set of parameters for the SNIFF mode which relate to the min and max intervals, the number of attempts and the timeout. The overall action is still the same, however -- SNIFF mode is attempted. There are definitions available up to SNIFF7, but actual SSR values are only defined up to SNIFF6. Params are defined in
BTA_DM_PM_ACTIVEis full-on power.
BTA_DM_PM_RETRYhas the same effect as
BTA_DM_PM_NO_ACTION, except a timeout is possible to be set, which effectively allows a power operation to be “retried”.
bta_dm_init_pm function calls out to register
bta_dm_pm_cback with the bta sys module for incoming power management events, and also registers
bta_dm_pm_btm_cback with the btm module to handle responses and timeouts of HCI requests (via
At this point, the power managment code is basically done until the first set of events come in through
bta_dm_pm.cc file, connections whose power management states are managed are tracked in a global array called
bta_dm_conn_srvcs. Unfortunately, while this variable is declared as an extern in the
bta_dm_int.h file, it only seems to be used in the
bta_dm_act.cc file, and only for reinitialization.
bta_dm_pm_cbackfunction is called.
bta_dm_pm_cfgtable. If none are found for the given profile ID and app ID, the function simply returns with no action taken.
BD_ADDR, they are stopped.
bta_dm_pm_spec) is checked to see if there's no action to be performed (
BTA_DM_PM_NO_ACTION), and if so, returns with no action taken.
bta_dm_conn_srvcsis consulted to ensure there‘s an entry for this connection if it’s supposed to be managed according to the power spec state tables. If the spec specifies
BTA_DM_PM_NO_PREF, then any existing entry in this list is removed, otherwise one is added/updated with the state given to the function.
bta_dm_pm_cbackchecks to see if the
bta_dm_ssr_specspecifies SSR adjustments are to be made, and if so,
bta_dm_pm_ssris called with the peer
bta_dm_pm_ssriterates the managed services array to find all connected services for the given
BD_ADDR, then looks up the ssr values from the
bta_dm_ssr_spectables, looking for the smallest max latency to use.
BTM_SetSsrParamsto actually send along the SSR params to the bluetooth chip.
bta_dm_pm_set_modewith the peer address and the
timed_outparameter set to
bta_dm_pm_set_modegrabs both actions specified for the profile in the
bta_dm_pm_spectables. If the first power management action didn't timeout (or was never attempted, according to the
pm_mode_attemptedfields), its timeout and mode are used. Otherwise, the same check is done against the second action and it is used instead. If both actions have been attempted, then the action is set to
BTA_DM_PM_NO_ACTION. Only the highest power mode action is chosen from all connected profiles.
BTA_DM_PM_SNIFFbut the profile doesn't allow it, this function takes no action.
bta_dm_pm_parkis called, which calls
BTM_SetPowerModeto make an HCI request to enable PARK for the given peer and connection.
BTA_DM_PM_SNIFF, the peer device‘s link policy is checked to see if it’s allowed. If so, then
bta_dm_pm_sniffis called, which makes various calls to the local controller,
BTM_SetPowerModeto ensure SNIFF mode is enabled.
BTA_DM_PM_ACTIVE, a call to
bta_dm_pm_activeis made, which calls
BTM_SetPowerModeto set the link into ACTIVE mode.
At this point, if one of the timers in
bta_dm_cb.pm_timer times out, a call is made through the BTA workqueue thread to
bta_dm_pm_btm_cback, which then triggers
bta_dm_pm_btm_status, with the timeout field set to TRUE. HCI responses are also fired as messages through the BTA workqueue thread, which are handled again, through
Essentially these messages eventually go through the same functions as events fired from the SYS side of things, except from the initial path they take:
bta_dm_pm_btm_cbackpackages up the given parameters into a
tBTA_DM_PM_BTM_STATUSstruct and posts it to the BTA workqueue thread via
bta_sys_sendmsg, with the event header set to
bta_dm_pm_btm_statusfunction. Determine if this is running on the workqueue thread or not
statuspassed in is actually the current status of the device.
BTM_PM_STS_ACTIVE(still in the ACTIVE power mode), checks the HCI status code:
bta_dm_pm_btm_statusstops any timers started for the device in
bta_dm_pm_set_mode, clears some status bits in the peer device structure, and then calls back into
bta_dm_pm_set_modewith the peer device address and timeout set to FALSE.
prev_lowfield is set, calls
bta_dm_pm_ssrto re-send SSR params, stops all timers for the device, and then re-calls
bta_dm_pm_set_modewith timeout set to FALSE to re-attempt with a second action (if the previous PARK or SNIFF failed, otherwise it'll re-attempt the first action).
BTM_PM_STS_HOLD, saves the previous low power mode in the peer device's
BTM_PM_STS_SSR, simply clears or sets the device
BTA_DM_DI_USE_SSRbit, depending on the value of
tBTA_DM_MSG.value, which determines if the device can handle SSR.
BTM_PM_STS_SNIFFand the info field has the
BTA_DM_DI_SET_SNIFFbit set, then
BTA_DM_DI_INT_SNIFFis set, otherwise
BTA_DM_DI_SET_SNIFFbit is cleared in the device info field.
At this point, either the method simply returns, or has called back into
bta_dm_pm_set_mode, in which case the usual flow takes over.
Timers are used exclusively for handling HCI command timeouts, and filter through to a call to
bta_dm_pm_timer_cbackclears the use flag on the timer that fired, and sends off an event to the BTA workqueue thread.
bta_dm_pm_timer, which just calls
bta_dm_pm_set_modewith timeout set to