Merge "Formalizing states in BatterySaverStateMachine."
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 8fce94e..9bf1825 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -318,6 +318,16 @@
// 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 @@
// 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 @@
// 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 5adcf35..f0e4625 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 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 af5d40bf..8f2e997 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 @@
* 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 @@
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 @@
@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 @@
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 @@
mLock = lock;
mContext = context;
mBatterySaverController = batterySaverController;
+ mState = STATE_OFF;
mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled);
@@ -188,8 +242,36 @@
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 @@
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 @@
? "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 @@
}
}
- @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 @@
+ " mSettingsLoaded=" + mSettingsLoaded
+ " mBatteryStatusSet=" + mBatteryStatusSet
+ " mIsBatteryLevelLow=" + mIsBatteryLevelLow
- + " mBatterySaverSnoozing=" + mBatterySaverSnoozing
+ " mIsPowered=" + mIsPowered
+ " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver
+ " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky
@@ -460,66 +539,166 @@
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");
-
- if (mBatteryLevel >= 80 /* Arbitrary level */) {
- mBatterySaverController.setAdaptivePolicyEnabledLocked(
- false, BatterySaverController.REASON_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;
}
- } 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_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 (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_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_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_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;
}
+
+ 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;
}
- // do nothing if automatic battery saver mode = PERCENTAGE and low warning threshold = 0%
+ }
+
+ @VisibleForTesting
+ int getState() {
+ synchronized (mLock) {
+ return mState;
+ }
}
/**
@@ -533,13 +712,17 @@
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 @@
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 @@
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 @@
pw.print(")");
}
pw.println();
+ pw.print(" mState=");
+ pw.println(mState);
pw.print(" mLastChangedIntReason=");
pw.println(mLastChangedIntReason);
@@ -697,9 +859,6 @@
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 @@
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 @@
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 86e8598..2e5efbd 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.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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
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);
+ }
}