SoFunction
Updated on 2025-03-03

Android PowerManagerService Turn on power saving mode

summary

Getting started with Android PowerManagerService Power Saving ModeA preliminary understanding of the power-saving mode was made, some concepts were introduced, and a simple analysis of the code for the power-saving mode environment was conducted. Readers need to read the first article carefully and then read this article.

There are three ways to turn on power saving mode:

  • Manual mode, that is, the user manually turns on the power saving mode.
  • Automatic mode, the user sets a battery percentage threshold, and when the battery is below this threshold, the power saving mode will be automatically triggered.
  • Dynamic mode, this mode is actually automatic mode. According to the document, this mode is provided to the application and automatically adjusts the threshold for triggering the power saving mode according to the situation.

This article only focuses on the following content:

  • Power-saving mode on the process.
  • What isbattery saver stickymodel.

As long as you master the above two points, automatic mode and dynamic mode, you can analyze them yourself.

Turn on power saving mode

Now, taking the manual power saving mode as an example, analyze the power saving mode opening process.

fromGetting started with Android PowerManagerService Power Saving ModeIt can be seen that in the Settings->Battery->Battery Saver interface, you can manually turn on the power saving mode, and the code is called as follows

Finally, the corresponding method of PowerManagerService will be called:

public boolean setPowerSaveModeEnabled(boolean enabled) {
    if ((.POWER_SAVER)
            != PackageManager.PERMISSION_GRANTED) {
        (
                .DEVICE_POWER, null);
    }
    final long ident = ();
    try {
        return setLowPowerModeInternal(enabled);
    } finally {
        (ident);
    }
}


private boolean setLowPowerModeInternal(boolean enabled) {
    synchronized (mLock) {
        // When charging, power saving mode is not allowed to be turned on/off        if (mIsPowered) {
            return false;
        }

        (enabled);

        return true;
    }
}

In the design of AOSP, the power saving mode and the charging state are conflicting. If the device is in power saving mode and plug in the charger at this time, the power saving mode will definitely be turned off. If the device is in a charged state, power saving mode is not allowed.

To be honest, I don't agree with this design very much. I think the power saving mode is the user's strong personal wish, and it can only be turned on or off by the user's own decision.

BatterySaverStateMachine State Management

From the above code, we can see that when power saving mode is turned on, theBatterySaverStateMachine#setBatterySaverEnabledManually()Method: Pass the command to the state machine

public void setBatterySaverEnabledManually(boolean enabled) {
    synchronized (mLock) {
        updateStateLocked(true, enabled);
    }
}

The state machine passesupdateStateLocked()Update the internal state and then perform the corresponding operations according to the state. Note that the first parameter here indicates whether the power saving mode is manually turned on by the user, the value is true, and the second parameter indicates whether the power saving mode is turned on. According to the example we analyzed, the value here is true.

    private void updateStateLocked(boolean manual, boolean enable) {
        if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
            return; // Not fully initialized yet.
        }

        switch (mState) {
            case STATE_OFF: {
                if (!mIsPowered) { // Only in non-charge mode can the power saving mode be enabled                    if (manual) { // Manual operation                        if (!enable) {
                            return;
                        }
                        // User manually turn on power saving mode                        enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
                                BatterySaverController.REASON_MANUAL_ON);
                        hideStickyDisabledNotification();
                        // Switch to STATE_MANUAL_ON                        mState = STATE_MANUAL_ON;
                    } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) {
                        // ...Auto mode                    } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { 
                        // ... Dynamic mode                    }
                }
                break;
            }

            // ...
        }
    }

The default state in the state machine isSTATE_OFF, means that the power saving mode is turned off by default.

passenableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON);Turn on power saving mode and switch the status toSTATE_MANUAL_ON. We must pay attention to each state switch, so this will affect the next state switch.

private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) {
    enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason));
}

private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
        String strReason) {
    final boolean wasEnabled = ();
    // Already in power saving mode    if (wasEnabled == enable) {
        return;
    }

    // Power saving mode is not allowed during charging    if (enable && mIsPowered) {
        return;
    }

    mLastChangedIntReason = intReason;
    mLastChangedStrReason = strReason;

    mSettingBatterySaverEnabled = enable;
    // 1. Save the state of power saving mode    putGlobalSetting(.LOW_POWER_MODE, enable ? 1 : 0);

    // 2. Open battery saver sticky mode    if (manual) { // User manually operates power saving mode        // mBatterySaverStickyBehaviourDisabled defaults to false, indicating that battery saver sticky mode is supported        setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable);
    }

    // 3. Turn on power saving mode through BatterySaverController    (enable, intReason);

    // Dynamic power saving mode related    if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
        triggerDynamicModeNotification();
    } else if (!enable) {
        hideDynamicModeNotification();
    }
}

