add full battery saver runtime modification API

New system API added for privledged clients to modify the full battery
saver mode policy. This API is used to override static settings at
runtime, and any overridden settings are cleared when exiting full
battery saver mode.

Bug: 172294448
Test: atest PowerManagerTest
Test: atest PowerManagerServiceTest
Test: atest BatterySaverPolicyTest
Test: build and boot ensuring SoundTrigger service behavior is not
changed

Change-Id: I41f968799b184a5dac702553379294795614be0a
This commit is contained in:
Nicholas Ambur
2020-12-04 08:34:48 -08:00
parent 8b317b15a3
commit 4838c0051c
10 changed files with 330 additions and 11 deletions

View File

@@ -7854,6 +7854,7 @@ package android.os {
public static final class BatterySaverPolicyConfig.Builder {
ctor public BatterySaverPolicyConfig.Builder();
ctor public BatterySaverPolicyConfig.Builder(@NonNull android.os.BatterySaverPolicyConfig);
method @NonNull public android.os.BatterySaverPolicyConfig.Builder addDeviceSpecificSetting(@NonNull String, @NonNull String);
method @NonNull public android.os.BatterySaverPolicyConfig build();
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setAdjustBrightnessFactor(float);
@@ -8221,6 +8222,7 @@ package android.os {
public final class PowerManager {
method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
method @NonNull public android.os.BatterySaverPolicyConfig getFullPowerSavePolicy();
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed();
@@ -8229,6 +8231,7 @@ package android.os {
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setFullPowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int);

View File

@@ -340,6 +340,38 @@ public final class BatterySaverPolicyConfig implements Parcelable {
public Builder() {
}
/**
* Creates a Builder prepopulated with the values from the passed in
* {@link BatterySaverPolicyConfig}.
*/
public Builder(@NonNull BatterySaverPolicyConfig batterySaverPolicyConfig) {
mAdjustBrightnessFactor = batterySaverPolicyConfig.getAdjustBrightnessFactor();
mAdvertiseIsEnabled = batterySaverPolicyConfig.getAdvertiseIsEnabled();
mDeferFullBackup = batterySaverPolicyConfig.getDeferFullBackup();
mDeferKeyValueBackup = batterySaverPolicyConfig.getDeferKeyValueBackup();
for (String key :
batterySaverPolicyConfig.getDeviceSpecificSettings().keySet()) {
mDeviceSpecificSettings.put(key,
batterySaverPolicyConfig.getDeviceSpecificSettings().get(key));
}
mDisableAnimation = batterySaverPolicyConfig.getDisableAnimation();
mDisableAod = batterySaverPolicyConfig.getDisableAod();
mDisableLaunchBoost = batterySaverPolicyConfig.getDisableLaunchBoost();
mDisableOptionalSensors = batterySaverPolicyConfig.getDisableOptionalSensors();
mDisableVibration = batterySaverPolicyConfig.getDisableVibration();
mEnableAdjustBrightness = batterySaverPolicyConfig.getEnableAdjustBrightness();
mEnableDataSaver = batterySaverPolicyConfig.getEnableDataSaver();
mEnableFirewall = batterySaverPolicyConfig.getEnableFirewall();
mEnableNightMode = batterySaverPolicyConfig.getEnableNightMode();
mEnableQuickDoze = batterySaverPolicyConfig.getEnableQuickDoze();
mForceAllAppsStandby = batterySaverPolicyConfig.getForceAllAppsStandby();
mForceBackgroundCheck = batterySaverPolicyConfig.getForceBackgroundCheck();
mLocationMode = batterySaverPolicyConfig.getLocationMode();
mSoundTriggerMode = batterySaverPolicyConfig.getSoundTriggerMode();
}
/**
* Set how much to adjust the screen brightness while in Battery Saver. The value should
* be in the [0, 1] range, where 1 will not change the brightness. This will have no

View File

@@ -55,6 +55,8 @@ interface IPowerManager
boolean isPowerSaveMode();
PowerSaveState getPowerSaveState(int serviceType);
boolean setPowerSaveModeEnabled(boolean mode);
BatterySaverPolicyConfig getFullPowerSavePolicy();
boolean setFullPowerSavePolicy(in BatterySaverPolicyConfig config);
boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold);
boolean setAdaptivePowerSavePolicy(in BatterySaverPolicyConfig config);
boolean setAdaptivePowerSaveEnabled(boolean enabled);

View File

@@ -1637,6 +1637,65 @@ public final class PowerManager {
}
}
/**
* Gets the current policy for full power save mode.
*
* @return The {@link BatterySaverPolicyConfig} which is currently set for the full power save
* policy level.
*
* @hide
*/
@SystemApi
@NonNull
public BatterySaverPolicyConfig getFullPowerSavePolicy() {
try {
return mService.getFullPowerSavePolicy();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets the policy for full power save mode.
*
* Any settings set by this API will persist for only one session of full battery saver mode.
* The settings set by this API are cleared upon exit of full battery saver mode, and the
* caller is expected to set the desired values again for the next full battery saver mode
* session if desired.
*
* Use-cases:
* 1. Set policy outside of full battery saver mode
* - full policy set -> enter BS -> policy setting applied -> exit BS -> setting cleared
* 2. Set policy inside of full battery saver mode
* - enter BS -> full policy set -> policy setting applied -> exit BS -> setting cleared
*
* This API is intended to be used with {@link #getFullPowerSavePolicy()} API when a client only
* wants to modify a specific setting(s) and leave the remaining policy attributes the same.
* Example:
* BatterySaverPolicyConfig newFullPolicyConfig =
* new BatterySaverPolicyConfig.Builder(powerManager.getFullPowerSavePolicy())
* .setSoundTriggerMode(PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED)
* .build();
* powerManager.setFullPowerSavePolicy(newFullPolicyConfig);
*
* @return true if there was an effectual change. If full battery saver is enabled, then this
* will return true.
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.DEVICE_POWER,
android.Manifest.permission.POWER_SAVER
})
public boolean setFullPowerSavePolicy(@NonNull BatterySaverPolicyConfig config) {
try {
return mService.setFullPowerSavePolicy(config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* 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

View File

@@ -5031,6 +5031,31 @@ public final class PowerManagerService extends SystemService
}
}
@Override // Binder call
public BatterySaverPolicyConfig getFullPowerSavePolicy() {
final long ident = Binder.clearCallingIdentity();
try {
return mBatterySaverStateMachine.getFullBatterySaverPolicy();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override // Binder call
public boolean setFullPowerSavePolicy(@NonNull BatterySaverPolicyConfig config) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER)
!= PackageManager.PERMISSION_GRANTED) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, "setFullPowerSavePolicy");
}
final long ident = Binder.clearCallingIdentity();
try {
return mBatterySaverStateMachine.setFullBatterySaverPolicy(config);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override // Binder call
public boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER,

View File

@@ -46,6 +46,7 @@ import com.android.server.LocalServices;
import com.android.server.power.PowerManagerService;
import com.android.server.power.batterysaver.BatterySaverPolicy.BatterySaverPolicyListener;
import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
import com.android.server.power.batterysaver.BatterySaverPolicy.PolicyLevel;
import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
@@ -133,6 +134,7 @@ public class BatterySaverController implements BatterySaverPolicyListener {
public static final int REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_OFF = 10;
public static final int REASON_ADAPTIVE_DYNAMIC_POWER_SAVINGS_CHANGED = 11;
public static final int REASON_TIMEOUT = 12;
public static final int REASON_FULL_POWER_SAVINGS_CHANGED = 13;
static String reasonToString(int reason) {
switch (reason) {
@@ -162,6 +164,8 @@ public class BatterySaverController implements BatterySaverPolicyListener {
return "Adaptive Power Savings changed";
case BatterySaverController.REASON_TIMEOUT:
return "timeout";
case BatterySaverController.REASON_FULL_POWER_SAVINGS_CHANGED:
return "Full Power Savings changed";
default:
return "Unknown reason: " + reason;
}
@@ -313,6 +317,10 @@ public class BatterySaverController implements BatterySaverPolicyListener {
}
}
BatterySaverPolicyConfig getPolicyLocked(@PolicyLevel int policyLevel) {
return mBatterySaverPolicy.getPolicyLocked(policyLevel).toConfig();
}
/**
* @return whether battery saver is enabled or not. This takes into
* account whether a policy says to advertise isEnabled so this can be propagated externally.
@@ -340,6 +348,18 @@ public class BatterySaverController implements BatterySaverPolicyListener {
}
}
boolean setFullPolicyLocked(BatterySaverPolicyConfig config, int reason) {
return setFullPolicyLocked(BatterySaverPolicy.Policy.fromConfig(config), reason);
}
boolean setFullPolicyLocked(Policy policy, int reason) {
if (mBatterySaverPolicy.setFullPolicyLocked(policy)) {
mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
return true;
}
return false;
}
boolean isAdaptiveEnabled() {
synchronized (mLock) {
return getAdaptiveEnabledLocked();

View File

@@ -222,6 +222,10 @@ public class BatterySaverPolicy extends ContentObserver implements
@GuardedBy("mLock")
private Policy mAdaptivePolicy = DEFAULT_ADAPTIVE_POLICY;
/** The current default full policy. */
@GuardedBy("mLock")
private Policy mDefaultFullPolicy = DEFAULT_FULL_POLICY;
/** The policy to be used for full battery saver. */
@GuardedBy("mLock")
private Policy mFullPolicy = DEFAULT_FULL_POLICY;
@@ -377,9 +381,8 @@ public class BatterySaverPolicy extends ContentObserver implements
}
}
if (newFullPolicy != null && !mFullPolicy.equals(newFullPolicy)) {
mFullPolicy = newFullPolicy;
changed |= (mPolicyLevel == POLICY_LEVEL_FULL);
if (newFullPolicy != null) {
changed |= maybeUpdateDefaultFullPolicy(newFullPolicy);
}
if (newAdaptivePolicy != null && !mAdaptivePolicy.equals(newAdaptivePolicy)) {
@@ -447,13 +450,9 @@ public class BatterySaverPolicy extends ContentObserver implements
Slog.i(TAG, "mDeviceSpecificSettings=" + mDeviceSpecificSettings);
}
boolean changed = false;
Policy newFullPolicy = Policy.fromSettings(setting, deviceSpecificSetting,
mLastDeviceConfigProperties, null, DEFAULT_FULL_POLICY);
if (mPolicyLevel == POLICY_LEVEL_FULL && !mFullPolicy.equals(newFullPolicy)) {
changed = true;
}
mFullPolicy = newFullPolicy;
boolean changed = maybeUpdateDefaultFullPolicy(
Policy.fromSettings(setting, deviceSpecificSetting,
mLastDeviceConfigProperties, null, DEFAULT_FULL_POLICY));
mDefaultAdaptivePolicy = Policy.fromSettings("", "",
mLastDeviceConfigProperties, KEY_SUFFIX_ADAPTIVE, DEFAULT_ADAPTIVE_POLICY);
@@ -802,6 +801,33 @@ public class BatterySaverPolicy extends ContentObserver implements
);
}
BatterySaverPolicyConfig toConfig() {
return new BatterySaverPolicyConfig.Builder()
.addDeviceSpecificSetting(KEY_CPU_FREQ_INTERACTIVE,
cpuFrequenciesForInteractive.toString())
.addDeviceSpecificSetting(KEY_CPU_FREQ_NONINTERACTIVE,
cpuFrequenciesForNoninteractive.toString())
.setAdjustBrightnessFactor(adjustBrightnessFactor)
.setAdvertiseIsEnabled(advertiseIsEnabled)
.setDeferFullBackup(deferFullBackup)
.setDeferKeyValueBackup(deferKeyValueBackup)
.setDisableAnimation(disableAnimation)
.setDisableAod(disableAod)
.setDisableLaunchBoost(disableLaunchBoost)
.setDisableOptionalSensors(disableOptionalSensors)
.setDisableVibration(disableVibration)
.setEnableAdjustBrightness(enableAdjustBrightness)
.setEnableDataSaver(enableDataSaver)
.setEnableFirewall(enableFirewall)
.setEnableNightMode(enableNightMode)
.setEnableQuickDoze(enableQuickDoze)
.setForceAllAppsStandby(forceAllAppsStandby)
.setForceBackgroundCheck(forceBackgroundCheck)
.setLocationMode(locationMode)
.setSoundTriggerMode(soundTriggerMode)
.build();
}
@VisibleForTesting
static Policy fromSettings(String settings, String deviceSpecificSettings,
DeviceConfig.Properties properties, String configSuffix) {
@@ -1032,6 +1058,11 @@ public class BatterySaverPolicy extends ContentObserver implements
if (mPolicyLevel == level) {
return false;
}
// If we are leaving the full policy level, then any overrides to the full policy set
// through #setFullPolicyLocked should be cleared.
if (mPolicyLevel == POLICY_LEVEL_FULL) {
mFullPolicy = mDefaultFullPolicy;
}
switch (level) {
case POLICY_LEVEL_FULL:
case POLICY_LEVEL_ADAPTIVE:
@@ -1047,6 +1078,62 @@ public class BatterySaverPolicy extends ContentObserver implements
}
}
/**
* Get the current policy for the provided policy level.
*/
Policy getPolicyLocked(@PolicyLevel int policyLevel) {
switch (policyLevel) {
case POLICY_LEVEL_OFF:
return OFF_POLICY;
case POLICY_LEVEL_ADAPTIVE:
return mAdaptivePolicy;
case POLICY_LEVEL_FULL:
return mFullPolicy;
}
throw new IllegalArgumentException(
"getPolicyLocked: incorrect policy level provided - " + policyLevel);
}
/**
* Updates the default policy with the passed in policy.
* If the full policy is not overridden with runtime settings, then the full policy will be
* updated.
*
* @return True if the active policy requires an update, false if not.
*/
private boolean maybeUpdateDefaultFullPolicy(Policy p) {
boolean fullPolicyChanged = false;
if (!mDefaultFullPolicy.equals(p)) {
// default policy can be overridden by #setFullPolicyLocked
boolean isDefaultFullPolicyOverridden = !mDefaultFullPolicy.equals(mFullPolicy);
if (!isDefaultFullPolicyOverridden) {
mFullPolicy = p;
fullPolicyChanged = (mPolicyLevel == POLICY_LEVEL_FULL);
}
mDefaultFullPolicy = p;
}
return fullPolicyChanged;
}
/** @return true if the current policy changed and the policy level is FULL. */
boolean setFullPolicyLocked(Policy p) {
if (p == null) {
Slog.wtf(TAG, "setFullPolicy given null policy");
return false;
}
if (mFullPolicy.equals(p)) {
return false;
}
mFullPolicy = p;
if (mPolicyLevel == POLICY_LEVEL_FULL) {
updatePolicyDependenciesLocked();
return true;
}
return false;
}
/** @return true if the current policy changed and the policy level is ADAPTIVE. */
boolean setAdaptivePolicyLocked(Policy p) {
if (p == null) {
@@ -1154,7 +1241,8 @@ public class BatterySaverPolicy extends ContentObserver implements
ipw.println("mAutomotiveProjectionActive=" + mAutomotiveProjectionActive.get());
ipw.println("mPolicyLevel=" + mPolicyLevel);
dumpPolicyLocked(ipw, "full", mFullPolicy);
dumpPolicyLocked(ipw, "default full", mDefaultFullPolicy);
dumpPolicyLocked(ipw, "current full", mFullPolicy);
dumpPolicyLocked(ipw, "default adaptive", mDefaultAdaptivePolicy);
dumpPolicyLocked(ipw, "current adaptive", mAdaptivePolicy);
dumpPolicyLocked(ipw, "effective", mEffectivePolicyRaw);

View File

@@ -501,6 +501,33 @@ public class BatterySaverStateMachine {
}
}
/**
* Change the full battery saver policy.
*/
public BatterySaverPolicyConfig getFullBatterySaverPolicy() {
if (DEBUG) {
Slog.d(TAG, "getFullBatterySaverPolicy");
}
synchronized (mLock) {
return mBatterySaverController.getPolicyLocked(BatterySaverPolicy.POLICY_LEVEL_FULL);
}
}
/**
* Change the full battery saver policy.
*/
public boolean setFullBatterySaverPolicy(BatterySaverPolicyConfig config) {
if (DEBUG) {
Slog.d(TAG, "setFullBatterySaverPolicy: config=" + config);
}
synchronized (mLock) {
return mBatterySaverController.setFullPolicyLocked(config,
BatterySaverController.REASON_FULL_POWER_SAVINGS_CHANGED);
}
}
/**
* Enable or disable the current adaptive battery saver policy. This may not change what's in
* effect if full battery saver is also enabled.

View File

@@ -58,6 +58,7 @@ import android.hardware.power.Boost;
import android.hardware.power.Mode;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.BatterySaverPolicyConfig;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -1143,4 +1144,33 @@ public class PowerManagerServiceTest {
assertFalse(
mService.getBinderServiceInstance().setPowerModeChecked(Mode.INTERACTIVE, false));
}
@Test
public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
createService();
mService.systemReady(null);
BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
when(mBatterySaverStateMachineMock.getFullBatterySaverPolicy())
.thenReturn(mockReturnConfig);
mService.getBinderServiceInstance().setPowerSaveModeEnabled(true);
BatterySaverPolicyConfig policyConfig =
mService.getBinderServiceInstance().getFullPowerSavePolicy();
assertThat(mockReturnConfig).isEqualTo(policyConfig);
verify(mBatterySaverStateMachineMock).getFullBatterySaverPolicy();
}
@Test
public void testSetFullPowerSavePolicy_callsStateMachine() {
createService();
mService.systemReady(null);
BatterySaverPolicyConfig mockSetPolicyConfig =
new BatterySaverPolicyConfig.Builder().build();
when(mBatterySaverStateMachineMock.setFullBatterySaverPolicy(any())).thenReturn(true);
mService.getBinderServiceInstance().setPowerSaveModeEnabled(true);
assertThat(mService.getBinderServiceInstance()
.setFullPowerSavePolicy(mockSetPolicyConfig)).isTrue();
verify(mBatterySaverStateMachineMock).setFullBatterySaverPolicy(eq(mockSetPolicyConfig));
}
}

View File

@@ -22,6 +22,7 @@ import static com.android.server.power.batterysaver.BatterySaverPolicy.POLICY_LE
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.BatterySaverPolicyConfig;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
@@ -595,4 +596,36 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
assertEquals(PowerManager.LOCATION_MODE_FOREGROUND_ONLY, policy.locationMode);
assertEquals(PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY, policy.soundTriggerMode);
}
public void testSetFullPolicy_overridesSettingsAndDeviceConfig_clearOnFullExit() {
mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_1;
mMockGlobalSettings.put(Global.BATTERY_SAVER_CONSTANTS,
"location_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, "");
mBatterySaverPolicy.onChange();
assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
.isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
Policy currentFullPolicy = mBatterySaverPolicy.getPolicyLocked(POLICY_LEVEL_FULL);
BatterySaverPolicyConfig currentFullPolicyConfig = currentFullPolicy.toConfig();
BatterySaverPolicyConfig newFullPolicyConfig =
new BatterySaverPolicyConfig.Builder(currentFullPolicyConfig)
.setLocationMode(PowerManager.LOCATION_MODE_FOREGROUND_ONLY)
.build();
mBatterySaverPolicy.setFullPolicyLocked(Policy.fromConfig(newFullPolicyConfig));
assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
.isEqualTo(PowerManager.LOCATION_MODE_FOREGROUND_ONLY);
// Any policy settings set through #setFullPolicy will be cleared when exiting full policy.
// Default policy settings will be used on the next full policy mode enter unless
// #setFullPolicy is called again.
mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_OFF);
assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
.isEqualTo(PowerManager.LOCATION_MODE_NO_CHANGE);
mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
.isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
}
}