diff options
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); + } } |