Before turning on power saving mode, first put the database.LOW_POWER_MODEThe value of the field is saved as 1.

low power should be translated as low power consumption, commonly known as power saving mode, while low battery should be translated as low power, so don't be confused. In source codeBatteryManagerService#getBatteryLevelLow()It indicates whether the power is lower than the percentage of the automatic power saving mode. The naming of this function is very poor. It once made me mistakenly think that it was low power (the power is less than 15%). In fact, it indicates whether the automatic power saving mode was triggered.

The second step is that we need to pay attention to, it involvesbattery saver stickyFunction. According to the judgment conditions, it can be seen that only if the user manually operates the power saving mode, it will be triggered.battery saver stickyFunction, take a looksetStickyActive()

private void setStickyActive(boolean active) {
    // means battery saver sticky mode has been turned on    mSettingBatterySaverEnabledSticky = active;
    // .LOW_POWER_MODE_STICKY represents the status of battery saver sticky function    putGlobalSetting(.LOW_POWER_MODE_STICKY,
            mSettingBatterySaverEnabledSticky ? 1 : 0);
}

It's very simple, it's saving the state, which meansbattery saver stickyThe function has been turned on.

The third step is to hand over the actual operation of turning on the power saving mode to the power saving mode controller.BatterySaverController

BatterySaverController switches power saving mode

Let's take a look nowBatterySaverController#enableBatterySaver()How to turn on power saving mode

public void enableBatterySaver(boolean enable, int reason) {
    synchronized (mLock) {
        if (getFullEnabledLocked() == enable) {
            return;
        }
        // 1. Save the state of power saving mode        setFullEnabledLocked(enable);

        // 2. Update the power saving mode strategy        if (updatePolicyLevelLocked()) {
            // 3. Handle changes in power saving mode state            (/*sendBroadcast=*/ true, reason);
        }
    }
}

private boolean getFullEnabledLocked() {
    return mFullEnabledRaw;
}
private void setFullEnabledLocked(boolean value) {
    if (mFullEnabledRaw == value) {
        return;
    }
    // Refresh the cache of power saving mode, and the client can obtain the power saving mode status through PowerManager    ();
    mFullEnabledRaw = value;
}

First usemFullEnabledRawSave the power saving mode state.

Then, update the policy for power saving mode. The power saving mode will affect the functions of many modules, for example, the most intuitive thing is to affect the screen brightness. Therefore, when power saving mode is turned on, there must be a strategy that affects the functions of certain modules.

Finally, the change in the power saving mode state is handled. This includes switching power saving mode to notify the monitor of power saving mode.

mFullEnabledRawexpressfull battery saver, in fact, it is the power saving mode used by users. In fact, there is also a power saving modeadaptive battery saver, This power saving mode is set through the command line and should be related to testing and executedadb shell power set-adaptive-power-saver-enabled trueLet's open it, please refer to it for detailsPowerManagerShellCommandkind.

BattterySaverPolicy controls power saving strategy

Let's take a look nowBatterySaverController#updatePolicyLevelLocked()How to update the power saving mode strategy

private boolean updatePolicyLevelLocked() {
    if (getFullEnabledLocked()) {
        // Set power saving mode policy level        return (BatterySaverPolicy.POLICY_LEVEL_FULL);
    } else if (getAdaptiveEnabledLocked()) {
        return (BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE);
    } else {
        return (BatterySaverPolicy.POLICY_LEVEL_OFF);
    }
}

It turns out to beBatterySaverPolicySet uppolicy level, the value isBatterySaverPolicy.POLICY_LEVEL_FULL

