Merge "Hook up data saver to battery saver."
diff --git a/core/java/android/os/PowerSaveState.java b/core/java/android/os/PowerSaveState.java
index 9269e76..7058a1d 100644
--- a/core/java/android/os/PowerSaveState.java
+++ b/core/java/android/os/PowerSaveState.java
@@ -24,7 +24,17 @@
  * @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;
 
@@ -32,10 +42,12 @@
         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();
     }
@@ -48,12 +60,14 @@
     @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;
 
@@ -64,6 +78,11 @@
             return this;
         }
 
+        public Builder setGlobalBatterySaverEnabled(boolean enabled) {
+            mGlobalBatterySaverEnabled = enabled;
+            return this;
+        }
+
         public Builder setGpsMode(int mode) {
             mGpsMode = mode;
             return this;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8a4f3f7..fc45344 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -322,6 +322,12 @@
     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();
@@ -332,6 +338,8 @@
     @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;
 
@@ -617,8 +625,9 @@
                                 @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;
@@ -626,7 +635,7 @@
                                         }
                                     }
                                 }
-                    });
+                            });
                     mRestrictPower = mPowerManagerInternal.getLowPowerState(
                             ServiceType.NETWORK_FIREWALL).batterySaverEnabled;
 
@@ -635,6 +644,32 @@
                     // 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();
                     }
@@ -2159,6 +2194,10 @@
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
+
+        if (mRestrictBackgroundPowerState.globalBatterySaverEnabled) {
+            mRestrictBackgroundChangedInBsm = true;
+        }
         synchronized (mNetworkPoliciesSecondLock) {
             updateNotificationsNL();
             writePolicyAL();
@@ -3645,6 +3684,35 @@
         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++) {
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 8d20531..1781d8c 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -43,7 +43,8 @@
             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;
@@ -55,6 +56,7 @@
         int SCREEN_BRIGHTNESS = 7;
         int SOUND = 8;
         int BATTERY_STATS = 9;
+        int DATA_SAVER = 10;
     }
 
     private static final String TAG = "BatterySaverPolicy";
@@ -73,6 +75,7 @@
     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";
@@ -137,6 +140,14 @@
     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
@@ -191,6 +202,7 @@
             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,
@@ -210,7 +222,8 @@
      */
     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();
@@ -236,6 +249,9 @@
                     return builder.setBatterySaverEnabled(!mAdjustBrightnessDisabled)
                             .setBrightnessFactor(mAdjustBrightnessFactor)
                             .build();
+                case ServiceType.DATA_SAVER:
+                    return builder.setBatterySaverEnabled(!mDataSaverDisabled)
+                            .build();
                 case ServiceType.SOUND:
                     return builder.setBatterySaverEnabled(mSoundTriggerDisabled)
                             .build();
@@ -262,6 +278,7 @@
         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);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index f8d105e..29c6f89 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -37,6 +37,7 @@
 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;
@@ -51,10 +52,15 @@
 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;
 
@@ -89,6 +95,7 @@
 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;
@@ -198,6 +205,7 @@
 
     private IUidObserver mUidObserver;
     private INetworkManagementEventObserver mNetworkObserver;
+    private PowerManagerInternal mPowerManagerInternal;
 
     private NetworkPolicyListenerAnswer mPolicyListener;
     private NetworkPolicyManagerService mService;
@@ -227,12 +235,16 @@
 
     @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
@@ -401,6 +413,85 @@
         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);
@@ -1231,7 +1322,7 @@
 
     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());
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 7282b3e..69589e7 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -19,7 +19,9 @@
 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;
 
@@ -40,6 +42,7 @@
             + "animation_disabled=false,"
             + "soundtrigger_disabled=true,"
             + "firewall_disabled=false,"
+            + "datasaver_disabled=false,"
             + "adjust_brightness_disabled=true,"
             + "adjust_brightness_factor=0.7,"
             + "fullbackup_deferred=true,"
@@ -99,6 +102,18 @@
     }
 
     @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);
 
@@ -137,7 +152,8 @@
         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);
 
@@ -149,6 +165,10 @@
                 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();