diff options
| -rw-r--r-- | core/java/android/provider/Settings.java | 32 | ||||
| -rw-r--r-- | core/proto/android/providers/settings/global.proto | 10 | ||||
| -rw-r--r-- | core/proto/android/server/powermanagerservice.proto | 8 | ||||
| -rw-r--r-- | packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java | 22 | ||||
| -rw-r--r-- | services/core/java/com/android/server/power/batterysaver/BatterySaverController.java | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java | 125 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java (renamed from services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java) | 238 |
7 files changed, 376 insertions, 60 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 39c4266e4cf8..3fac9385e31f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12053,6 +12053,31 @@ public final class Settings { public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; /** + * When a device is unplugged from a changer (or is rebooted), do not re-activate battery + * saver even if {@link #LOW_POWER_MODE_STICKY} is 1, if the battery level is equal to or + * above this threshold. + * + * @hide + */ + public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL = + "low_power_sticky_auto_disable_level"; + + private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR = + new SettingsValidators.InclusiveIntegerRangeValidator(0, 100); + + /** + * Whether sticky battery saver should be deactivated once the battery level has reached the + * threshold specified by {@link #LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL}. + * + * @hide + */ + public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED = + "low_power_sticky_auto_disable_enabled"; + + private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"}); + + /** * Battery level [1-100] at which low power mode automatically turns on. * Pre-Q If 0, it will not automatically turn on. Q and newer it will only automatically * turn on if the {@link #AUTOMATIC_POWER_SAVER_MODE} setting is also set to @@ -12064,7 +12089,6 @@ public final class Settings { */ public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level"; - private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR = new SettingsValidators.InclusiveIntegerRangeValidator(0, 100); @@ -13055,6 +13079,8 @@ public final class Settings { ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS, LOW_POWER_MODE_TRIGGER_LEVEL, + LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, + LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, BLUETOOTH_ON, PRIVATE_DNS_MODE, PRIVATE_DNS_SPECIFIER, @@ -13093,6 +13119,10 @@ public final class Settings { VALIDATORS.put(ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_VALIDATOR); VALIDATORS.put(ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS, ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS_VALIDATOR); + VALIDATORS.put(LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, + LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR); + VALIDATORS.put(LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, + LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR); VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL, LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR); VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL_MAX, LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR); diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index cc5aa20258ce..30362cc01a51 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -528,6 +528,16 @@ message GlobalSettingsProto { // Whether automatic battery saver mode is controlled via percentage, // {@link #DYNAMIC_POWER_SAVINGS_ENABLED} or disabled. optional SettingProto automatic_power_saver_mode = 4 [ (android.privacy).dest = DEST_AUTOMATIC]; + // If 1, battery saver (low_power_mode) will be re-activated after the device is + // unplugged from a charger or rebooted. + optional SettingProto sticky_enabled = 5; + // Whether sticky battery saver should be deactivated once the battery level has reached the + // threshold specified by sticky_disable_level. + optional SettingProto sticky_auto_disable_enabled = 6; + // When a device is unplugged from a changer (or is rebooted), do not re-activate battery + // saver even if {@link #LOW_POWER_MODE_STICKY} is 1, if the battery level is equal to or + // above this threshold. + optional SettingProto sticky_auto_disable_level = 7; } optional LowPowerMode low_power_mode = 70; diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index cee556a7b67f..af0a942dea2f 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -350,4 +350,12 @@ message BatterySaverStateMachineProto { // The value of Global.LOW_POWER_MODE_TRIGGER_LEVEL. This is a cached value, so it could // be slightly different from what's in GlobalSettingsProto.LowPowerMode. optional int32 setting_battery_saver_trigger_threshold = 11; + + // The value of Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED. This is a cached value, so + // it could be slightly different from what's in GlobalSettingsProto.LowPowerMode. + optional bool setting_battery_saver_sticky_auto_disable_enabled = 12; + + // The value of Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL. This is a cached value, so it + // could be slightly different from what's in GlobalSettingsProto.LowPowerMode. + optional int32 setting_battery_saver_sticky_auto_disable_threshold = 13; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 18bdb20ad0a6..7bbf3ee1f870 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -19,7 +19,6 @@ package com.android.providers.settings; import android.annotation.NonNull; import android.os.UserHandle; import android.provider.Settings; -import android.provider.Settings.Global; import android.providers.settings.GlobalSettingsProto; import android.providers.settings.SecureSettingsProto; import android.providers.settings.SettingProto; @@ -397,7 +396,7 @@ class SettingsProtoDumpUtil { p.end(certPinToken); dumpSetting(s, p, - Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, + Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, GlobalSettingsProto.CHAINED_BATTERY_ATTRIBUTION_ENABLED); dumpSetting(s, p, Settings.Global.COMPATIBILITY_MODE, @@ -734,7 +733,7 @@ class SettingsProtoDumpUtil { Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, GlobalSettingsProto.HEADS_UP_NOTIFICATIONS_ENABLED); dumpSetting(s, p, - Global.HIDDEN_API_BLACKLIST_EXEMPTIONS, + Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS, GlobalSettingsProto.HIDDEN_API_BLACKLIST_EXEMPTIONS); final long inetCondToken = p.start(GlobalSettingsProto.INET_CONDITION); @@ -829,6 +828,15 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.AUTOMATIC_POWER_SAVER_MODE, GlobalSettingsProto.LowPowerMode.AUTOMATIC_POWER_SAVER_MODE); + dumpSetting(s, p, + Settings.Global.LOW_POWER_MODE_STICKY, + GlobalSettingsProto.LowPowerMode.STICKY_ENABLED); + dumpSetting(s, p, + Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, + GlobalSettingsProto.LowPowerMode.STICKY_AUTO_DISABLE_ENABLED); + dumpSetting(s, p, + Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, + GlobalSettingsProto.LowPowerMode.STICKY_AUTO_DISABLE_LEVEL); p.end(lpmToken); dumpSetting(s, p, @@ -879,7 +887,7 @@ class SettingsProtoDumpUtil { p.end(multiSimToken); dumpSetting(s, p, - Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED, + Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED, GlobalSettingsProto.NATIVE_FLAGS_HEALTH_CHECK_ENABLED); final long netstatsToken = p.start(GlobalSettingsProto.NETSTATS); @@ -1259,10 +1267,10 @@ class SettingsProtoDumpUtil { final long soundTriggerToken = p.start(GlobalSettingsProto.SOUND_TRIGGER); dumpSetting(s, p, - Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, + Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, GlobalSettingsProto.SoundTrigger.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY); dumpSetting(s, p, - Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT, + Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT, GlobalSettingsProto.SoundTrigger.DETECTION_SERVICE_OP_TIMEOUT_MS); p.end(soundTriggerToken); @@ -1558,7 +1566,7 @@ class SettingsProtoDumpUtil { GlobalSettingsProto.ZRAM_ENABLED); dumpSetting(s, p, - Global.APP_OPS_CONSTANTS, + Settings.Global.APP_OPS_CONSTANTS, GlobalSettingsProto.APP_OPS_CONSTANTS); p.end(token); 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 1d74350de978..ab2807a9a8a8 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -104,6 +104,7 @@ 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 = 13; /** * 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 b7f28da499e9..404e450ccf14 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -28,7 +28,6 @@ import android.os.Handler; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; -import android.provider.Settings.Global; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -85,42 +84,56 @@ public class BatterySaverStateMachine { @GuardedBy("mLock") private boolean mIsBatteryLevelLow; - /** Previously known value of Global.LOW_POWER_MODE. */ + /** Previously known value of Settings.Global.LOW_POWER_MODE. */ @GuardedBy("mLock") private boolean mSettingBatterySaverEnabled; - /** Previously known value of Global.LOW_POWER_MODE_STICKY. */ + /** Previously known value of Settings.Global.LOW_POWER_MODE_STICKY. */ @GuardedBy("mLock") private boolean mSettingBatterySaverEnabledSticky; /** Config flag to track if battery saver's sticky behaviour is disabled. */ private final boolean mBatterySaverStickyBehaviourDisabled; + /** + * Whether or not to end sticky battery saver upon reaching a level specified by + * {@link #mSettingBatterySaverStickyAutoDisableThreshold}. + */ + @GuardedBy("mLock") + private boolean mSettingBatterySaverStickyAutoDisableEnabled; + + /** + * The battery level at which to end sticky battery saver. Only useful if + * {@link #mSettingBatterySaverStickyAutoDisableEnabled} is {@code true}. + */ + @GuardedBy("mLock") + private int mSettingBatterySaverStickyAutoDisableThreshold; + /** Config flag to track default disable threshold for Dynamic Power Savings enabled battery * saver. */ @GuardedBy("mLock") private final int mDynamicPowerSavingsDefaultDisableThreshold; /** - * Previously known value of Global.LOW_POWER_MODE_TRIGGER_LEVEL. + * Previously known value of Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL. * (Currently only used in dumpsys.) */ @GuardedBy("mLock") private int mSettingBatterySaverTriggerThreshold; - /** Previously known value of Global.AUTOMATIC_POWER_SAVER_MODE. */ + /** Previously known value of Settings.Global.AUTOMATIC_POWER_SAVER_MODE. */ @GuardedBy("mLock") private int mSettingAutomaticBatterySaver; /** When to disable battery saver again if it was enabled due to an external suggestion. - * Corresponds to Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD. + * Corresponds to Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD. */ @GuardedBy("mLock") private int mDynamicPowerSavingsDisableThreshold; /** * Whether we've received a suggestion that battery saver should be on from an external app. - * Updates when Global.DYNAMIC_POWER_SAVINGS_ENABLED changes. + * Updates when Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED changes. */ @GuardedBy("mLock") private boolean mDynamicPowerSavingsBatterySaver; @@ -181,7 +194,7 @@ public class BatterySaverStateMachine { Slog.d(TAG, "onBootCompleted"); } // Just booted. We don't want LOW_POWER_MODE to be persisted, so just always clear it. - putGlobalSetting(Global.LOW_POWER_MODE, 0); + putGlobalSetting(Settings.Global.LOW_POWER_MODE, 0); // This is called with the power manager lock held. Don't do anything that may call to // upper services. (e.g. don't call into AM directly) @@ -199,13 +212,19 @@ public class BatterySaverStateMachine { Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), false, mSettingsObserver, UserHandle.USER_SYSTEM); cr.registerContentObserver(Settings.Global.getUriFor( - Global.AUTOMATIC_POWER_SAVER_MODE), + Settings.Global.AUTOMATIC_POWER_SAVER_MODE), + false, mSettingsObserver, UserHandle.USER_SYSTEM); + cr.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED), + false, mSettingsObserver, UserHandle.USER_SYSTEM); + cr.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD), false, mSettingsObserver, UserHandle.USER_SYSTEM); cr.registerContentObserver(Settings.Global.getUriFor( - Global.DYNAMIC_POWER_SAVINGS_ENABLED), + Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED), false, mSettingsObserver, UserHandle.USER_SYSTEM); cr.registerContentObserver(Settings.Global.getUriFor( - Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD), + Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL), false, mSettingsObserver, UserHandle.USER_SYSTEM); synchronized (mLock) { @@ -239,25 +258,31 @@ public class BatterySaverStateMachine { } @GuardedBy("mLock") - void refreshSettingsLocked() { + private void refreshSettingsLocked() { final boolean lowPowerModeEnabled = getGlobalSetting( Settings.Global.LOW_POWER_MODE, 0) != 0; final boolean lowPowerModeEnabledSticky = getGlobalSetting( Settings.Global.LOW_POWER_MODE_STICKY, 0) != 0; final boolean dynamicPowerSavingsBatterySaver = getGlobalSetting( - Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0; + Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0; final int lowPowerModeTriggerLevel = getGlobalSetting( Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); - final int automaticBatterySaver = getGlobalSetting( - Global.AUTOMATIC_POWER_SAVER_MODE, + final int automaticBatterySaverMode = getGlobalSetting( + Settings.Global.AUTOMATIC_POWER_SAVER_MODE, PowerManager.POWER_SAVER_MODE_PERCENTAGE); final int dynamicPowerSavingsDisableThreshold = getGlobalSetting( - Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, + Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, mDynamicPowerSavingsDefaultDisableThreshold); + final boolean isStickyAutoDisableEnabled = getGlobalSetting( + Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0) != 0; + final int stickyAutoDisableThreshold = getGlobalSetting( + Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90); setSettingsLocked(lowPowerModeEnabled, lowPowerModeEnabledSticky, - lowPowerModeTriggerLevel, automaticBatterySaver, dynamicPowerSavingsBatterySaver, - dynamicPowerSavingsDisableThreshold); + lowPowerModeTriggerLevel, + isStickyAutoDisableEnabled, stickyAutoDisableThreshold, + automaticBatterySaverMode, + dynamicPowerSavingsBatterySaver, dynamicPowerSavingsDisableThreshold); } /** @@ -269,12 +294,16 @@ public class BatterySaverStateMachine { @GuardedBy("mLock") @VisibleForTesting void setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky, - int batterySaverTriggerThreshold, int automaticBatterySaver, + int batterySaverTriggerThreshold, + boolean isStickyAutoDisableEnabled, int stickyAutoDisableThreshold, + int automaticBatterySaver, boolean dynamicPowerSavingsBatterySaver, int dynamicPowerSavingsDisableThreshold) { if (DEBUG) { Slog.d(TAG, "setSettings: enabled=" + batterySaverEnabled + " sticky=" + batterySaverEnabledSticky + " threshold=" + batterySaverTriggerThreshold + + " stickyAutoDisableEnabled=" + isStickyAutoDisableEnabled + + " stickyAutoDisableThreshold=" + stickyAutoDisableThreshold + " automaticBatterySaver=" + automaticBatterySaver + " dynamicPowerSavingsBatterySaver=" + dynamicPowerSavingsBatterySaver + " dynamicPowerSavingsDisableThreshold=" @@ -283,11 +312,19 @@ public class BatterySaverStateMachine { mSettingsLoaded = true; + // Set sensible limits. + stickyAutoDisableThreshold = Math.max(stickyAutoDisableThreshold, + batterySaverTriggerThreshold); + final boolean enabledChanged = mSettingBatterySaverEnabled != batterySaverEnabled; final boolean stickyChanged = mSettingBatterySaverEnabledSticky != batterySaverEnabledSticky; final boolean thresholdChanged = mSettingBatterySaverTriggerThreshold != batterySaverTriggerThreshold; + final boolean stickyAutoDisableEnabledChanged = + mSettingBatterySaverStickyAutoDisableEnabled != isStickyAutoDisableEnabled; + final boolean stickyAutoDisableThresholdChanged = + mSettingBatterySaverStickyAutoDisableThreshold != stickyAutoDisableThreshold; final boolean automaticModeChanged = mSettingAutomaticBatterySaver != automaticBatterySaver; final boolean dynamicPowerSavingsThresholdChanged = mDynamicPowerSavingsDisableThreshold != dynamicPowerSavingsDisableThreshold; @@ -295,6 +332,7 @@ public class BatterySaverStateMachine { mDynamicPowerSavingsBatterySaver != dynamicPowerSavingsBatterySaver; if (!(enabledChanged || stickyChanged || thresholdChanged || automaticModeChanged + || stickyAutoDisableEnabledChanged || stickyAutoDisableThresholdChanged || dynamicPowerSavingsThresholdChanged || dynamicPowerSavingsBatterySaverChanged)) { return; } @@ -302,6 +340,8 @@ public class BatterySaverStateMachine { mSettingBatterySaverEnabled = batterySaverEnabled; mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky; mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold; + mSettingBatterySaverStickyAutoDisableEnabled = isStickyAutoDisableEnabled; + mSettingBatterySaverStickyAutoDisableThreshold = stickyAutoDisableThreshold; mSettingAutomaticBatterySaver = automaticBatterySaver; mDynamicPowerSavingsDisableThreshold = dynamicPowerSavingsDisableThreshold; mDynamicPowerSavingsBatterySaver = dynamicPowerSavingsBatterySaver; @@ -376,7 +416,9 @@ public class BatterySaverStateMachine { + " mBatterySaverSnoozing=" + mBatterySaverSnoozing + " mIsPowered=" + mIsPowered + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver - + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky); + + " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky + + " mSettingBatterySaverStickyAutoDisableEnabled=" + + mSettingBatterySaverStickyAutoDisableEnabled); } if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) { return; // Not fully initialized yet. @@ -392,10 +434,15 @@ public class BatterySaverStateMachine { "Plugged in"); } else if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) { - // Re-enable BS. - enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, - BatterySaverController.REASON_STICKY_RESTORE, - "Sticky restore"); + if (mSettingBatterySaverStickyAutoDisableEnabled + && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold) { + setStickyActive(false); + } else { + // Re-enable BS. + enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true, + BatterySaverController.REASON_STICKY_RESTORE, + "Sticky restore"); + } } else if (mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_PERCENTAGE @@ -483,12 +530,10 @@ public class BatterySaverStateMachine { } mSettingBatterySaverEnabled = enable; - putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0); + putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0); if (manual) { - mSettingBatterySaverEnabledSticky = !mBatterySaverStickyBehaviourDisabled && enable; - putGlobalSetting(Global.LOW_POWER_MODE_STICKY, - mSettingBatterySaverEnabledSticky ? 1 : 0); + setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable); } mBatterySaverController.enableBatterySaver(enable, intReason); @@ -506,7 +551,8 @@ public class BatterySaverStateMachine { } } - private void triggerDynamicModeNotification() { + @VisibleForTesting + void triggerDynamicModeNotification() { NotificationManager manager = mContext.getSystemService(NotificationManager.class); ensureNotificationChannelExists(manager); @@ -553,14 +599,20 @@ public class BatterySaverStateMachine { mBatterySaverSnoozing = snoozing; } + private void setStickyActive(boolean active) { + mSettingBatterySaverEnabledSticky = active; + putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY, + mSettingBatterySaverEnabledSticky ? 1 : 0); + } + @VisibleForTesting protected void putGlobalSetting(String key, int value) { - Global.putInt(mContext.getContentResolver(), key, value); + Settings.Global.putInt(mContext.getContentResolver(), key, value); } @VisibleForTesting protected int getGlobalSetting(String key, int defValue) { - return Global.getInt(mContext.getContentResolver(), key, defValue); + return Settings.Global.getInt(mContext.getContentResolver(), key, defValue); } public void dump(PrintWriter pw) { @@ -597,6 +649,10 @@ public class BatterySaverStateMachine { pw.println(mSettingBatterySaverEnabled); pw.print(" mSettingBatterySaverEnabledSticky="); pw.println(mSettingBatterySaverEnabledSticky); + pw.print(" mSettingBatterySaverStickyAutoDisableEnabled="); + pw.println(mSettingBatterySaverStickyAutoDisableEnabled); + pw.print(" mSettingBatterySaverStickyAutoDisableThreshold="); + pw.println(mSettingBatterySaverStickyAutoDisableThreshold); pw.print(" mSettingBatterySaverTriggerThreshold="); pw.println(mSettingBatterySaverTriggerThreshold); pw.print(" mBatterySaverStickyBehaviourDisabled="); @@ -628,6 +684,13 @@ public class BatterySaverStateMachine { mSettingBatterySaverEnabledSticky); proto.write(BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_TRIGGER_THRESHOLD, mSettingBatterySaverTriggerThreshold); + proto.write( + BatterySaverStateMachineProto.SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_ENABLED, + mSettingBatterySaverStickyAutoDisableEnabled); + proto.write( + BatterySaverStateMachineProto + .SETTING_BATTERY_SAVER_STICKY_AUTO_DISABLE_THRESHOLD, + mSettingBatterySaverStickyAutoDisableThreshold); proto.end(token); } diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java index f31ca55d422e..5009d64b94d4 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java @@ -19,51 +19,41 @@ import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.app.NotificationManager; import android.content.ContentResolver; +import android.content.Context; import android.content.res.Resources; import android.provider.Settings.Global; -import android.test.mock.MockContext; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.google.common.base.Objects; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.HashMap; +import java.util.Objects; /** - atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java + * atest com.android.server.power.batterysaver.BatterySaverStateMachineTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class BatterySaverStateMachineTest { - private MyMockContext mMockContext; + private Context mMockContext; private ContentResolver mMockContextResolver; private BatterySaverController mMockBatterySaverController; + private NotificationManager mMockNotificationManager; private Device mDevice; private TestableBatterySaverStateMachine mTarget; private Resources mMockResources; - private class MyMockContext extends MockContext { - @Override - public ContentResolver getContentResolver() { - return mMockContextResolver; - } - - @Override - public Resources getResources() { - return mMockResources; - } - } - private DevicePersistedState mPersistedState; private class DevicePersistedState { @@ -116,6 +106,10 @@ public class BatterySaverStateMachineTest { mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE, 0) != 0, mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE_STICKY, 0) != 0, mDevice.getLowPowerModeTriggerLevel(), + mPersistedState.global.getOrDefault( + Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0) != 0, + mPersistedState.global.getOrDefault( + Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90), mPersistedState.global.getOrDefault(Global.AUTOMATIC_POWER_SAVER_MODE, 0), mPersistedState.global.getOrDefault( Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0, @@ -137,13 +131,13 @@ public class BatterySaverStateMachineTest { * Test target class. */ private class TestableBatterySaverStateMachine extends BatterySaverStateMachine { - public TestableBatterySaverStateMachine() { + TestableBatterySaverStateMachine() { super(new Object(), mMockContext, mMockBatterySaverController); } @Override protected void putGlobalSetting(String key, int value) { - if (Objects.equal(mPersistedState.global.get(key), value)) { + if (Objects.equals(mPersistedState.global.get(key), value)) { return; } mDevice.putGlobalSetting(key, value); @@ -163,15 +157,25 @@ public class BatterySaverStateMachineTest { void runOnBgThreadLazy(Runnable r, int delayMillis) { r.run(); } + + @Override + void triggerDynamicModeNotification() { + // Do nothing + } } @Before public void setUp() { - mMockContext = new MyMockContext(); + mMockContext = mock(Context.class); mMockContextResolver = mock(ContentResolver.class); mMockBatterySaverController = mock(BatterySaverController.class); + mMockNotificationManager = mock(NotificationManager.class); mMockResources = mock(Resources.class); + doReturn(mMockContextResolver).when(mMockContext).getContentResolver(); + doReturn(mMockResources).when(mMockContext).getResources(); + doReturn(mMockNotificationManager).when(mMockContext) + .getSystemService(NotificationManager.class); doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0)) .when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt()); when(mMockBatterySaverController.isEnabled()) @@ -446,8 +450,9 @@ public class BatterySaverStateMachineTest { } @Test - public void testAutoBatterySaver_withSticky() { + public void testAutoBatterySaver_withSticky_withAutoOffDisabled() { mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0); mTarget.setBatterySaverEnabledManually(true); @@ -518,6 +523,197 @@ public class BatterySaverStateMachineTest { } @Test + public void testAutoBatterySaver_withSticky_withAutoOffEnabled() { + 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); + + // Scenario 2: User turns BS on manually above the threshold then charges device. BS + // shouldn't turn back on. + mDevice.setPowered(true); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setBatteryLevel(97); + mDevice.setPowered(false); + + assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled. + assertEquals(97, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Scenario 3: User turns BS on manually above the threshold. Device drains below + // threshold and then charged to below threshold. Sticky BS should activate. + mTarget.setBatterySaverEnabledManually(true); + mDevice.setBatteryLevel(30); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + assertEquals(true, mPersistedState.batteryLow); + + mDevice.setPowered(true); + mDevice.setBatteryLevel(80); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(80, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); // Still enabled. + assertEquals(80, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setBatteryLevel(30); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(30, mPersistedState.batteryLevel); + assertEquals(true, mPersistedState.batteryLow); + + // Scenario 4: User turns BS on manually above the threshold. Device drains below + // threshold and is eventually charged to above threshold. Sticky BS should turn off. + mDevice.setPowered(true); + mDevice.setBatteryLevel(90); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(90, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setPowered(false); + + assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled. + assertEquals(90, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Scenario 5: User turns BS on manually below threshold and charges to below threshold. + // Sticky BS should activate. + mDevice.setBatteryLevel(70); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(70, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mTarget.setBatterySaverEnabledManually(true); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(70, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setPowered(true); + mDevice.setBatteryLevel(80); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(80, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); // Sticky BS still on. + assertEquals(80, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Scenario 6: User turns BS on manually below threshold and eventually charges to above + // threshold. Sticky BS should turn off. + + mDevice.setPowered(true); + mDevice.setBatteryLevel(95); + mDevice.setPowered(false); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(95, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Scenario 7: User turns BS on above threshold and then reboots device. Sticky BS + // shouldn't activate. + mTarget.setBatterySaverEnabledManually(true); + mPersistedState.batteryLevel = 93; + + initDevice(); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(93, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Scenario 8: User turns BS on below threshold and then reboots device without charging. + // Sticky BS should activate. + mDevice.setBatteryLevel(75); + mTarget.setBatterySaverEnabledManually(true); + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(75, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + initDevice(); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(75, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Scenario 9: User turns BS on below threshold and then reboots device after charging + // above threshold. Sticky BS shouldn't activate. + mDevice.setBatteryLevel(80); + mTarget.setBatterySaverEnabledManually(true); + mPersistedState.batteryLevel = 100; + + initDevice(); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(100, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + // Scenario 10: Somehow autoDisableLevel is set to a value below lowPowerModeTriggerLevel + // and then user enables manually above both thresholds, discharges below + // autoDisableLevel and then charges up to between autoDisableLevel and + // lowPowerModeTriggerLevel. Sticky BS shouldn't activate, but BS should still be on + // because the level is below lowPowerModeTriggerLevel. + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 75); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1); + mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 60); + initDevice(); + + mDevice.setBatteryLevel(90); + mTarget.setBatterySaverEnabledManually(true); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(90, mPersistedState.batteryLevel); + assertEquals(false, mPersistedState.batteryLow); + + mDevice.setBatteryLevel(50); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(50, mPersistedState.batteryLevel); + assertEquals(true, mPersistedState.batteryLow); + + mDevice.setPowered(true); + mDevice.setBatteryLevel(65); + + assertEquals(false, mDevice.batterySaverEnabled); + assertEquals(65, mPersistedState.batteryLevel); + assertEquals(true, mPersistedState.batteryLow); + + mDevice.setPowered(false); + + assertEquals(true, mDevice.batterySaverEnabled); + assertEquals(65, mPersistedState.batteryLevel); + assertEquals(true, mPersistedState.batteryLow); + } + + @Test public void testAutoBatterySaver_withStickyDisabled() { when(mMockResources.getBoolean( com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled)) |