boolean setPolicyLevel(@PolicyLevel int level) {
    synchronized (mLock) {
        if (mPolicyLevel == level) {
            return false;
        }
        if (mPolicyLevel == POLICY_LEVEL_FULL) {
            mFullPolicy = mDefaultFullPolicy;
        }
        switch (level) {
            case POLICY_LEVEL_FULL:
            case POLICY_LEVEL_ADAPTIVE:
            case POLICY_LEVEL_OFF:
                // 1. Save level                mPolicyLevel = level;
                break;
            default:
                (TAG, "setPolicyLevel invalid level given: " + level);
                return false;
        }
        // 2. Updating a valid policy according to level        updatePolicyDependenciesLocked();
        return true;
    }
}

BatterSaverPolicySavedpolicy level, and callupdatePolicyDependenciesLocked()To update the validbattery saver policy

private void updatePolicyDependenciesLocked() {
    // 1. According to the policy level, get the corresponding policy    final Policy rawPolicy = getCurrentRawPolicyLocked();

    // Refresh power saving mode cache    invalidatePowerSaveModeCaches();

    // Car on board    final int locationMode;
    if (()
            &&  != PowerManager.LOCATION_MODE_NO_CHANGE
            &&  != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) {
        // If car projection is enabled, ensure that navigation works.
        locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
    } else {
        locationMode = ;
    }

    // 2. Update effective policies based on the acquired policies    // mEffectivePolicyRaw indicates the actual policy that takes effect    // The data of mEffectivePolicyRaw is basically copied from rawPolicy    // There are only a few items that need to be adjusted, such as on-board or barrier-free. In these two special situations, please pay attention to them when using them.    mEffectivePolicyRaw = new Policy(
            ,
            ,
            ,
            ,
            ,
            ,
            ,
            ,
            ,
            ,
            // Don't disable vibration when accessibility is on.
             && !(),
            ,
            ,
            ,
            // Don't force night mode when car projection is enabled.
             && !(),
            ,
            ,
            ,
            locationMode,
            
    );
    // ...
}

// Default power saving mode policyprivate Policy mFullPolicy = DEFAULT_FULL_POLICY;

private Policy getCurrentRawPolicyLocked() {
    switch (mPolicyLevel) {
        case POLICY_LEVEL_FULL:
            return mFullPolicy;
        case POLICY_LEVEL_ADAPTIVE:
            return mAdaptivePolicy;
        case POLICY_LEVEL_OFF:
        default:
            return OFF_POLICY;
    }
}

getCurrentRawPolicyLocked()The default power saving mode strategy will be obtained.DEFAULT_FULL_POLICY, and then adjust the power saving mode strategy according to some situations, and finally form an effective power saving mode strategy.mEffectivePolicyRaw

Now let's take a look at this default power saving strategy.DEFAULT_FULL_POLICYWho is the saint

private static final Policy DEFAULT_FULL_POLICY = new Policy(
        0.5f,  /* adjustBrightnessFactor */
        true,  /* advertiseIsEnabled */
        new CpuFrequencies(), /* cpuFrequenciesForInteractive */
        new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */
        true,  /* deferFullBackup */
        true,  /* deferKeyValueBackup */
        false, /* disableAnimation */
        true,  /* disableAod */
        true,  /* disableLaunchBoost */
        true,  /* disableOptionalSensors */
        true,  /* disableVibration */
        false, /* enableAdjustBrightness */
        false, /* enableDataSaver */
        true,  /* enableFirewall */
        true, /* enableNightMode */
        true, /* enableQuickDoze */
        true, /* forceAllAppsStandby */
        true, /* forceBackgroundCheck */
        PowerManager.LOCATION_MODE_FOREGROUND_ONLY, /* locationMode */
        PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY /* soundTriggerMode */
);

PolicyIt is a data encapsulation class. After looking at the parameters of its constructor, we can roughly guess which modules' functions affect the power saving mode.

Pay attention to the third and fourth parameters here, which indicates the number of the CPU that needs to limit the frequency and the limit frequency value in power saving mode. This is empty by default and will be used later.

Handle power saving mode state changes

Now let's go back to the code that turns on power saving mode

public void enableBatterySaver(boolean enable, int reason) {
    synchronized (mLock) {
        if (getFullEnabledLocked() == enable) {
            return;
        }
        // 1. Save the state of power saving mode        setFullEnabledLocked(enable);

        // 2. Update the power saving mode strategy        if (updatePolicyLevelLocked()) {
            // 3. Handle changes in power saving mode state            (/*sendBroadcast=*/ true, reason);
        }
    }
}

