diff options
| author | 2019-03-19 04:59:23 +0000 | |
|---|---|---|
| committer | 2019-03-19 04:59:23 +0000 | |
| commit | e7f50e16f28e528f2a9e09df63dde24c39957274 (patch) | |
| tree | b4e662eabe874b0b38438ee8b444bd445ffa5f34 | |
| parent | ba7f46d12da5c037bbf981d3364659b205770364 (diff) | |
| parent | f57f99edd721af648840c92aa0f05001c52dad6b (diff) | |
Merge "Formalizing states in BatterySaverStateMachine."
4 files changed, 591 insertions, 110 deletions
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index 8fce94edb104..9bf1825a8da6 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -318,6 +318,16 @@ message BatterySaverStateMachineProto { // Whether battery saver is enabled. optional bool enabled = 1; + enum StateEnum { + STATE_UNKNOWN = 0; + STATE_OFF = 1; + STATE_MANUAL_ON = 2; + STATE_AUTOMATIC_ON = 3; + STATE_OFF_AUTOMATIC_SNOOZED = 4; + STATE_PENDING_STICKY_ON = 5; + } + optional StateEnum state = 18; + // Whether full battery saver is enabled. optional bool is_full_enabled = 14; @@ -337,8 +347,7 @@ message BatterySaverStateMachineProto { // Whether battery status has been set at least once. optional bool battery_status_set = 4; - // Whether automatic battery saver has been canceled by the user. - optional bool battery_saver_snoozing = 5; + reserved 5; // battery_saver_snoozing // Whether the device is connected to any power source. optional bool is_powered = 6; @@ -373,5 +382,5 @@ message BatterySaverStateMachineProto { // using elapsed realtime as the timebase. optional int64 last_adaptive_battery_saver_changed_externally_elapsed = 17; - // Next tag: 18 + // Next tag: 19 } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index 5adcf352e260..f0e462503769 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -115,9 +115,41 @@ public class BatterySaverController implements BatterySaverPolicyListener { public static final int REASON_SETTING_CHANGED = 8; public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON = 9; public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF = 10; - public static final int REASON_STICKY_RESTORE_OFF = 11; - public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 12; - public static final int REASON_TIMEOUT = 13; + public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 11; + public static final int REASON_TIMEOUT = 12; + + static String reasonToString(int reason) { + switch (reason) { + case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON: + return "Percentage Auto ON"; + case BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF: + return "Percentage Auto OFF"; + case BatterySaverController.REASON_MANUAL_ON: + return "Manual ON"; + case BatterySaverController.REASON_MANUAL_OFF: + return "Manual OFF"; + case BatterySaverController.REASON_STICKY_RESTORE: + return "Sticky restore"; + case BatterySaverController.REASON_INTERACTIVE_CHANGED: + return "Interactivity changed"; + case BatterySaverController.REASON_POLICY_CHANGED: + return "Policy changed"; + case BatterySaverController.REASON_PLUGGED_IN: + return "Plugged in"; + case BatterySaverController.REASON_SETTING_CHANGED: + return "Setting changed"; + case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON: + return "Dynamic Warning Auto ON"; + case BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF: + return "Dynamic Warning Auto OFF"; + case BatterySaverController.REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED: + return "Adaptive Power Savings changed"; + case BatterySaverController.REASON_TIMEOUT: + return "timeout"; + default: + return "Unknown reason: " + reason; + } + } /** * Plugin interface. All methods are guaranteed to be called on the same (handler) thread. diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index af5d40bfbcec..8f2e997d319e 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -15,6 +15,8 @@ */ package com.android.server.power.batterysaver; +import static com.android.server.power.batterysaver.BatterySaverController.reasonToString; + import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -49,6 +51,42 @@ import java.io.PrintWriter; * Do not call out with the lock held. (Settings provider is okay.) * * Test: atest com.android.server.power.batterysaver.BatterySaverStateMachineTest + * + * Current state machine. This can be visualized using Graphviz: + <pre> + + digraph { + STATE_OFF + STATE_MANUAL_ON [label="STATE_MANUAL_ON\nTurned on manually by the user"] + STATE_AUTOMATIC_ON [label="STATE_AUTOMATIC_ON\nTurned on automatically by the system"] + STATE_OFF_AUTOMATIC_SNOOZED [ + label="STATE_OFF_AUTOMATIC_SNOOZED\nTurned off manually by the user." + + " The system should not turn it back on automatically." + ] + STATE_PENDING_STICKY_ON [ + label="STATE_PENDING_STICKY_ON\n" + + " Turned on manually by the user and then plugged in. Will turn back on after unplug." + ] + + STATE_OFF -> STATE_MANUAL_ON [label="manual"] + STATE_OFF -> STATE_AUTOMATIC_ON [label="Auto on AND charge <= auto threshold"] + + STATE_MANUAL_ON -> STATE_OFF [label="manual\nOR\nPlugged & sticky disabled"] + STATE_MANUAL_ON -> STATE_PENDING_STICKY_ON [label="Plugged & sticky enabled"] + + STATE_PENDING_STICKY_ON -> STATE_MANUAL_ON [label="Unplugged & sticky enabled"] + STATE_PENDING_STICKY_ON -> STATE_OFF [ + label="Sticky disabled\nOR\nSticky auto off enabled AND charge >= sticky auto off threshold" + ] + + STATE_AUTOMATIC_ON -> STATE_OFF [label="Plugged"] + STATE_AUTOMATIC_ON -> STATE_OFF_AUTOMATIC_SNOOZED [label="Manual"] + + STATE_OFF_AUTOMATIC_SNOOZED -> STATE_OFF [label="Plug\nOR\nCharge > auto threshold"] + STATE_OFF_AUTOMATIC_SNOOZED -> STATE_MANUAL_ON [label="manual"] + + </pre> + } */ public class BatterySaverStateMachine { private static final String TAG = "BatterySaverStateMachine"; @@ -60,6 +98,25 @@ public class BatterySaverStateMachine { private static final long ADAPTIVE_CHANGE_TIMEOUT_MS = 24 * 60 * 60 * 1000L; + /** Turn off adaptive battery saver if the device has charged above this level. */ + private static final int ADAPTIVE_AUTO_DISABLE_BATTERY_LEVEL = 80; + + private static final int STATE_OFF = BatterySaverStateMachineProto.STATE_OFF; + + /** Turned on manually by the user. */ + private static final int STATE_MANUAL_ON = BatterySaverStateMachineProto.STATE_MANUAL_ON; + + /** Turned on automatically by the system. */ + private static final int STATE_AUTOMATIC_ON = BatterySaverStateMachineProto.STATE_AUTOMATIC_ON; + + /** Turned off manually by the user. The system should not turn it back on automatically. */ + private static final int STATE_OFF_AUTOMATIC_SNOOZED = + BatterySaverStateMachineProto.STATE_OFF_AUTOMATIC_SNOOZED; + + /** Turned on manually by the user and then plugged in. Will turn back on after unplug. */ + private static final int STATE_PENDING_STICKY_ON = + BatterySaverStateMachineProto.STATE_PENDING_STICKY_ON; + private final Context mContext; private final BatterySaverController mBatterySaverController; @@ -75,6 +132,9 @@ public class BatterySaverStateMachine { @GuardedBy("mLock") private boolean mBatteryStatusSet; + @GuardedBy("mLock") + private int mState; + /** Whether the device is connected to any power source. */ @GuardedBy("mLock") private boolean mIsPowered; @@ -142,13 +202,6 @@ public class BatterySaverStateMachine { private boolean mDynamicPowerSavingsBatterySaver; /** - * Whether BS has been manually disabled while the battery level is low, in which case we - * shouldn't auto re-enable it until the battery level is not low. - */ - @GuardedBy("mLock") - private boolean mBatterySaverSnoozing; - - /** * Last reason passed to {@link #enableBatterySaverLocked}. */ @GuardedBy("mLock") @@ -181,6 +234,7 @@ public class BatterySaverStateMachine { mLock = lock; mContext = context; mBatterySaverController = batterySaverController; + mState = STATE_OFF; mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled); @@ -188,8 +242,36 @@ public class BatterySaverStateMachine { com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold); } - private boolean isAutoBatterySaverConfiguredLocked() { - return mSettingBatterySaverTriggerThreshold > 0; + /** @return true if the automatic percentage based mode should be used */ + private boolean isAutomaticModeActiveLocked() { + return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_PERCENTAGE + && mSettingBatterySaverTriggerThreshold > 0; + } + + /** + * The returned value won't necessarily make sense if {@link #isAutomaticModeActiveLocked()} + * returns {@code false}. + * + * @return true if the battery level is below automatic's threshold. + */ + private boolean isInAutomaticLowZoneLocked() { + return mIsBatteryLevelLow; + } + + /** @return true if the dynamic mode should be used */ + private boolean isDynamicModeActiveLocked() { + return mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_DYNAMIC + && mDynamicPowerSavingsBatterySaver; + } + + /** + * The returned value won't necessarily make sense if {@link #isDynamicModeActiveLocked()} + * returns {@code false}. + * + * @return true if the battery level is below dynamic's threshold. + */ + private boolean isInDynamicLowZoneLocked() { + return mBatteryLevel <= mDynamicPowerSavingsDisableThreshold; } /** @@ -233,7 +315,14 @@ public class BatterySaverStateMachine { Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL), false, mSettingsObserver, UserHandle.USER_SYSTEM); + synchronized (mLock) { + final boolean lowPowerModeEnabledSticky = getGlobalSetting( + Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0; + + if (lowPowerModeEnabledSticky) { + mState = STATE_PENDING_STICKY_ON; + } mBootCompleted = true; @@ -362,6 +451,8 @@ public class BatterySaverStateMachine { ? "Global.low_power changed to 1" : "Global.low_power changed to 0"; enableBatterySaverLocked(/*enable=*/ batterySaverEnabled, /*manual=*/ true, BatterySaverController.REASON_SETTING_CHANGED, reason); + } else { + doAutoBatterySaverLocked(); } } @@ -428,17 +519,6 @@ public class BatterySaverStateMachine { } } - @GuardedBy("mLock") - private boolean isBatteryLowLocked() { - final boolean percentageLow = - mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_PERCENTAGE - && mIsBatteryLevelLow; - final boolean dynamicPowerSavingsLow = - mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_DYNAMIC - && mBatteryLevel <= mDynamicPowerSavingsDisableThreshold; - return percentageLow || dynamicPowerSavingsLow; - } - /** * Decide whether to auto-start / stop battery saver. */ @@ -449,7 +529,6 @@ public class BatterySaverStateMachine { + " mSettingsLoaded=" + mSettingsLoaded + " mBatteryStatusSet=" + mBatteryStatusSet + " mIsBatteryLevelLow=" + mIsBatteryLevelLow - + " mBatterySaverSnoozing=" + mBatterySaverSnoozing + " mIsPowered=" + mIsPowered + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky @@ -460,66 +539,166 @@ public class BatterySaverStateMachine { return; // Not fully initialized yet. } - if (!isBatteryLowLocked()) { - updateSnoozingLocked(false, "Battery not low"); - } + updateStateLocked(false, false); + // Adaptive control. if (SystemClock.elapsedRealtime() - mLastAdaptiveBatterySaverChangedExternallyElapsed > ADAPTIVE_CHANGE_TIMEOUT_MS) { mBatterySaverController.setAdaptivePolicyEnabledLocked( false, BatterySaverController.REASON_TIMEOUT); mBatterySaverController.resetAdaptivePolicyLocked( BatterySaverController.REASON_TIMEOUT); + } else if (mIsPowered && mBatteryLevel >= ADAPTIVE_AUTO_DISABLE_BATTERY_LEVEL) { + mBatterySaverController.setAdaptivePolicyEnabledLocked(false, + BatterySaverController.REASON_PLUGGED_IN); + } + } + + /** + * Update the state machine based on the current settings and battery/charge status. + * + * @param manual Whether the change was made by the user. + * @param enable Whether the user wants to turn battery saver on or off. Is only used if {@param + * manual} is true. + */ + @GuardedBy("mLock") + private void updateStateLocked(boolean manual, boolean enable) { + if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { + return; // Not fully initialized yet. } - if (mIsPowered) { - updateSnoozingLocked(false, "Plugged in"); - enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, - BatterySaverController.REASON_PLUGGED_IN, - "Plugged in"); + switch (mState) { + case STATE_OFF: { + if (!mIsPowered) { + if (manual) { + if (!enable) { + Slog.e(TAG, "Tried to disable BS when it's already OFF"); + return; + } + enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, + BatterySaverController.REASON_MANUAL_ON); + mState = STATE_MANUAL_ON; + } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ true, /*manual*/ false, + BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON); + mState = STATE_AUTOMATIC_ON; + } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ true, /*manual*/ false, + BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON); + mState = STATE_AUTOMATIC_ON; + } + } + break; + } - if (mBatteryLevel >= 80 /* Arbitrary level */) { - mBatterySaverController.setAdaptivePolicyEnabledLocked( - false, BatterySaverController.REASON_PLUGGED_IN); + case STATE_MANUAL_ON: { + if (manual) { + if (enable) { + Slog.e(TAG, "Tried to enable BS when it's already MANUAL_ON"); + return; + } + enableBatterySaverLocked(/*enable*/ false, /*manual*/ true, + BatterySaverController.REASON_MANUAL_OFF); + mState = STATE_OFF; + } else if (mIsPowered) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_PLUGGED_IN); + if (mSettingBatterySaverEnabledSticky + && !mBatterySaverStickyBehaviourDisabled) { + mState = STATE_PENDING_STICKY_ON; + } else { + mState = STATE_OFF; + } + } + break; } - } else if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) { - if (mSettingBatterySaverStickyAutoDisableEnabled - && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold) { - setStickyActive(false); - } else { - // Re-enable BS. - enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, - BatterySaverController.REASON_STICKY_RESTORE, - "Sticky restore"); + case STATE_AUTOMATIC_ON: { + if (mIsPowered) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_PLUGGED_IN); + mState = STATE_OFF; + } else if (manual) { + if (enable) { + Slog.e(TAG, "Tried to enable BS when it's already AUTO_ON"); + return; + } + enableBatterySaverLocked(/*enable*/ false, /*manual*/ true, + BatterySaverController.REASON_MANUAL_OFF); + // When battery saver is disabled manually (while battery saver is enabled) + // when the battery level is low, we "snooze" BS -- i.e. disable auto battery + // saver. + // We resume auto-BS once the battery level is not low, or the device is + // plugged in. + mState = STATE_OFF_AUTOMATIC_SNOOZED; + } else if (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF); + mState = STATE_OFF; + } else if (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF); + mState = STATE_OFF; + } else if (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked()) { + enableBatterySaverLocked(/*enable*/ false, /*manual*/ false, + BatterySaverController.REASON_SETTING_CHANGED); + mState = STATE_OFF; + } + break; } - } else if (mSettingAutomaticBatterySaver - == PowerManager.POWER_SAVER_MODE_PERCENTAGE - && isAutoBatterySaverConfiguredLocked()) { - if (mIsBatteryLevelLow && !mBatterySaverSnoozing) { - enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, - BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON, - "Percentage Auto ON"); - } else { - // Battery not low - enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, - BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF, - "Percentage Auto OFF"); + case STATE_OFF_AUTOMATIC_SNOOZED: { + if (manual) { + if (!enable) { + Slog.e(TAG, "Tried to disable BS when it's already AUTO_SNOOZED"); + return; + } + enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, + BatterySaverController.REASON_MANUAL_ON); + mState = STATE_MANUAL_ON; + } else if (mIsPowered // Plugging in resets snooze. + || (isAutomaticModeActiveLocked() && !isInAutomaticLowZoneLocked()) + || (isDynamicModeActiveLocked() && !isInDynamicLowZoneLocked()) + || (!isAutomaticModeActiveLocked() && !isDynamicModeActiveLocked())) { + mState = STATE_OFF; + } + break; } - } else if (mSettingAutomaticBatterySaver - == PowerManager.POWER_SAVER_MODE_DYNAMIC) { - if (mBatteryLevel >= mDynamicPowerSavingsDisableThreshold) { - enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false, - BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF, - "Dynamic Warning Auto OFF"); - } else if (mDynamicPowerSavingsBatterySaver && !mBatterySaverSnoozing) { - enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false, - BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON, - "Dynamic Warning Auto ON"); + + case STATE_PENDING_STICKY_ON: { + if (manual) { + // This shouldn't be possible. We'll only be in this state when the device is + // plugged in, so the user shouldn't be able to manually change state. + Slog.e(TAG, "Tried to manually change BS state from PENDING_STICKY_ON"); + return; + } + final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled + && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold; + final boolean isStickyDisabled = + mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky; + if (isStickyDisabled || shouldTurnOffSticky) { + setStickyActive(false); + mState = STATE_OFF; + } else if (!mIsPowered) { + // Re-enable BS. + enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, + BatterySaverController.REASON_STICKY_RESTORE); + mState = STATE_MANUAL_ON; + } + break; } + + default: + Slog.wtf(TAG, "Unknown state: " + mState); + break; + } + } + + @VisibleForTesting + int getState() { + synchronized (mLock) { + return mState; } - // do nothing if automatic battery saver mode = PERCENTAGE and low warning threshold = 0% } /** @@ -533,13 +712,17 @@ public class BatterySaverStateMachine { Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled); } synchronized (mLock) { - enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true, - (enabled ? BatterySaverController.REASON_MANUAL_ON - : BatterySaverController.REASON_MANUAL_OFF), - (enabled ? "Manual ON" : "Manual OFF")); + updateStateLocked(true, enabled); + // TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true and + // enabled is false } } + @GuardedBy("mLock") + private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) { + enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason)); + } + /** * Actually enable / disable battery saver. Write the new state to the global settings * and propagate it to {@link #mBatterySaverController}. @@ -566,20 +749,6 @@ public class BatterySaverStateMachine { mLastChangedIntReason = intReason; mLastChangedStrReason = strReason; - if (manual) { - if (enable) { - updateSnoozingLocked(false, "Manual snooze OFF"); - } else { - // When battery saver is disabled manually (while battery saver is enabled) - // when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver. - // We resume auto-BS once the battery level is not low, or the device is plugged in. - if (mBatterySaverController.isFullEnabled() && isBatteryLowLocked()) { - updateSnoozingLocked(true, "Manual snooze"); - } - // TODO: maybe turn off adaptive if it's on and advertiseIsEnabled is true - } - } - mSettingBatterySaverEnabled = enable; putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0); @@ -641,15 +810,6 @@ public class BatterySaverStateMachine { manager.cancel(DYNAMIC_MODE_NOTIFICATION_ID); } - @GuardedBy("mLock") - private void updateSnoozingLocked(boolean snoozing, String reason) { - if (mBatterySaverSnoozing == snoozing) { - return; - } - if (DEBUG) Slog.d(TAG, "Snooze: " + (snoozing ? "start" : "stop") + " reason=" + reason); - mBatterySaverSnoozing = snoozing; - } - private void setStickyActive(boolean active) { mSettingBatterySaverEnabledSticky = active; putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY, @@ -684,6 +844,8 @@ public class BatterySaverStateMachine { pw.print(")"); } pw.println(); + pw.print(" mState="); + pw.println(mState); pw.print(" mLastChangedIntReason="); pw.println(mLastChangedIntReason); @@ -697,9 +859,6 @@ public class BatterySaverStateMachine { pw.print(" mBatteryStatusSet="); pw.println(mBatteryStatusSet); - pw.print(" mBatterySaverSnoozing="); - pw.println(mBatterySaverSnoozing); - pw.print(" mIsPowered="); pw.println(mIsPowered); pw.print(" mBatteryLevel="); @@ -731,6 +890,7 @@ public class BatterySaverStateMachine { proto.write(BatterySaverStateMachineProto.ENABLED, mBatterySaverController.isEnabled()); + proto.write(BatterySaverStateMachineProto.STATE, mState); proto.write(BatterySaverStateMachineProto.IS_FULL_ENABLED, mBatterySaverController.isFullEnabled()); proto.write(BatterySaverStateMachineProto.IS_ADAPTIVE_ENABLED, @@ -742,8 +902,6 @@ public class BatterySaverStateMachine { proto.write(BatterySaverStateMachineProto.SETTINGS_LOADED, mSettingsLoaded); proto.write(BatterySaverStateMachineProto.BATTERY_STATUS_SET, mBatteryStatusSet); - proto.write(BatterySaverStateMachineProto.BATTERY_SAVER_SNOOZING, - mBatterySaverSnoozing); proto.write(BatterySaverStateMachineProto.IS_POWERED, mIsPowered); proto.write(BatterySaverStateMachineProto.BATTERY_LEVEL, mBatteryLevel); diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java index 86e859809ffd..2e5efbd1deef 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java @@ -28,6 +28,7 @@ import android.app.NotificationManager; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.os.PowerManager; import android.provider.Settings.Global; import androidx.test.filters.SmallTest; @@ -452,6 +453,21 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); assertEquals(30, mPersistedState.batteryLevel); assertEquals(true, mPersistedState.batteryLow); + + // Disable auto battery saver. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + mDevice.setBatteryLevel(25); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(25, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // PowerManager sets batteryLow to true at 15% if battery saver trigger level is lower. + mDevice.setBatteryLevel(15); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(15, mPersistedState.batteryLevel); + assertEquals(true, mPersistedState.batteryLow); } @Test @@ -542,6 +558,12 @@ public class BatterySaverStateMachineTest { assertEquals(100, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); + mDevice.setBatteryLevel(97); + + assertEquals(true, mDevice.batterySaverEnabled); // Stays on. + assertEquals(97, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + mDevice.setBatteryLevel(95); assertEquals(true, mDevice.batterySaverEnabled); // Stays on. @@ -719,6 +741,48 @@ public class BatterySaverStateMachineTest { } @Test + public void testAutoBatterySaver_withSticky_withAutoOffToggled() { + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90); + + // Scenario 1: User turns BS on manually above the threshold, it shouldn't turn off even + // with battery level change above threshold. + mDevice.setBatteryLevel(100); + mTarget.setBatterySaverEnabledManually(true); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(100, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setBatteryLevel(95); + + assertEquals(true, mDevice.batterySaverEnabled); // Stays on. + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Disable auto disable while in the pending sticky state. BS should reactivate after + // unplug. + mDevice.setPowered(true); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0); + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); // Sticky BS should activate. + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Enable auto disable while in the pending sticky state. Sticky should turn off after + // unplug. + mDevice.setPowered(true); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1); + mDevice.setPowered(false); + + assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled. + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + } + + @Test public void testAutoBatterySaver_withStickyDisabled() { when(mMockResources.getBoolean( com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled)) @@ -739,7 +803,9 @@ public class BatterySaverStateMachineTest { assertEquals(30, mPersistedState.batteryLevel); assertEquals(true, mPersistedState.batteryLow); + mDevice.setPowered(true); mDevice.setBatteryLevel(80); + mDevice.setPowered(false); assertEquals(false, mDevice.batterySaverEnabled); // Not sticky. assertEquals(80, mPersistedState.batteryLevel); @@ -830,10 +896,9 @@ public class BatterySaverStateMachineTest { assertEquals(90, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); - // Reboot -- setting BS from adb is also sticky. + // Reboot -- LOW_POWER_MODE shouldn't be persisted. initDevice(); - - assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(false, mDevice.batterySaverEnabled); assertEquals(90, mPersistedState.batteryLevel); assertEquals(false, mPersistedState.batteryLow); } @@ -841,7 +906,8 @@ public class BatterySaverStateMachineTest { @Test public void testAutoBatterySaver_smartBatterySaverEnabled() { mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 50); - mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, 1); + mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_DYNAMIC); mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0); assertEquals(false, mDevice.batterySaverEnabled); @@ -922,8 +988,8 @@ public class BatterySaverStateMachineTest { mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 71); mDevice.setBatteryLevel(mPersistedState.batteryLevel); - // changes are only registered if some battery level changed - assertEquals(false, mDevice.batterySaverEnabled); + // Changes should register immediately. + assertEquals(true, mDevice.batterySaverEnabled); assertEquals(70, mPersistedState.batteryLevel); mDevice.setBatteryLevel(69); @@ -935,8 +1001,8 @@ public class BatterySaverStateMachineTest { mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 60); mDevice.setBatteryLevel(mPersistedState.batteryLevel); - // changes are only registered if battery level changed - assertEquals(true, mDevice.batterySaverEnabled); + // Changes should register immediately. + assertEquals(false, mDevice.batterySaverEnabled); assertEquals(69, mPersistedState.batteryLevel); mDevice.setBatteryLevel(68); @@ -956,4 +1022,220 @@ public class BatterySaverStateMachineTest { assertEquals(true, mDevice.batterySaverEnabled); assertEquals(30, mPersistedState.batteryLevel); } + + @Test + public void testAutoBatterySaver_snoozed_autoEnabled() { + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 30); + // Test dynamic threshold higher than automatic to make sure it doesn't interfere when it's + // not enabled. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 50); + mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_PERCENTAGE); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(100, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(90); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(90, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(51); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(51, mPersistedState.batteryLevel); + + // Hit dynamic threshold. BS should be disabled since dynamic is off + mDevice.setBatteryLevel(50); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(50, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(30); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(20); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold. Level is still below, so should still be snoozed. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 25); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold even more. Battery no longer considered "low" so snoozing should be + // disabled. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 10); + // "batteryLow" is set in setBatteryLevel. + mDevice.setBatteryLevel(19); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(19, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(10); + + assertEquals(true, mDevice.batterySaverEnabled); // No longer snoozing. + assertEquals(10, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + // Plug in and out, snooze will reset. + mDevice.setPowered(true); + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(10, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + mDevice.setBatteryLevel(60); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(60, mPersistedState.batteryLevel); + + // Test toggling resets snooze. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + mDevice.setPowered(false); + mDevice.setBatteryLevel(45); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + // Disable and re-enable. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + + assertEquals(true, mDevice.batterySaverEnabled); // Snooze reset + assertEquals(45, mPersistedState.batteryLevel); + } + + @Test + public void testAutoBatterySaver_snoozed_dynamicEnabled() { + // Test auto threshold higher than dynamic to make sure it doesn't interfere when it's + // not enabled. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 30); + mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, + PowerManager.POWER_SAVER_MODE_DYNAMIC); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 1); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(100, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(90); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(90, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(51); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(51, mPersistedState.batteryLevel); + + // Hit automatic threshold. BS should be disabled since automatic is off + mDevice.setBatteryLevel(50); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(50, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(30); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(20); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold. Level is still below, so should still be snoozed. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 25); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + // Lower threshold even more. Battery no longer considered "low" so snoozing should be + // disabled. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 10); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(20, mPersistedState.batteryLevel); + + mDevice.setBatteryLevel(10); + + assertEquals(true, mDevice.batterySaverEnabled); // No longer snoozing. + assertEquals(10, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + + // Plug in and out, snooze will reset. + mDevice.setPowered(true); + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(10, mPersistedState.batteryLevel); + + mDevice.setPowered(true); + mDevice.setBatteryLevel(60); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(60, mPersistedState.batteryLevel); + + // Test toggling resets snooze. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 50); + mDevice.setPowered(false); + mDevice.setBatteryLevel(45); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze. + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(45, mPersistedState.batteryLevel); + + // Disable and re-enable. + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0); + mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 1); + + assertEquals(true, mDevice.batterySaverEnabled); // Snooze reset + assertEquals(45, mPersistedState.batteryLevel); + } } |