summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt4
-rw-r--r--api/test-current.txt7
-rw-r--r--core/java/android/os/IPowerManager.aidl2
-rw-r--r--core/java/android/os/PowerManager.java100
-rw-r--r--core/java/android/provider/Settings.java45
-rw-r--r--core/proto/android/providers/settings/global.proto17
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java3
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java12
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java36
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java6
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java119
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java131
16 files changed, 466 insertions, 22 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 7d82e258ddde..5c4efcd90b5b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4109,8 +4109,12 @@ package android.os {
}
public final class PowerManager {
+ method public int getPowerSaveMode();
+ method public boolean setDynamicPowerSavings(boolean, int);
method public boolean setPowerSaveMode(boolean);
method public void userActivity(long, int, int);
+ field public static final int POWER_SAVER_MODE_DYNAMIC = 1; // 0x1
+ field public static final int POWER_SAVER_MODE_PERCENTAGE = 0; // 0x0
field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3
field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0
diff --git a/api/test-current.txt b/api/test-current.txt
index c3920b89b564..5531014cae75 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -744,7 +744,11 @@ package android.os {
}
public final class PowerManager {
+ method public int getPowerSaveMode();
+ method public boolean setDynamicPowerSavings(boolean, int);
method public boolean setPowerSaveMode(boolean);
+ field public static final int POWER_SAVER_MODE_DYNAMIC = 1; // 0x1
+ field public static final int POWER_SAVER_MODE_PERCENTAGE = 0; // 0x0
}
public class Process {
@@ -981,6 +985,9 @@ package android.provider {
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+ field public static final java.lang.String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
+ field public static final java.lang.String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
+ field public static final java.lang.String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
field public static final java.lang.String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
field public static final java.lang.String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
field public static final java.lang.String LOW_POWER_MODE = "low_power";
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 7ceeb526ffcf..ca5b2333b14d 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -48,6 +48,8 @@ interface IPowerManager
boolean isPowerSaveMode();
PowerSaveState getPowerSaveState(int serviceType);
boolean setPowerSaveMode(boolean mode);
+ boolean setDynamicPowerSavings(boolean dynamicPowerSavingsEnabled, int disableThreshold);
+ int getPowerSaveMode();
boolean isDeviceIdleMode();
boolean isLightDeviceIdleMode();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 27c281d56f4d..a307cd80e724 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -16,6 +16,7 @@
package android.os;
+import android.Manifest.permission;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -1185,6 +1186,105 @@ public final class PowerManager {
}
/**
+ * Updates the current state of dynamic power savings and disable threshold. This is
+ * a signal to the system which an app can update to serve as an indicator that
+ * the user will be in a battery critical situation before being able to plug in.
+ * Only apps with the {@link android.Manifest.permission#POWER_SAVER} permission may do this.
+ * This is a device global state, not a per user setting.
+ *
+ * <p>When enabled, the system may enact various measures for reducing power consumption in
+ * order to help ensure that the user will make it to their next charging point. The most
+ * visible of these will be the automatic enabling of battery saver if the user has set
+ * their battery saver mode to "automatic". Note
+ * that this is NOT simply an on/off switch for features, but rather a hint for the
+ * system to consider enacting these power saving features, some of which have additional
+ * logic around when to activate based on this signal.
+ *
+ * <p>The provided threshold is the percentage the system should consider itself safe at given
+ * the current state of the device. The value is an integer representing a battery level.
+ *
+ * <p>The threshold is meant to set an explicit stopping point for dynamic power savings
+ * functionality so that the dynamic power savings itself remains a signal rather than becoming
+ * an on/off switch for a subset of features.
+ * @hide
+ *
+ * @param dynamicPowerSavingsEnabled A signal indicating to the system if it believes the
+ * dynamic power savings behaviors should be activated.
+ * @param disableThreshold When the suggesting app believes it would be safe to disable dynamic
+ * power savings behaviors.
+ * @return True if the update was allowed and succeeded.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(permission.POWER_SAVER)
+ public boolean setDynamicPowerSavings(boolean dynamicPowerSavingsEnabled,
+ int disableThreshold) {
+ try {
+ return mService.setDynamicPowerSavings(dynamicPowerSavingsEnabled, disableThreshold);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates automatic battery saver toggling by the system will be based on percentage.
+ *
+ * @see PowerManager#getPowerSaveMode()
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int POWER_SAVER_MODE_PERCENTAGE = 0;
+
+ /**
+ * Indicates automatic battery saver toggling by the system will be based on the state
+ * of the dynamic power savings signal.
+ *
+ * @see PowerManager#setDynamicPowerSavings(boolean, int)
+ * @see PowerManager#getPowerSaveMode()
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int POWER_SAVER_MODE_DYNAMIC = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ POWER_SAVER_MODE_PERCENTAGE,
+ POWER_SAVER_MODE_DYNAMIC
+
+ })
+ public @interface AutoPowerSaverMode{}
+
+
+ /**
+ * Returns the current battery saver control mode. Values it may return are defined in
+ * AutoPowerSaverMode. Note that this is a global device state, not a per user setting.
+ *
+ * @return The current value power saver mode for the system.
+ *
+ * @see AutoPowerSaverMode
+ * @see PowerManager#getPowerSaveMode()
+ * @hide
+ */
+ @AutoPowerSaverMode
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.POWER_SAVER)
+ public int getPowerSaveMode() {
+ try {
+ return mService.getPowerSaveMode();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Get data about the battery saver mode for a specific service
* @param serviceType unique key for the service, one of {@link ServiceType}
* @return Battery saver state data.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f84079222536..040222217db4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11811,14 +11811,54 @@ public final class Settings {
/**
* Battery level [1-100] at which low power mode automatically turns on.
- * If 0, it will not automatically turn 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
+ * {@link android.os.PowerManager.AutoPowerSaverMode#POWER_SAVER_MODE_PERCENTAGE}.
+ *
+ * @see #AUTOMATIC_POWER_SAVER_MODE
+ * @see android.os.PowerManager#getPowerSaveMode()
* @hide
*/
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);
+ /**
+ * Whether battery saver is currently set to trigger based on percentage, dynamic power
+ * savings trigger, or none. See {@link android.os.PowerManager.AutoPowerSaverMode} for
+ * accepted values.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
+
+ private static final Validator AUTOMATIC_POWER_SAVER_MODE_VALIDATOR =
+ new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+
+ /**
+ * The setting that backs the disable threshold for the setPowerSavingsWarning api in
+ * PowerManager
+ *
+ * @see android.os.PowerManager#setDynamicPowerSavings(boolean, int)
+ * @hide
+ */
+ @TestApi
+ public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
+ "dynamic_power_savings_disable_threshold";
+ private static final Validator DYNAMIC_POWER_SAVINGS_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+
+ /**
+ * The setting which backs the setDynamicPowerSavings api in PowerManager.
+ *
+ * @see android.os.PowerManager#setDynamicPowerSavings(boolean, int)
+ * @hide
+ */
+ @TestApi
+ public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
/**
* The max value for {@link #LOW_POWER_MODE_TRIGGER_LEVEL}. If this setting is not set
@@ -12742,6 +12782,9 @@ public final class Settings {
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);
+ VALIDATORS.put(AUTOMATIC_POWER_SAVER_MODE, AUTOMATIC_POWER_SAVER_MODE_VALIDATOR);
+ VALIDATORS.put(DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
+ DYNAMIC_POWER_SAVINGS_VALIDATOR);
VALIDATORS.put(BLUETOOTH_ON, BLUETOOTH_ON_VALIDATOR);
VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR);
VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR);
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 72892fa07381..7de8020c4d32 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -332,6 +332,18 @@ message GlobalSettingsProto {
}
optional Dropbox dropbox = 46;
+ message DynamicPowerSavings {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ // When to auto disable interventions that were triggered due to
+ // {@link #DYNAMIC_POWER_SAVINGS_ENABLED}. Value is a percentage indicating
+ // a battery level.
+ optional SettingProto disable_threshold = 1 [ (android.privacy).dest = DEST_AUTOMATIC];
+ // Whether dynamic power savings based behaviors should be running or not.
+ optional SettingProto enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC];
+ }
+ optional DynamicPowerSavings dynamic_power_savings = 143;
+
message Emergency {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -491,6 +503,9 @@ message GlobalSettingsProto {
// The max value for {@link #LOW_POWER_MODE_TRIGGER_LEVEL}. If this setting
// is not set or the value is 0, the default max will be used.
optional SettingProto trigger_level_max = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // 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];
}
optional LowPowerMode low_power_mode = 70;
@@ -972,5 +987,5 @@ message GlobalSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 143;
+ // Next tag = 144;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6ae183b99942..26f3370f0dc5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3531,6 +3531,9 @@
<!-- Whether or not battery saver should be "sticky" when manually enabled. -->
<bool name="config_batterySaverStickyBehaviourDisabled">false</bool>
+ <!-- Config flag to track default disable threshold for Dynamic power savings enabled battery saver. -->
+ <integer name="config_dynamicPowerSavingsDefaultDisableThreshold">80</integer>
+
<!-- Model of potentially misprovisioned devices. If none is specified in an overlay, an
empty string is passed in. -->
<string name="config_misprovisionedDeviceModel" translatable="false"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6276884801a4..4eb723eb973d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3436,6 +3436,7 @@
<java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
<java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
+ <java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
<!-- For car devices -->
<java-symbol type="string" name="car_loading_profile" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 727f3998b03c..8c91c370fcf6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -124,6 +124,7 @@ public class SettingsBackupTest {
Settings.Global.AUTOFILL_LOGGING_LEVEL,
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
+ Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
@@ -235,6 +236,8 @@ public class SettingsBackupTest {
Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
Settings.Global.ENABLE_DISKSTATS_LOGGING,
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
+ Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
+ Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a1fbe0a8eaa1..c7945bd02c49 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -324,6 +324,7 @@ applications that come with the platform
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.MOVE_PACKAGE"/>
<permission name="android.permission.PACKAGE_USAGE_STATS" />
+ <permission name="android.permission.POWER_SAVER" />
<permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 448a96301990..cbb6e82c8f86 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -572,6 +572,15 @@ class SettingsProtoDumpUtil {
GlobalSettingsProto.Dropbox.SETTINGS);
p.end(dropboxToken);
+ final long dynamicPowerSavingsToken = p.start(GlobalSettingsProto.DYNAMIC_POWER_SAVINGS);
+ dumpSetting(s, p,
+ Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
+ GlobalSettingsProto.DynamicPowerSavings.DISABLE_THRESHOLD);
+ dumpSetting(s, p,
+ Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
+ GlobalSettingsProto.DynamicPowerSavings.ENABLED);
+ p.end(dynamicPowerSavingsToken);
+
final long emergencyToken = p.start(GlobalSettingsProto.EMERGENCY);
dumpSetting(s, p,
Settings.Global.EMERGENCY_TONE,
@@ -794,6 +803,9 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
GlobalSettingsProto.LowPowerMode.TRIGGER_LEVEL_MAX);
+ dumpSetting(s, p,
+ Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
+ GlobalSettingsProto.LowPowerMode.AUTOMATIC_POWER_SAVER_MODE);
p.end(lpmToken);
dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 822c39b6a71d..89690fb8dc5b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -83,6 +83,7 @@
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.POWER_SAVER" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<uses-permission android:name="android.permission.BACKUP" />
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 43a9c782b8d6..29d62372e154 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4439,6 +4439,42 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
+ public boolean setDynamicPowerSavings(boolean dynamicPowerSavingsEnabled,
+ int disableThreshold) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER,
+ "updateDynamicPowerSavings");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final ContentResolver resolver = mContext.getContentResolver();
+ boolean success = Settings.Global.putInt(resolver,
+ Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
+ disableThreshold);
+ if (success) {
+ // abort updating if we weren't able to succeed on the threshold
+ success &= Settings.Global.putInt(resolver,
+ Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
+ dynamicPowerSavingsEnabled ? 1 : 0);
+ }
+ return success;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public int getPowerSaveMode() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public boolean isDeviceIdleMode() {
final long ident = Binder.clearCallingIdentity();
try {
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 5569822300b9..6400c88b320f 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -93,8 +93,8 @@ public class BatterySaverController implements BatterySaverPolicyListener {
*/
private final Plugin[] mPlugins;
- public static final int REASON_AUTOMATIC_ON = 0;
- public static final int REASON_AUTOMATIC_OFF = 1;
+ public static final int REASON_PERCENTAGE_AUTOMATIC_ON = 0;
+ public static final int REASON_PERCENTAGE_AUTOMATIC_OFF = 1;
public static final int REASON_MANUAL_ON = 2;
public static final int REASON_MANUAL_OFF = 3;
public static final int REASON_STICKY_RESTORE = 4;
@@ -102,6 +102,8 @@ public class BatterySaverController implements BatterySaverPolicyListener {
public static final int REASON_POLICY_CHANGED = 6;
public static final int REASON_PLUGGED_IN = 7;
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;
/**
* 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 20ceed43d27d..f262f6dabbe0 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -19,6 +19,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
+import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -87,6 +88,11 @@ public class BatterySaverStateMachine {
/** Config flag to track if battery saver's sticky behaviour is disabled. */
private final boolean mBatterySaverStickyBehaviourDisabled;
+ /** 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.
* (Currently only used in dumpsys.)
@@ -94,6 +100,23 @@ public class BatterySaverStateMachine {
@GuardedBy("mLock")
private int mSettingBatterySaverTriggerThreshold;
+ /** Previously known value of 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.
+ */
+ @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.
+ */
+ @GuardedBy("mLock")
+ private boolean mDynamicPowerSavingsBatterySaver;
+
/**
* Whether BS has been manually disabled while the battery level is low, in which case we
* shouldn't auto re-enable it until the battery level is not low.
@@ -130,13 +153,15 @@ public class BatterySaverStateMachine {
mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled);
+ mDynamicPowerSavingsDefaultDisableThreshold = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold);
}
private boolean isBatterySaverEnabled() {
return mBatterySaverController.isEnabled();
}
- private boolean isAutoBatterySaverConfigured() {
+ private boolean isAutoBatterySaverConfiguredLocked() {
return mSettingBatterySaverTriggerThreshold > 0;
}
@@ -165,6 +190,15 @@ public class BatterySaverStateMachine {
cr.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Global.AUTOMATIC_POWER_SAVER_MODE),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Global.DYNAMIC_POWER_SAVINGS_ENABLED),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ cr.registerContentObserver(Settings.Global.getUriFor(
+ Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD),
+ false, mSettingsObserver, UserHandle.USER_SYSTEM);
synchronized (mLock) {
@@ -202,11 +236,20 @@ public class BatterySaverStateMachine {
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;
final int lowPowerModeTriggerLevel = getGlobalSetting(
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ final int automaticBatterySaver = getGlobalSetting(
+ Global.AUTOMATIC_POWER_SAVER_MODE,
+ PowerManager.POWER_SAVER_MODE_PERCENTAGE);
+ final int dynamicPowerSavingsDisableThreshold = getGlobalSetting(
+ Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
+ mDynamicPowerSavingsDefaultDisableThreshold);
setSettingsLocked(lowPowerModeEnabled, lowPowerModeEnabledSticky,
- lowPowerModeTriggerLevel);
+ lowPowerModeTriggerLevel, automaticBatterySaver, dynamicPowerSavingsBatterySaver,
+ dynamicPowerSavingsDisableThreshold);
}
/**
@@ -218,11 +261,16 @@ public class BatterySaverStateMachine {
@GuardedBy("mLock")
@VisibleForTesting
void setSettingsLocked(boolean batterySaverEnabled, boolean batterySaverEnabledSticky,
- int batterySaverTriggerThreshold) {
+ int batterySaverTriggerThreshold, int automaticBatterySaver,
+ boolean dynamicPowerSavingsBatterySaver, int dynamicPowerSavingsDisableThreshold) {
if (DEBUG) {
Slog.d(TAG, "setSettings: enabled=" + batterySaverEnabled
+ " sticky=" + batterySaverEnabledSticky
- + " threshold=" + batterySaverTriggerThreshold);
+ + " threshold=" + batterySaverTriggerThreshold
+ + " automaticBatterySaver=" + automaticBatterySaver
+ + " dynamicPowerSavingsBatterySaver=" + dynamicPowerSavingsBatterySaver
+ + " dynamicPowerSavingsDisableThreshold="
+ + dynamicPowerSavingsDisableThreshold);
}
mSettingsLoaded = true;
@@ -232,14 +280,23 @@ public class BatterySaverStateMachine {
mSettingBatterySaverEnabledSticky != batterySaverEnabledSticky;
final boolean thresholdChanged
= mSettingBatterySaverTriggerThreshold != batterySaverTriggerThreshold;
-
- if (!(enabledChanged || stickyChanged || thresholdChanged)) {
+ final boolean automaticModeChanged = mSettingAutomaticBatterySaver != automaticBatterySaver;
+ final boolean dynamicPowerSavingsThresholdChanged =
+ mDynamicPowerSavingsDisableThreshold != dynamicPowerSavingsDisableThreshold;
+ final boolean dynamicPowerSavingsBatterySaverChanged =
+ mDynamicPowerSavingsBatterySaver != dynamicPowerSavingsBatterySaver;
+
+ if (!(enabledChanged || stickyChanged || thresholdChanged || automaticModeChanged
+ || dynamicPowerSavingsThresholdChanged || dynamicPowerSavingsBatterySaverChanged)) {
return;
}
mSettingBatterySaverEnabled = batterySaverEnabled;
mSettingBatterySaverEnabledSticky = batterySaverEnabledSticky;
mSettingBatterySaverTriggerThreshold = batterySaverTriggerThreshold;
+ mSettingAutomaticBatterySaver = automaticBatterySaver;
+ mDynamicPowerSavingsDisableThreshold = dynamicPowerSavingsDisableThreshold;
+ mDynamicPowerSavingsBatterySaver = dynamicPowerSavingsBatterySaver;
if (thresholdChanged) {
// To avoid spamming the event log, we throttle logging here.
@@ -287,6 +344,17 @@ public class BatterySaverStateMachine {
}
}
+ @GuardedBy("mLock")
+ private boolean isBatteryLowLocked() {
+ final boolean percentageLow =
+ mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_PERCENTAGE
+ && mIsBatteryLevelLow;
+ final boolean dynamicPowerSavingsLow =
+ mSettingAutomaticBatterySaver == PowerManager.POWER_SAVER_MODE_DYNAMIC
+ && mBatteryLevel <= mDynamicPowerSavingsDisableThreshold;
+ return percentageLow || dynamicPowerSavingsLow;
+ }
+
/**
* Decide whether to auto-start / stop battery saver.
*/
@@ -299,12 +367,14 @@ public class BatterySaverStateMachine {
+ " mIsBatteryLevelLow=" + mIsBatteryLevelLow
+ " mBatterySaverSnoozing=" + mBatterySaverSnoozing
+ " mIsPowered=" + mIsPowered
+ + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver
+ " mSettingBatterySaverEnabledSticky=" + mSettingBatterySaverEnabledSticky);
}
if (!(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
return; // Not fully initialized yet.
}
- if (!mIsBatteryLevelLow) {
+
+ if (!isBatteryLowLocked()) {
updateSnoozingLocked(false, "Battery not low");
}
if (mIsPowered) {
@@ -319,20 +389,35 @@ public class BatterySaverStateMachine {
BatterySaverController.REASON_STICKY_RESTORE,
"Sticky restore");
- } else if (mIsBatteryLevelLow) {
- if (!mBatterySaverSnoozing && isAutoBatterySaverConfigured()) {
+ } else if (mSettingAutomaticBatterySaver
+ == PowerManager.POWER_SAVER_MODE_PERCENTAGE
+ && isAutoBatterySaverConfiguredLocked()) {
+ if (mIsBatteryLevelLow && !mBatterySaverSnoozing) {
enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false,
- BatterySaverController.REASON_AUTOMATIC_ON,
- "Auto ON");
+ BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_ON,
+ "Percentage Auto ON");
+ } else {
+ // Battery not low
+ enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+ BatterySaverController.REASON_PERCENTAGE_AUTOMATIC_OFF,
+ "Percentage Auto OFF");
+ }
+ } else if (mSettingAutomaticBatterySaver
+ == PowerManager.POWER_SAVER_MODE_DYNAMIC) {
+ if (mBatteryLevel >= mDynamicPowerSavingsDisableThreshold) {
+ enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
+ BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF,
+ "Dynamic Warning Auto OFF");
+ } else if (mDynamicPowerSavingsBatterySaver && !mBatterySaverSnoozing) {
+ enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ false,
+ BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON,
+ "Dynamic Warning Auto ON");
}
- } else { // Battery not low
- enableBatterySaverLocked(/*enable=*/ false, /*manual=*/ false,
- BatterySaverController.REASON_AUTOMATIC_OFF,
- "Auto OFF");
}
+ // do nothing if automatic battery saver mode = PERCENTAGE and low warning threshold = 0%
}
- /**
+ /**
* {@link com.android.server.power.PowerManagerService} calls it when
* {@link android.os.PowerManager#setPowerSaveMode} is called.
*
@@ -383,7 +468,7 @@ public class BatterySaverStateMachine {
// When battery saver is disabled manually (while battery saver is enabled)
// when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver.
// We resume auto-BS once the battery level is not low, or the device is plugged in.
- if (isBatterySaverEnabled() && mIsBatteryLevelLow) {
+ if (isBatterySaverEnabled() && isBatteryLowLocked()) {
updateSnoozingLocked(true, "Manual snooze");
}
}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index fd04970d50fe..f31ca55d422e 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -115,7 +115,12 @@ public class BatterySaverStateMachineTest {
mTarget.setSettingsLocked(
mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE, 0) != 0,
mPersistedState.global.getOrDefault(Global.LOW_POWER_MODE_STICKY, 0) != 0,
- mDevice.getLowPowerModeTriggerLevel());
+ mDevice.getLowPowerModeTriggerLevel(),
+ mPersistedState.global.getOrDefault(Global.AUTOMATIC_POWER_SAVER_MODE, 0),
+ mPersistedState.global.getOrDefault(
+ Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0) != 0,
+ mPersistedState.global.getOrDefault(
+ Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 100));
}
public void putGlobalSetting(String key, int value) {
@@ -174,6 +179,9 @@ public class BatterySaverStateMachineTest {
when(mMockResources.getBoolean(
com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
.thenReturn(false);
+ when(mMockResources.getInteger(
+ com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold))
+ .thenReturn(80);
mPersistedState = new DevicePersistedState();
initDevice();
@@ -303,6 +311,7 @@ public class BatterySaverStateMachineTest {
@Test
public void testAutoBatterySaver() {
mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
+ mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, 0);
assertEquals(false, mDevice.batterySaverEnabled);
assertEquals(100, mPersistedState.batteryLevel);
@@ -515,6 +524,7 @@ public class BatterySaverStateMachineTest {
.thenReturn(true);
initDevice();
mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
+ mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, 0);
mTarget.setBatterySaverEnabledManually(true);
@@ -626,4 +636,123 @@ public class BatterySaverStateMachineTest {
assertEquals(90, mPersistedState.batteryLevel);
assertEquals(false, mPersistedState.batteryLow);
}
+
+ @Test
+ public void testAutoBatterySaver_smartBatterySaverEnabled() {
+ mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 50);
+ mDevice.putGlobalSetting(Global.AUTOMATIC_POWER_SAVER_MODE, 1);
+ mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+
+ mDevice.setBatteryLevel(90);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+
+ mDevice.setBatteryLevel(51);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(51, mPersistedState.batteryLevel);
+
+ // Hit the threshold. BS should be disabled since dynamic power savings still off
+ mDevice.setBatteryLevel(50);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(50, mPersistedState.batteryLevel);
+
+ // dynamic power savings comes on, battery saver should turn on
+ mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_ENABLED, 1);
+ mDevice.setBatteryLevel(40);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+
+ mDevice.setPowered(true);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+
+ mTarget.setBatterySaverEnabledManually(false); // Manually disable -> snooze.
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+
+ // Plug in and out, snooze will reset.
+ mDevice.setPowered(true);
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(60);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(60, mPersistedState.batteryLevel);
+
+ mDevice.setPowered(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(60, mPersistedState.batteryLevel);
+
+ mDevice.setBatteryLevel(40);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(40, mPersistedState.batteryLevel);
+
+ mDevice.setBatteryLevel(70);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(70, mPersistedState.batteryLevel);
+
+ // Bump up the threshold.
+ mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 71);
+ mDevice.setBatteryLevel(mPersistedState.batteryLevel);
+
+ // changes are only registered if some battery level changed
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(70, mPersistedState.batteryLevel);
+
+ mDevice.setBatteryLevel(69);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(69, mPersistedState.batteryLevel);
+
+ // Then down.
+ mDevice.putGlobalSetting(Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 60);
+ mDevice.setBatteryLevel(mPersistedState.batteryLevel);
+
+ // changes are only registered if battery level changed
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(69, mPersistedState.batteryLevel);
+
+ mDevice.setBatteryLevel(68);
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(68, mPersistedState.batteryLevel);
+
+ // Reboot in low state -> automatically enable BS.
+ mDevice.setPowered(false);
+ mDevice.setBatteryLevel(30);
+ mTarget.setBatterySaverEnabledManually(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+
+ initDevice();
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ }
}