The first two steps have been analyzed. Let’s take a look at the third step, it will eventually callBatterySaverController#handleBatterySaverStateChanged()To handle power saving mode state changes

void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
    final LowPowerModeListener[] listeners;

    final boolean enabled;
    // Get whether the device is in interactive state    // Generally speaking, if the screen is off, the device is in a non-interactive state, the screen is in a power level, and the device is in an interactive state    final boolean isInteractive = getPowerManager().isInteractive();
    final ArrayMap<String, String> fileValues;

    synchronized (mLock) {
        // Get the status of power saving mode        enabled = getFullEnabledLocked() || getAdaptiveEnabledLocked();

        // Save the previous full battery saver status        mFullPreviouslyEnabled = getFullEnabledLocked();
        // Save the previous adaptive battery saver status        mAdaptivePreviouslyEnabled = getAdaptiveEnabledLocked();

        listeners = (new LowPowerModeListener[0]);

        mIsInteractive = isInteractive;

        if (enabled) {
            // 1. When power saving mode is turned on, get the number of the CPU with limited frequency and the limited value            fileValues = (isInteractive);
        } else {
            fileValues = null;
        }
    }

    // 2. Set power saving mode to the underlying layer through PowerManagerService    final PowerManagerInternal pmi = ();
    if (pmi != null) {
        (Mode.LOW_POWER, isEnabled());
    }

    // Use BatterySavingStats to record data    updateBatterySavingStats();

    // 3. Limit or restore CPU frequency according to policy    if ((fileValues)) {
        // The CPU policy is empty, indicating that the frequency before the CPU needs to be restored        ();
    } else {
        // The CPU frequency policy is not empty, indicating that the CPU frequency needs to be limited        (fileValues);
    }

    if (sendBroadcast) {
        // 4. Send broadcast        Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
        (Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        (intent, );

        // You can configure the package name of an application in the frameworks-res configuration file        // This application can register a broadcast receiver in it to receive power saving mode state changes        if (getPowerSaveModeChangedListenerPackage().isPresent()) {
            intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
                    .setPackage(getPowerSaveModeChangedListenerPackage().get())
                    .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                            | Intent.FLAG_RECEIVER_FOREGROUND);
            (intent, );
        }

        // Send a built-in version of the broadcast, but the receiver needs permissions        intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
        (Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        (intent, ,
                .DEVICE_POWER);

        // 5. Notify the listener        for (LowPowerModeListener listener : listeners) {
            final PowerSaveState result =
                    (());
            (result);
        }
    }
}

The first and third steps are to limit the CPU frequency in power saving mode. According to the previous analysis, the current default strategy does not configure CPU frequency, so these two steps will not be analyzed. In the following article, I will analyze how to control the power saving mode strategy, and then analyze the code logic here.

Step 2, byPowerManagerServiceSet power saving mode to the bottom layer, which is called low power mode at the bottom layer.

Step 4: Send a broadcast with the power saving mode state changed.

Step 5: Notify the listener. Who will be the monitor? Of course, those modules affected by the power saving mode.

Let's see what exactly is the data returned to the listener? Take a look(())

    public PowerSaveState getBatterySaverPolicy(@ServiceType int type) {
        synchronized (mLock) {
            final Policy currPolicy = getCurrentPolicyLocked();
            final  builder = new ()
                    .setGlobalBatterySaverEnabled();
            switch (type) {
                case :
                    boolean isEnabled = 
                            ||  != PowerManager.LOCATION_MODE_NO_CHANGE;
                    return (isEnabled)
                            .setLocationMode()
                            .build();
                case :
                    return ()
                            .build();
                // ...

                case :
                    return ()
                            .build();
                case ServiceType.FORCE_ALL_APPS_STANDBY:
                    return ()
                            .build();
                case ServiceType.FORCE_BACKGROUND_CHECK:
                    return ()
                            .build();
                // ...
                default:
                    return ()
                            .build();
            }
        }
    }

It turns out that according to the type of listener, return aPowerSaveStateObject, this object only contains data that the listener cares about.

From this, we should have some understanding, if we develop a functional module ourselves

  • If affected by the power saving mode policy, you must register a monitor, obtain the policy in the power saving mode, and then adjust the module's functions.
  • If this module is a large power-consuming user, you must monitor the power-saving mode and perform the corresponding operations in the power-saving mode.

Many projects now focus on power consumption issues. How long can the power saving mode keep the phone standby is also an assessment indicator.

