* @hide
*/
public class PowerSaveState implements Parcelable {
+ /**
+ * Whether we should enable battery saver for this service.
+ *
+ * @see com.android.server.power.BatterySaverPolicy.ServiceType
+ */
public final boolean batterySaverEnabled;
+ /**
+ * Whether the battery saver is enabled globally, which means the data we get from
+ * {@link PowerManager#isPowerSaveMode()}
+ */
+ public final boolean globalBatterySaverEnabled;
public final int gpsMode;
public final float brightnessFactor;
batterySaverEnabled = builder.mBatterySaverEnabled;
gpsMode = builder.mGpsMode;
brightnessFactor = builder.mBrightnessFactor;
+ globalBatterySaverEnabled = builder.mGlobalBatterySaverEnabled;
}
public PowerSaveState(Parcel in) {
batterySaverEnabled = in.readByte() != 0;
+ globalBatterySaverEnabled = in.readByte() != 0;
gpsMode = in.readInt();
brightnessFactor = in.readFloat();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (batterySaverEnabled ? 1 : 0));
+ dest.writeByte((byte) (globalBatterySaverEnabled ? 1 : 0));
dest.writeInt(gpsMode);
dest.writeFloat(brightnessFactor);
}
public static final class Builder {
private boolean mBatterySaverEnabled = false;
+ private boolean mGlobalBatterySaverEnabled = false;
private int mGpsMode = 0;
private float mBrightnessFactor = 0.5f;
return this;
}
+ public Builder setGlobalBatterySaverEnabled(boolean enabled) {
+ mGlobalBatterySaverEnabled = enabled;
+ return this;
+ }
+
public Builder setGpsMode(int mode) {
mGpsMode = mode;
return this;
private INotificationManager mNotifManager;
private PowerManagerInternal mPowerManagerInternal;
private IDeviceIdleController mDeviceIdleController;
+ @GuardedBy("mUidRulesFirstLock")
+ private PowerSaveState mRestrictBackgroundPowerState;
+
+ // Store the status of restrict background before turning on battery saver.
+ // Used to restore mRestrictBackground when battery saver is turned off.
+ private boolean mRestrictBackgroundBeforeBsm;
// See main javadoc for instructions on how to use these locks.
final Object mUidRulesFirstLock = new Object();
@GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
@GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
@GuardedBy("mUidRulesFirstLock") volatile boolean mDeviceIdleMode;
+ // Store whether user flipped restrict background in battery saver mode
+ @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackgroundChangedInBsm;
private final boolean mSuppressDefaultPolicy;
@Override
public void onLowPowerModeChanged(PowerSaveState result) {
final boolean enabled = result.batterySaverEnabled;
- if (LOGD) Slog.d(TAG,
- "onLowPowerModeChanged(" + enabled + ")");
+ if (LOGD) {
+ Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")");
+ }
synchronized (mUidRulesFirstLock) {
if (mRestrictPower != enabled) {
mRestrictPower = enabled;
}
}
}
- });
+ });
mRestrictPower = mPowerManagerInternal.getLowPowerState(
ServiceType.NETWORK_FIREWALL).batterySaverEnabled;
// read policy from disk
readPolicyAL();
+ // Update the restrictBackground if battery saver is turned on
+ mRestrictBackgroundBeforeBsm = mRestrictBackground;
+ mRestrictBackgroundPowerState = mPowerManagerInternal
+ .getLowPowerState(ServiceType.DATA_SAVER);
+ final boolean localRestrictBackground =
+ mRestrictBackgroundPowerState.batterySaverEnabled;
+ if (localRestrictBackground && localRestrictBackground != mRestrictBackground) {
+ mRestrictBackground = localRestrictBackground;
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED,
+ mRestrictBackground ? 1 : 0, 0).sendToTarget();
+ }
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public int getServiceType() {
+ return ServiceType.DATA_SAVER;
+ }
+
+ @Override
+ public void onLowPowerModeChanged(PowerSaveState result) {
+ synchronized (mUidRulesFirstLock) {
+ updateRestrictBackgroundByLowPowerModeUL(result);
+ }
+ }
+ });
+
if (addDefaultRestrictBackgroundWhitelistUidsUL()) {
writePolicyAL();
}
} catch (RemoteException e) {
// ignored; service lives in system_server
}
+
+ if (mRestrictBackgroundPowerState.globalBatterySaverEnabled) {
+ mRestrictBackgroundChangedInBsm = true;
+ }
synchronized (mNetworkPoliciesSecondLock) {
updateNotificationsNL();
writePolicyAL();
mHandler.getLooper().getQueue().addIdleHandler(handler);
}
+ @VisibleForTesting
+ public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
+ mRestrictBackgroundPowerState = result;
+
+ boolean restrictBackground = result.batterySaverEnabled;
+ boolean shouldInvokeRestrictBackground;
+ // store the temporary mRestrictBackgroundChangedInBsm and update it at last
+ boolean localRestrictBgChangedInBsm = mRestrictBackgroundChangedInBsm;
+
+ if (result.globalBatterySaverEnabled) {
+ // Try to turn on restrictBackground if (1) it is off and (2) batter saver need to
+ // turn it on.
+ shouldInvokeRestrictBackground = !mRestrictBackground && result.batterySaverEnabled;
+ mRestrictBackgroundBeforeBsm = mRestrictBackground;
+ localRestrictBgChangedInBsm = false;
+ } else {
+ // Try to restore the restrictBackground if it doesn't change in bsm
+ shouldInvokeRestrictBackground = !mRestrictBackgroundChangedInBsm;
+ restrictBackground = mRestrictBackgroundBeforeBsm;
+ }
+
+ if (shouldInvokeRestrictBackground) {
+ setRestrictBackground(restrictBackground);
+ }
+
+ // Change it at last so setRestrictBackground() won't affect this variable
+ mRestrictBackgroundChangedInBsm = localRestrictBgChangedInBsm;
+ }
+
private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
final int size = source.size();
for (int i = 0; i < size; i++) {
ServiceType.NETWORK_FIREWALL,
ServiceType.SCREEN_BRIGHTNESS,
ServiceType.SOUND,
- ServiceType.BATTERY_STATS})
+ ServiceType.BATTERY_STATS,
+ ServiceType.DATA_SAVER})
public @interface ServiceType {
int NULL = 0;
int GPS = 1;
int SCREEN_BRIGHTNESS = 7;
int SOUND = 8;
int BATTERY_STATS = 9;
+ int DATA_SAVER = 10;
}
private static final String TAG = "BatterySaverPolicy";
private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled";
private static final String KEY_FIREWALL_DISABLED = "firewall_disabled";
private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled";
+ private static final String KEY_DATASAVER_DISABLED = "datasaver_disabled";
private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
private boolean mAdjustBrightnessDisabled;
/**
+ * {@code true} if data saver is disabled in battery saver mode.
+ *
+ * @see Settings.Global#BATTERY_SAVER_CONSTANTS
+ * @see #KEY_DATASAVER_DISABLED
+ */
+ private boolean mDataSaverDisabled;
+
+ /**
* This is the flag to decide the gps mode in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
mFireWallDisabled = mParser.getBoolean(KEY_FIREWALL_DISABLED, false);
mAdjustBrightnessDisabled = mParser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
mAdjustBrightnessFactor = mParser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
+ mDataSaverDisabled = mParser.getBoolean(KEY_DATASAVER_DISABLED, true);
// Get default value from Settings.Secure
final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
*/
public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) {
synchronized (BatterySaverPolicy.this) {
- final PowerSaveState.Builder builder = new PowerSaveState.Builder();
+ final PowerSaveState.Builder builder = new PowerSaveState.Builder()
+ .setGlobalBatterySaverEnabled(realMode);
if (!realMode) {
return builder.setBatterySaverEnabled(realMode)
.build();
return builder.setBatterySaverEnabled(!mAdjustBrightnessDisabled)
.setBrightnessFactor(mAdjustBrightnessFactor)
.build();
+ case ServiceType.DATA_SAVER:
+ return builder.setBatterySaverEnabled(!mDataSaverDisabled)
+ .build();
case ServiceType.SOUND:
return builder.setBatterySaverEnabled(mSoundTriggerDisabled)
.build();
pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + mFullBackupDeferred);
pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + mKeyValueBackupDeferred);
pw.println(" " + KEY_FIREWALL_DISABLED + "=" + mFireWallDisabled);
+ pw.println(" " + KEY_DATASAVER_DISABLED + "=" + mDataSaverDisabled);
pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
import android.os.UserHandle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
private IUidObserver mUidObserver;
private INetworkManagementEventObserver mNetworkObserver;
+ private PowerManagerInternal mPowerManagerInternal;
private NetworkPolicyListenerAnswer mPolicyListener;
private NetworkPolicyManagerService mService;
@BeforeClass
public static void registerLocalServices() {
- addLocalServiceMock(PowerManagerInternal.class);
addLocalServiceMock(DeviceIdleController.LocalService.class);
final UsageStatsManagerInternal usageStats =
addLocalServiceMock(UsageStatsManagerInternal.class);
when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{});
mActivityManagerInternal = addLocalServiceMock(ActivityManagerInternal.class);
+
+ final PowerSaveState state = new PowerSaveState.Builder()
+ .setBatterySaverEnabled(false).build();
+ final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
+ when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
}
@Before
removeRestrictBackgroundWhitelist(false);
}
+ @Test
+ public void testLowPowerModeObserver_ListenersRegistered()
+ throws Exception {
+ PowerManagerInternal pmInternal = LocalServices.getService(PowerManagerInternal.class);
+
+ verify(pmInternal, atLeast(2)).registerLowPowerModeObserver(any());
+ }
+
+ @Test
+ public void updateRestrictBackgroundByLowPowerMode_RestrictOnBeforeBsm_RestrictOnAfterBsm()
+ throws Exception {
+ setRestrictBackground(true);
+ PowerSaveState stateOn = new PowerSaveState.Builder()
+ .setGlobalBatterySaverEnabled(true)
+ .setBatterySaverEnabled(false)
+ .build();
+ mService.updateRestrictBackgroundByLowPowerModeUL(stateOn);
+
+ // RestrictBackground should be on even though battery saver want to turn it off
+ assertThat(mService.getRestrictBackground()).isTrue();
+
+ PowerSaveState stateOff = new PowerSaveState.Builder()
+ .setGlobalBatterySaverEnabled(false)
+ .setBatterySaverEnabled(false)
+ .build();
+ mService.updateRestrictBackgroundByLowPowerModeUL(stateOff);
+
+ // RestrictBackground should be on, following its previous state
+ assertThat(mService.getRestrictBackground()).isTrue();
+ }
+
+ @Test
+ public void updateRestrictBackgroundByLowPowerMode_RestrictOffBeforeBsm_RestrictOffAfterBsm()
+ throws Exception {
+ setRestrictBackground(false);
+ PowerSaveState stateOn = new PowerSaveState.Builder()
+ .setGlobalBatterySaverEnabled(true)
+ .setBatterySaverEnabled(true)
+ .build();
+
+ doReturn(true).when(mNetworkManager).setDataSaverModeEnabled(true);
+ mService.updateRestrictBackgroundByLowPowerModeUL(stateOn);
+
+ // RestrictBackground should be turned on because of battery saver
+ assertThat(mService.getRestrictBackground()).isTrue();
+
+ PowerSaveState stateOff = new PowerSaveState.Builder()
+ .setGlobalBatterySaverEnabled(false)
+ .setBatterySaverEnabled(false)
+ .build();
+ mService.updateRestrictBackgroundByLowPowerModeUL(stateOff);
+
+ // RestrictBackground should be off, following its previous state
+ assertThat(mService.getRestrictBackground()).isFalse();
+ }
+
+ @Test
+ public void updateRestrictBackgroundByLowPowerMode_StatusChangedInBsm_DoNotRestore()
+ throws Exception {
+ setRestrictBackground(true);
+ PowerSaveState stateOn = new PowerSaveState.Builder()
+ .setGlobalBatterySaverEnabled(true)
+ .setBatterySaverEnabled(true)
+ .build();
+ mService.updateRestrictBackgroundByLowPowerModeUL(stateOn);
+
+ // RestrictBackground should still be on
+ assertThat(mService.getRestrictBackground()).isTrue();
+
+ // User turns off RestrictBackground manually
+ setRestrictBackground(false);
+ PowerSaveState stateOff = new PowerSaveState.Builder().setBatterySaverEnabled(
+ false).build();
+ mService.updateRestrictBackgroundByLowPowerModeUL(stateOff);
+
+ // RestrictBackground should be off because user changes it manually
+ assertThat(mService.getRestrictBackground()).isFalse();
+ }
+
private void removeRestrictBackgroundWhitelist(boolean expectIntent) throws Exception {
// Sanity checks.
assertWhitelistUids(UID_A);
private void setRestrictBackground(boolean flag) throws Exception {
// Must set expectation, otherwise NMPS will reset value to previous one.
- when(mNetworkManager.setDataSaverModeEnabled(flag)).thenReturn(true);
+ doReturn(true).when(mNetworkManager).setDataSaverModeEnabled(flag);
mService.setRestrictBackground(flag);
// Sanity check.
assertEquals("restrictBackground not set", flag, mService.getRestrictBackground());
import android.os.Handler;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+
import com.android.server.power.BatterySaverPolicy.ServiceType;
+
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+ "animation_disabled=false,"
+ "soundtrigger_disabled=true,"
+ "firewall_disabled=false,"
+ + "datasaver_disabled=false,"
+ "adjust_brightness_disabled=true,"
+ "adjust_brightness_factor=0.7,"
+ "fullbackup_deferred=true,"
}
@SmallTest
+ public void testGetBatterySaverPolicy_PolicyDataSaver_DefaultValueCorrect() {
+ mBatterySaverPolicy.updateConstants("");
+ final PowerSaveState batterySaverStateOn =
+ mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.DATA_SAVER, BATTERY_SAVER_ON);
+ assertThat(batterySaverStateOn.batterySaverEnabled).isFalse();
+
+ final PowerSaveState batterySaverStateOff = mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.DATA_SAVER, BATTERY_SAVER_OFF);
+ assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
+ }
+
+ @SmallTest
public void testGetBatterySaverPolicy_PolicyScreenBrightness_DefaultValueCorrect() {
testServiceDefaultValue(ServiceType.SCREEN_BRIGHTNESS);
assertThat(networkState.batterySaverEnabled).isTrue();
final PowerSaveState screenState =
- mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS, BATTERY_SAVER_ON);
+ mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS,
+ BATTERY_SAVER_ON);
assertThat(screenState.batterySaverEnabled).isFalse();
assertThat(screenState.brightnessFactor).isWithin(PRECISION).of(BRIGHTNESS_FACTOR);
ServiceType.KEYVALUE_BACKUP, BATTERY_SAVER_ON);
assertThat(keyValueBackupState.batterySaverEnabled).isFalse();
+ final PowerSaveState dataSaverState = mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.DATA_SAVER, BATTERY_SAVER_ON);
+ assertThat(dataSaverState.batterySaverEnabled).isTrue();
+
final PowerSaveState gpsState =
mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS, BATTERY_SAVER_ON);
assertThat(gpsState.batterySaverEnabled).isTrue();