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:
Kweku Adams
2019-01-18 11:18:16 -08:00
parent cdfa6ed815
commit b243a996ca
7 changed files with 376 additions and 60 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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(