Merge "Added tests for idle apps and doze mode." into nyc-dev
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
new file mode 100644
index 0000000..13ce6ce
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+/**
+ * Base class for metered and non-metered tests on idle apps.
+ */
+abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    protected final void setUp() throws Exception {
+        super.setUp();
+
+        // Set initial state.
+        setUpMeteredNetwork();
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        setAppIdle(false);
+        turnBatteryOff();
+
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected final void tearDown() throws Exception {
+        super.tearDown();
+
+        try {
+            tearDownMeteredNetwork();
+        } finally {
+            turnBatteryOn();
+            setAppIdle(false);
+        }
+    }
+
+    /**
+     * Sets the initial (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void setUpMeteredNetwork() throws Exception {
+    }
+
+    /**
+     * Resets the (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void tearDownMeteredNetwork() throws Exception {
+    }
+
+    public void testBackgroundNetworkAccess_enabled() throws Exception {
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        // Make sure foreground app doesn't lose access upon enabling it.
+        setAppIdle(true);
+        launchActivity();
+        assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
+        assertForegroundNetworkAccess();
+        finishActivity();
+        assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
+        assertBackgroundNetworkAccess(true);
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        // Same for foreground service.
+        setAppIdle(true);
+        startForegroundService();
+        assertAppIdle(true); // Sanity check - still idle
+        assertForegroundServiceNetworkAccess();
+        stopForegroundService();
+        assertAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertAppIdle(false); // Sanity check - not idle anymore, since whitelisted
+        assertBackgroundNetworkAccess(true);
+
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertAppIdle(true); // Sanity check - idle again, once whitelisted was removed
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+
+        // Sanity check - no whitelist, no access!
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_disabled() throws Exception {
+        assertBackgroundNetworkAccess(true);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
similarity index 68%
rename from hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeTest.java
rename to hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index 22b876a..2acc670 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -16,60 +16,78 @@
 
 package com.android.cts.net.hostside;
 
-//TODO: move this and BatterySaverModeNonMeteredTest's logic into a common superclass
-public class BatterySaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
+/**
+ * Base class for metered and non-metered Battery Saver Mode tests.
+ */
+abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
 
     @Override
-    public void setUp() throws Exception {
+    protected final void setUp() throws Exception {
         super.setUp();
 
         // Set initial state.
-        setMeteredNetwork();
+        setUpMeteredNetwork();
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        setPowerSaveMode(false);
+        setBatterySaverMode(false);
 
         registerBroadcastReceiver();
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    protected final void tearDown() throws Exception {
         super.tearDown();
 
         try {
-            resetMeteredNetwork();
+            tearDownMeteredNetwork();
         } finally {
-            setPowerSaveMode(false);
+            setBatterySaverMode(false);
         }
     }
 
+    /**
+     * Sets the initial (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void setUpMeteredNetwork() throws Exception {
+    }
+
+    /**
+     * Resets the (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void tearDownMeteredNetwork() throws Exception {
+    }
+
     public void testBackgroundNetworkAccess_enabled() throws Exception {
-        setPowerSaveMode(true);
+        setBatterySaverMode(true);
         assertBackgroundNetworkAccess(false);
 
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(false);
 
         // Make sure foreground app doesn't lose access upon enabling it.
-        setPowerSaveMode(false);
+        setBatterySaverMode(false);
         launchActivity();
         assertForegroundNetworkAccess();
-        setPowerSaveMode(true);
+        setBatterySaverMode(true);
         assertForegroundNetworkAccess();
         finishActivity();
         assertBackgroundNetworkAccess(false);
 
         // Same for foreground service.
-        setPowerSaveMode(false);
+        setBatterySaverMode(false);
         startForegroundService();
         assertForegroundNetworkAccess();
-        setPowerSaveMode(true);
+        setBatterySaverMode(true);
         assertForegroundNetworkAccess();
         stopForegroundService();
         assertBackgroundNetworkAccess(false);
     }
 
     public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        setPowerSaveMode(true);
+        setBatterySaverMode(true);
         assertBackgroundNetworkAccess(false);
 
         addPowerSaveModeWhitelist(TEST_APP2_PKG);
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
new file mode 100644
index 0000000..f3c4935
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+/**
+ * Base class for metered and non-metered Doze Mode tests.
+ */
+abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    protected final void setUp() throws Exception {
+        super.setUp();
+
+        // Set initial state.
+        setUpMeteredNetwork();
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        setDozeMode(false);
+
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected final void tearDown() throws Exception {
+        super.tearDown();
+
+        try {
+            tearDownMeteredNetwork();
+        } finally {
+            setDozeMode(false);
+        }
+    }
+
+    /**
+     * Sets the initial (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void setUpMeteredNetwork() throws Exception {
+    }
+
+    /**
+     * Resets the (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void tearDownMeteredNetwork() throws Exception {
+    }
+
+    public void testBackgroundNetworkAccess_enabled() throws Exception {
+        setDozeMode(true);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+
+        // Make sure foreground service doesn't lose network access upon enabling doze.
+        setDozeMode(false);
+        startForegroundService();
+        assertForegroundNetworkAccess();
+        setDozeMode(true);
+        assertForegroundNetworkAccess();
+        stopForegroundService();
+        assertBackgroundState();
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+        setDozeMode(true);
+        assertBackgroundNetworkAccess(false);
+
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(true);
+
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_disabled() throws Exception {
+        assertBackgroundNetworkAccess(true);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+    }
+
+    // Must override so it only tests foreground service - once an app goes to foreground, device
+    // leaves Doze Mode.
+    @Override
+    protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception {
+        startForegroundService();
+        assertForegroundServiceNetworkAccess();
+        stopForegroundService();
+        assertBackgroundState();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index f2a69f2..17480e2 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -80,7 +80,7 @@
     private String mMeteredWifi;
 
     @Override
-    public void setUp() throws Exception {
+    protected void setUp() throws Exception {
         super.setUp();
 
         mInstrumentation = getInstrumentation();
@@ -329,7 +329,12 @@
      */
     protected void assertDelayedShellCommand(String command, final String expectedResult)
             throws Exception {
-        assertDelayedShellCommand(command, new ExpectResultChecker() {
+        assertDelayedShellCommand(command, 5, 1, expectedResult);
+    }
+
+    protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+            final String expectedResult) throws Exception {
+        assertDelayedShellCommand(command, maxTries, napTimeSeconds, new ExpectResultChecker() {
 
             @Override
             public boolean isExpected(String result) {
@@ -345,21 +350,28 @@
 
     protected void assertDelayedShellCommand(String command, ExpectResultChecker checker)
             throws Exception {
-        final int maxTries = 5;
+        assertDelayedShellCommand(command, 5, 1, checker);
+    }
+    protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+            ExpectResultChecker checker) throws Exception {
         String result = "";
         for (int i = 1; i <= maxTries; i++) {
             result = executeShellCommand(command).trim();
             if (checker.isExpected(result)) return;
             Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
                     + checker.getExpected() + "' on attempt #" + i
-                    + "; sleeping 1s before trying again");
-            Thread.sleep(SECOND_IN_MS);
+                    + "; sleeping " + napTimeSeconds + "s before trying again");
+            Thread.sleep(napTimeSeconds * SECOND_IN_MS);
         }
         fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after "
                 + maxTries
                 + " attempts. Last result: '" + result + "'");
     }
 
+    /**
+     * Puts the device in a state where the active network is metered, or fail if it can't achieve
+     * that state.
+     */
     protected void setMeteredNetwork() throws Exception {
         final NetworkInfo info = mCm.getActiveNetworkInfo();
         final boolean metered = mCm.isActiveNetworkMetered();
@@ -375,17 +387,37 @@
         mMeteredWifi = netId;
         // Sanity check.
         assertWifiMeteredStatus(netId, true);
-        assertTrue("Could not set wifi '" + netId + "' as metered ("
-                + mCm.getActiveNetworkInfo() +")", mCm.isActiveNetworkMetered());
+        assertActiveNetworkMetered(true);
     }
 
+    /**
+     * Puts the device in a state where the active network is not metered, or fail if it can't
+     * achieve that state.
+     * <p>It assumes the device has a valid WI-FI connection.
+     */
     protected void resetMeteredNetwork() throws Exception {
-        if (mMeteredWifi == null) {
-            Log.d(TAG, "resetMeteredNetwork(): wifi not set as metered");
-            return;
+        if (mMeteredWifi != null) {
+            Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi
+                    + "' was set as metered by test case; resetting it");
+            setWifiMeteredStatus(mMeteredWifi, false);
+        } else {
+            final NetworkInfo info = mCm.getActiveNetworkInfo();
+            assertNotNull("Could not get active network", info);
+            if (!info.isMetered()) {
+                Log.d(TAG, "Active network is not metered: " + info);
+            } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {
+                Log.i(TAG, "Setting active WI-FI network as metered: " + info );
+                setWifiMeteredStatus(false);
+            } else {
+                fail("Active network is not WI-FI hence cannot be set as non-metered: " + info);
+            }
         }
-        Log.i(TAG, "resetMeteredNetwork(): resetting " + mMeteredWifi);
-        setWifiMeteredStatus(mMeteredWifi, false);
+        assertActiveNetworkMetered(false); // Sanity check.
+    }
+
+    private void assertActiveNetworkMetered(boolean expected) {
+        final NetworkInfo info = mCm.getActiveNetworkInfo();
+        assertEquals("Wrong metered status for active network " + info, expected, info.isMetered());
     }
 
     private String setWifiMeteredStatus(boolean metered) throws Exception {
@@ -512,16 +544,63 @@
         assertPowerSaveModeWhitelist(packageName, false); // Sanity check
     }
 
-    protected void setPowerSaveMode(boolean enabled) throws Exception {
-        Log.i(TAG, "Setting power mode to " + enabled);
+    protected void turnBatteryOff() throws Exception {
+        executeSilentShellCommand("cmd battery unplug");
+    }
+
+    protected void turnBatteryOn() throws Exception {
+        executeSilentShellCommand("cmd battery reset");
+    }
+
+    protected void turnScreenOff() throws Exception {
+        executeSilentShellCommand("input keyevent KEYCODE_SLEEP");
+    }
+
+    protected void turnScreenOn() throws Exception {
+        executeSilentShellCommand("input keyevent KEYCODE_WAKEUP");
+        executeSilentShellCommand("wm dismiss-keyguard");
+    }
+
+    protected void setBatterySaverMode(boolean enabled) throws Exception {
+        Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
         if (enabled) {
+            turnBatteryOff();
             executeSilentShellCommand("cmd battery unplug");
             executeSilentShellCommand("settings put global low_power 1");
         } else {
-            executeSilentShellCommand("cmd battery reset");
+            turnBatteryOn();
         }
     }
 
+    protected void setDozeMode(boolean enabled) throws Exception {
+        Log.i(TAG, "Setting Doze Mode to " + enabled);
+        if (enabled) {
+            turnBatteryOff();
+            turnScreenOff();
+            executeShellCommand("dumpsys deviceidle force-idle deep");
+        } else {
+            turnScreenOn();
+            turnBatteryOn();
+            executeShellCommand("dumpsys deviceidle unforce");
+        }
+        // Sanity check.
+        assertDozeMode(enabled);
+    }
+
+    protected void assertDozeMode(boolean enabled) throws Exception {
+        assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
+    }
+
+    protected void setAppIdle(boolean enabled) throws Exception {
+        Log.i(TAG, "Setting app idle to " + enabled);
+        executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
+        assertAppIdle(enabled); // Sanity check
+    }
+
+    protected void assertAppIdle(boolean enabled) throws Exception {
+        assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 10, 2, "Idle=" + enabled);
+    }
+
     /**
      * Starts a service that will register a broadcast receiver to receive
      * {@code RESTRICT_BACKGROUND_CHANGE} intents.
@@ -548,19 +627,23 @@
 
     protected void startForegroundService() throws Exception {
         executeShellCommand(
-                "am startservice com.android.cts.net.hostside.app2/.MyForegroundService");
+                "am startservice -f 1 com.android.cts.net.hostside.app2/.MyForegroundService");
+        assertForegroundServiceState();
     }
 
     protected void stopForegroundService() throws Exception {
         executeShellCommand(
-                "am stopservice com.android.cts.net.hostside.app2/.MyForegroundService");
+                "am startservice -f 2 com.android.cts.net.hostside.app2/.MyForegroundService");
+        // NOTE: cannot assert state because it depends on whether activity was on top before.
     }
 
     /**
      * Launches an activity on app2 so its process is elevated to foreground status.
      */
     protected void launchActivity() throws Exception {
+        turnScreenOn();
         executeShellCommand("am start com.android.cts.net.hostside.app2/.MyActivity");
+        assertForegroundState();
     }
 
     /**
@@ -608,8 +691,17 @@
         }
     }
 
+    /**
+     * Helper class used to assert the result of a Shell command.
+     */
     protected static interface ExpectResultChecker {
+        /**
+         * Checkes whether the result of the command matched the expectation.
+         */
         boolean isExpected(String result);
+        /**
+         * Gets the expected result so it's displayed on log and failure messages.
+         */
         String getExpected();
     }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
new file mode 100644
index 0000000..e008c69
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
+
+    @Override
+    protected void setUpMeteredNetwork() throws Exception {
+        setMeteredNetwork();
+    }
+
+    @Override
+    protected void tearDownMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
new file mode 100644
index 0000000..633dc81
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
+
+    @Override
+    protected void setUpMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
new file mode 100644
index 0000000..3a88bbd
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
+
+    @Override
+    protected void setUpMeteredNetwork() throws Exception {
+        setMeteredNetwork();
+    }
+
+    @Override
+    protected void tearDownMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
index d1db01c..646c4b9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -16,71 +16,10 @@
 
 package com.android.cts.net.hostside;
 
-//TODO: move this and BatterySaverModeTest's logic into a common superclass
-public class BatterySaverModeNonMeteredTest extends AbstractRestrictBackgroundNetworkTestCase {
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
 
     @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
-        // Set initial state.
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        setPowerSaveMode(false);
-
-        registerBroadcastReceiver();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        setPowerSaveMode(false);
-    }
-
-    public void testBackgroundNetworkAccess_enabled() throws Exception {
-        setPowerSaveMode(true);
-        assertBackgroundNetworkAccess(false);
-
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-
-        // Make sure foreground app doesn't lose access upon enabling it.
-        setPowerSaveMode(false);
-        launchActivity();
-        assertForegroundNetworkAccess();
-        setPowerSaveMode(true);
-        assertForegroundNetworkAccess();
-        finishActivity();
-        assertBackgroundNetworkAccess(false);
-
-        // Same for foreground service.
-        setPowerSaveMode(false);
-        startForegroundService();
-        assertForegroundNetworkAccess();
-        setPowerSaveMode(true);
-        assertForegroundNetworkAccess();
-        stopForegroundService();
-        assertBackgroundNetworkAccess(false);
-    }
-
-    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        setPowerSaveMode(true);
-        assertBackgroundNetworkAccess(false);
-
-        addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(true);
-
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(false);
-
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-    }
-
-    public void testBackgroundNetworkAccess_disabled() throws Exception {
-        assertBackgroundNetworkAccess(true);
-
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(true);
+    protected void setUpMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
     }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
new file mode 100644
index 0000000..656d274
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
+
+    @Override
+    protected void setUpMeteredNetwork() throws Exception {
+        setMeteredNetwork();
+    }
+
+    @Override
+    protected void tearDownMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
new file mode 100644
index 0000000..c761238
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+
+    @Override
+    protected void setUpMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
index 140d135..c97a0f9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -47,7 +47,7 @@
         try {
             setRestrictBackground(false);
         } finally {
-            setPowerSaveMode(false);
+            setBatterySaverMode(false);
         }
     }
 
@@ -60,7 +60,7 @@
 
         try {
             setRestrictBackground(true);
-            setPowerSaveMode(true);
+            setBatterySaverMode(true);
 
             Log.v(TAG, "Not whitelisted for any.");
             assertBackgroundNetworkAccess(false);
@@ -126,7 +126,7 @@
         }
         Log.i(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() tests");
         setRestrictBackground(true);
-        setPowerSaveMode(true);
+        setBatterySaverMode(true);
 
         Log.v(TAG, "Not whitelisted for any.");
         assertBackgroundNetworkAccess(false);
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
index 1afc3d6..b88c45d 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
@@ -28,6 +28,9 @@
  */
 public class MyForegroundService extends Service {
 
+    private static final int FLAG_START_FOREGROUND = 1;
+    private static final int FLAG_STOP_FOREGROUND = 2;
+
     @Override
     public IBinder onBind(Intent intent) {
         return null;
@@ -35,10 +38,21 @@
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        Log.d(TAG, "MyForegroundService.onStartCommand(): " + intent);
-        startForeground(42, new Notification.Builder(this)
-            .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
-            .build());
+        Log.v(TAG, "MyForegroundService.onStartCommand(): " + intent);
+        switch (intent.getFlags()) {
+            case FLAG_START_FOREGROUND:
+                Log.d(TAG, "Starting foreground");
+                startForeground(42, new Notification.Builder(this)
+                        .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
+                        .build());
+                break;
+            case FLAG_STOP_FOREGROUND:
+                Log.d(TAG, "Stopping foreground");
+                stopForeground(true);
+                break;
+            default:
+                Log.wtf(TAG, "Invalid flag on intent " + intent);
+        }
         return START_STICKY;
     }
 }
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index ec375d6..04a02ea 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -39,6 +39,10 @@
         uninstallPackage(TEST_APP2_PKG, true);
     }
 
+    /**************************
+     * Data Saver Mode tests. *
+     **************************/
+
     public void testDataSaverMode_disabled() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
                 "testGetRestrictBackgroundStatus_disabled");
@@ -80,18 +84,22 @@
                 "testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
     }
 
-    public void testBatterySaverMode_disabled() throws Exception {
-        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeTest",
+    /*****************************
+     * Battery Saver Mode tests. *
+     *****************************/
+
+    public void testBatterySaverModeMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
                 "testBackgroundNetworkAccess_disabled");
     }
 
