Automatically turning off battery saver at a high percentage.
This introduces a setting that turns off sticky Battery Saver above a certain threshold and disables Battery Saver if it was enabled due to the sticky setting. Bug: 112232746 Test: atest com.android.server.power.batterysaver.BatterySaverStateMachineTest Test: atest android.provider.SettingsBackupTest Change-Id: Ib9a9fd627a56529404b41fbabedf8bb4a372074e
This commit is contained in:
@@ -12052,6 +12052,31 @@ public final class Settings {
|
||||
@TestApi
|
||||
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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(
|
||||
Global.DYNAMIC_POWER_SAVINGS_ENABLED),
|
||||
Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED),
|
||||
false, mSettingsObserver, UserHandle.USER_SYSTEM);
|
||||
cr.registerContentObserver(Settings.Global.getUriFor(
|
||||
Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD),
|
||||
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD),
|
||||
false, mSettingsObserver, UserHandle.USER_SYSTEM);
|
||||
cr.registerContentObserver(Settings.Global.getUriFor(
|
||||
Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED),
|
||||
false, mSettingsObserver, UserHandle.USER_SYSTEM);
|
||||
cr.registerContentObserver(Settings.Global.getUriFor(
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -517,6 +522,197 @@ public class BatterySaverStateMachineTest {
|
||||
assertEquals(false, mPersistedState.batteryLow);
|
||||
}
|
||||
|
||||
@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(
|
||||
Reference in New Issue
Block a user