battery saver sticky mode

According to the previous analysis, it will only be turned on or off when the user manually operates the power saving mode.battery saver stickymodel.

Let me summarize what isbattery saver stickyMode? When the phone is already in power saving mode, plug in the power saving mode, the system will turn off the power saving mode by default. If the power is unplugged at this time or the phone is restarted, whenbattery saver stickyWhen the function is turned on, the system will turn on the power saving mode again.

Now let’s analyze from the code perspective and continue to use the above examples to analyze. If the power saving mode is turned on now and the power supply is plugged in, let’s take a look at the switching action of the state machine.BatterySaverStateMachine#updateStateLocked()

    private void updateStateLocked(boolean manual, boolean enable) {
        if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
            return; // Not fully initialized yet.
        }

        switch (mState) {
            case STATE_OFF: {
                if (!mIsPowered) { // Power saving mode is not allowed when charging                    if (manual) { // Manual mode                        if (!enable) {
                            (TAG, "Tried to disable BS when it's already OFF");
                            return;
                        }
                        enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
                                BatterySaverController.REASON_MANUAL_ON);
                        hideStickyDisabledNotification();
                        // 1. The user turns on power saving mode and switches to STATE_MANUAL_ON                        mState = STATE_MANUAL_ON;
                    } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) {
                        // Automatic mode ...                    } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) {
                        // Dynamic mode...                    }
                }
                break;
            }

            case STATE_MANUAL_ON: {
                if (manual) {
                    // ...
                } else if (mIsPowered) { // 2. Plug in the power supply                    // Turn off power saving mode                    enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
                            BatterySaverController.REASON_PLUGGED_IN);
                    // When power saving mode is turned on manually, mSettingBatterySaverEnabledSticky is set to true                    // mBatterySaverStickyBehaviourDisabled defaults to false, indicating that this feature is supported.                    if (mSettingBatterySaverEnabledSticky
                            && !mBatterySaverStickyBehaviourDisabled) {
                        // Plug in the power supply and switch the status to STATE_PENDING_STICKY_ON                        mState = STATE_PENDING_STICKY_ON;
                    } else {
                        mState = STATE_OFF;
                    }
                }
                break;
            }

            // ...

            case STATE_PENDING_STICKY_ON: { // 3. battery saver sticky mode operation                if (manual) {
                    return;
                }
                // mSettingBatterySaverStickyAutoDisableEnabled corresponding to the Turn off when charging switch in the Battery Saver interface                // mSettingBatterySaverStickyAutoDisableThreshold Default value is 90                final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled
                        && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold;
                // Manually turn on the power saving mode and plug in the power supply. At this time, the isStickyDisabled value is false                final boolean isStickyDisabled =
                        mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky;
                if (isStickyDisabled || shouldTurnOffSticky) {
                    // 3.2 If the Turn off when charging switch is turned on and the power is greater than 90%, the power saving mode will not be turned on again                    mState = STATE_OFF;
                    setStickyActive(false);
                    triggerStickyDisabledNotification();
                } else if (!mIsPowered) {
                    // Re-enable BS.
                    // 3.1 Disconnect the power supply and turn on the power saving mode again                    enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
                            BatterySaverController.REASON_STICKY_RESTORE);
                    mState = STATE_MANUAL_ON;
                }
                break;
            }
            // ...
        }
    }

First look at the first step, it turns on the power saving mode and switches toSTATE_MANUAL_ON

If you plug in the power supply at this time, you will enter the second step, turn off the power saving mode, and switch the status toSTATE_PENDING_STICKY_ON

If the Turn off when Charging switch of the Battery Saver interface in the settings is turned off and the power supply is unplugged, then when you enter step 3.1, the power saving mode will be turned on again. This isstickyThe meaning of  .

If the Turn off when Charging switch of the Battery Saver interface in the settings is turned on, then step 3.2 will not turn on the power saving mode again.

The Turn off when Charging switch of the Battery Saver interface in the settings isbattery saver sticky auto disableFunction.

Finish

After reading the entire power-saving mode code, I feel that many functions are very useless. Due to the limitations, I only analyzed the core code, which is how to switch the power-saving mode. The remaining other functions are left to readers for their own analysis.

This is the article about turning on the power saving mode of Android PowerManagerService. This is all about this article. For more related content on Android PowerManagerService, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!