-    public void testBatterySaverMode_whitelisted() throws Exception {
-        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeTest",
+    public void testBatterySaverModeMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
                 "testBackgroundNetworkAccess_whitelisted");
     }
 
-    public void testBatterySaverMode_enabled() throws Exception {
-        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeTest",
+    public void testBatterySaverModeMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
                 "testBackgroundNetworkAccess_enabled");
     }
 
@@ -121,6 +129,88 @@
                 "testBackgroundNetworkAccess_enabled");
     }
 
+    /*******************
+     * App idle tests. *
+     *******************/
+
+    public void testAppIdleMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testAppIdleMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testAppIdleMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+    // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+    //    public void testAppIdle_reinstall() throws Exception {
+    //    }
+
+    public void testAppIdleNonMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testAppIdleNonMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testAppIdleNonMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    /********************
+     * Doze Mode tests. *
+     ********************/
+
+    public void testDozeModeMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testDozeModeMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testDozeModeMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+    // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+    //    public void testDozeMode_reinstall() throws Exception {
+    //    }
+
+    public void testDozeModeNonMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testDozeModeNonMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testDozeModeNonMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    /**********************
+     * Mixed modes tests. *
+     **********************/
+
     public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
                 "testDataAndBatterySaverModes_meteredNetwork");
@@ -131,6 +221,10 @@
                 "testDataAndBatterySaverModes_nonMeteredNetwork");
     }
 
+    /*******************
+     * Helper methods. *
+     *******************/
+
     private void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
         final int max_tries = 5;
         boolean actual = false;