Pull service dumps to help debug test failures.

+ Convert tests to use Junit4
+ Add annotations to specify required conditions for the test to run.

Bug: 137859686
Test: atest hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
Test: atest hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java
Change-Id: I93317c201a0ea06732e29154ab7e140735381f59
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index dbff179..5479c51 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -31,4 +31,9 @@
         <option name="jar" value="CtsHostsideNetworkTests.jar" />
         <option name="runtime-hint" value="3m56s" />
     </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/CtsHostsideNetworkTests" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
 </configuration>
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index d66b71b..e988ea4 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -20,6 +20,8 @@
     //sdk_version: "current",
     platform_apis: true,
     static_libs: [
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
         "ub-uiautomator",
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index f54b5c1..3b00713 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -26,8 +26,9 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
-    <application>
+    <application android:requestLegacyExternalStorage="true" >
         <uses-library android:name="android.test.runner" />
         <activity android:name=".MyActivity" />
         <service android:name=".MyVpnService"
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 55bd406..d06ab13 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -16,20 +16,27 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+
 import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * Base class for metered and non-metered tests on idle apps.
  */
+@RequiredProperties({APP_STANDBY_MODE})
 abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
 
-    @Override
-    protected final void setUp() throws Exception {
+    @Before
+    public final void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
         removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -39,41 +46,16 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected final void tearDown() throws Exception {
+    @After
+    public final void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            tearDownMeteredNetwork();
-        } finally {
-            turnBatteryOff();
-            setAppIdle(false);
-        }
+        turnBatteryOff();
+        setAppIdle(false);
     }
 
-    @Override
-    protected boolean isSupported() throws Exception {
-        boolean supported = isDozeModeEnabled();
-        if (!supported) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Doze Mode");
-        }
-        return supported;
-    }
-
-    /**
-     * Resets the (non) metered network state.
-     *
-     * <p>By default is empty - it's up to subclasses to override.
-     */
-    protected void tearDownMeteredNetwork() throws Exception {
-    }
-
+    @Test
     public void testBackgroundNetworkAccess_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
 
@@ -98,9 +80,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
 
@@ -127,9 +108,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
 
@@ -140,23 +120,17 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertBackgroundNetworkAccess(true);
 
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(true);
     }
 
+    @RequiredProperties({BATTERY_SAVER_MODE})
+    @Test
     public void testAppIdleNetworkAccess_whenCharging() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) return;
-
         // Check that app is paroled when charging
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
@@ -180,9 +154,8 @@
         assertBackgroundNetworkAccess(true);
     }
 
+    @Test
     public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertAppIdle(true);
         assertBackgroundNetworkAccess(false);
@@ -199,9 +172,8 @@
         removeAppIdleWhitelist(mUid + 1);
     }
 
+    @Test
     public void testAppIdle_toast() throws Exception {
-        if (!isSupported()) return;
-
         setAppIdle(true);
         assertAppIdle(true);
         assertEquals("Shown", showToast());
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index 931376b..04d054d 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -16,20 +16,22 @@
 
 package com.android.cts.net.hostside;
 
-import android.text.TextUtils;
-import android.util.Log;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * Base class for metered and non-metered Battery Saver Mode tests.
  */
+@RequiredProperties({BATTERY_SAVER_MODE})
 abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
 
-    @Override
-    protected final void setUp() throws Exception {
+    @Before
+    public final void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
         removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,55 +40,15 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected final void tearDown() throws Exception {
+    @After
+    public final void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            tearDownMeteredNetwork();
-        } finally {
-            setBatterySaverMode(false);
-        }
+        setBatterySaverMode(false);
     }
 
-    @Override
-    protected boolean isSupported() throws Exception {
-        String unSupported = "";
-        if (!isDozeModeEnabled()) {
-            unSupported += "Doze mode,";
-        }
-        if (!isBatterySaverSupported()) {
-            unSupported += "Battery saver mode,";
-        }
-        if (!TextUtils.isEmpty(unSupported)) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support " + unSupported);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * 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 {
-    }
-
+    @Test
     public void testBackgroundNetworkAccess_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setBatterySaverMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -118,9 +80,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setBatterySaverMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -140,9 +101,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertBackgroundNetworkAccess(true);
 
         assertsForegroundAlwaysHasNetworkAccess();
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index f20f1d1..6f32c56 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -16,20 +16,25 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.NOT_LOW_RAM_DEVICE;
+
 import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * Base class for metered and non-metered Doze Mode tests.
  */
+@RequiredProperties({DOZE_MODE})
 abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
 
-    @Override
-    protected final void setUp() throws Exception {
+    @Before
+    public final void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removePowerSaveModeWhitelist(TEST_APP2_PKG);
         removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,48 +43,15 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected final void tearDown() throws Exception {
+    @After
+    public final void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            tearDownMeteredNetwork();
-        } finally {
-            setDozeMode(false);
-        }
+        setDozeMode(false);
     }
 
-    @Override
-    protected boolean isSupported() throws Exception {
-        boolean supported = isDozeModeEnabled();
-        if (!supported) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Doze Mode");
-        }
-        return supported;
-    }
-
-    /**
-     * 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 {
-    }
-
+    @Test
     public void testBackgroundNetworkAccess_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setDozeMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -96,9 +68,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setDozeMode(true);
         assertBackgroundNetworkAccess(false);
 
@@ -118,19 +89,18 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testBackgroundNetworkAccess_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertBackgroundNetworkAccess(true);
 
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(true);
     }
 
+    @RequiredProperties({NOT_LOW_RAM_DEVICE})
+    @Test
     public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
             throws Exception {
-        if (!isSupported() || isLowRamDevice()) return;
-
         setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
         try {
             registerNotificationListenerService();
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 40d7e34..57b7bb4 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -17,14 +17,22 @@
 package com.android.cts.net.hostside;
 
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.os.BatteryManager.BATTERY_PLUGGED_AC;
 import static android.os.BatteryManager.BATTERY_PLUGGED_USB;
 import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
 
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getWifiManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.app.ActivityManager;
 import android.app.Instrumentation;
@@ -34,9 +42,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
 import android.net.wifi.WifiManager;
@@ -44,24 +50,27 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
-import android.test.InstrumentationTestCase;
-import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.compatibility.common.util.BatteryUtils;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 /**
  * Superclass for tests related to background network restrictions.
  */
-abstract class AbstractRestrictBackgroundNetworkTestCase extends InstrumentationTestCase {
-    protected static final String TAG = "RestrictBackgroundNetworkTests";
+@RunWith(AndroidJUnit4.class)
+public abstract class AbstractRestrictBackgroundNetworkTestCase {
+    public static final String TAG = "RestrictBackgroundNetworkTests";
 
     protected static final String TEST_PKG = "com.android.cts.net.hostside";
     protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
@@ -98,8 +107,6 @@
     static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
     private static int PROCESS_STATE_FOREGROUND_SERVICE;
 
-    private static final int PROCESS_STATE_TOP = 2;
-
     private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
 
     protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
@@ -126,22 +133,23 @@
     protected WifiManager mWfm;
     protected int mUid;
     private int mMyUid;
-    private String mMeteredWifi;
     private MyServiceClient mServiceClient;
     private String mDeviceIdleConstantsSetting;
-    private boolean mSupported;
     private boolean mIsLocationOn;
 
-    @Override
+    @Rule
+    public final RuleChain mRuleChain = RuleChain.outerRule(new DumpOnFailureRule())
+            .around(new RequiredPropertiesRule())
+            .around(new MeterednessConfigurationRule());
+
     protected void setUp() throws Exception {
-        super.setUp();
 
         PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class
                 .getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null);
         mInstrumentation = getInstrumentation();
-        mContext = mInstrumentation.getContext();
-        mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mContext = getContext();
+        mCm = getConnectivityManager();
+        mWfm = getWifiManager();
         mUid = getUid(TEST_APP2_PKG);
         mMyUid = getUid(mContext.getPackageName());
         mServiceClient = new MyServiceClient(mContext);
@@ -151,10 +159,9 @@
         if (!mIsLocationOn) {
             enableLocation();
         }
-        mSupported = setUpActiveNetworkMeteringState();
         setAppIdle(false);
 
-        Log.i(TAG, "Apps status on " + getName() + ":\n"
+        Log.i(TAG, "Apps status:\n"
                 + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
                 + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
 
@@ -165,16 +172,13 @@
         final String currentConstants =
                 executeShellCommand("settings get global app_idle_constants");
         assertEquals(appIdleConstants, currentConstants);
-   }
+    }
 
-    @Override
     protected void tearDown() throws Exception {
         if (!mIsLocationOn) {
             disableLocation();
         }
         mServiceClient.unbind();
-
-        super.tearDown();
     }
 
     private void enableLocation() throws Exception {
@@ -259,23 +263,8 @@
     protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
         final String status = mServiceClient.getRestrictBackgroundStatus();
         assertNotNull("didn't get API status from app2", status);
-        final String actualStatus = toString(Integer.parseInt(status));
-        assertEquals("wrong status", toString(expectedStatus), actualStatus);
-    }
-
-    protected void assertMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
-        final int actualStatus = mCm.getRestrictBackgroundStatus();
-        assertEquals("Wrong status", toString(expectedStatus), toString(actualStatus));
-    }
-
-    protected boolean isMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
-        final int actualStatus = mCm.getRestrictBackgroundStatus();
-        if (expectedStatus != actualStatus) {
-            Log.d(TAG, "Expected: " + toString(expectedStatus)
-                    + " but actual: " + toString(actualStatus));
-            return false;
-        }
-        return true;
+        assertEquals(restrictBackgroundValueToString(expectedStatus),
+                restrictBackgroundValueToString(Integer.parseInt(status)));
     }
 
     protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
@@ -298,28 +287,6 @@
     }
 
     /**
-     * Whether this device suport this type of test.
-     *
-     * <p>Should be overridden when necessary (but always calling
-     * {@code super.isSupported()} first), and explicitly used before each test
-     * Example:
-     *
-     * <pre><code>
-     * public void testSomething() {
-     *    if (!isSupported()) return;
-     * </code></pre>
-     *
-     * @return {@code true} by default.
-     */
-    protected boolean isSupported() throws Exception {
-        return mSupported;
-    }
-
-    protected boolean isBatterySaverSupported() {
-        return BatteryUtils.isBatterySaverSupported();
-    }
-
-    /**
      * Asserts that an app always have access while on foreground or running a foreground service.
      *
      * <p>This method will launch an activity and a foreground service to make the assertion, but
@@ -388,23 +355,6 @@
     }
 
     /**
-     * As per CDD requirements, if the device doesn't support data saver mode then
-     * ConnectivityManager.getRestrictBackgroundStatus() will always return
-     * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
-     * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
-     * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
-     */
-    protected boolean isDataSaverSupported() throws Exception {
-        assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
-        try {
-            setRestrictBackground(true);
-            return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
-        } finally {
-            setRestrictBackground(false);
-        }
-    }
-
-    /**
      * Returns whether an app state should be considered "background" for restriction purposes.
      */
     protected boolean isBackground(int state) {
@@ -443,40 +393,10 @@
             // Exponential back-off.
             timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS);
         }
-        dumpOnFailure();
         fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries
                 + " attempts.\nLast error: " + error);
     }
 
-    private void dumpOnFailure() throws Exception {
-        dumpAllNetworkRules();
-        Log.d(TAG, "Usagestats dump: " + getUsageStatsDump());
-        executeShellCommand("settings get global app_idle_constants");
-    }
-
-    private void dumpAllNetworkRules() throws Exception {
-        final String networkManagementDump = runShellCommand(mInstrumentation,
-                "dumpsys network_management").trim();
-        final String networkPolicyDump = runShellCommand(mInstrumentation,
-                "dumpsys netpolicy").trim();
-        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
-        splitter.setString(networkManagementDump);
-        String next;
-        Log.d(TAG, ">>> Begin network_management dump");
-        while (splitter.hasNext()) {
-            next = splitter.next();
-            Log.d(TAG, next);
-        }
-        Log.d(TAG, "<<< End network_management dump");
-        splitter.setString(networkPolicyDump);
-        Log.d(TAG, ">>> Begin netpolicy dump");
-        while (splitter.hasNext()) {
-            next = splitter.next();
-            Log.d(TAG, next);
-        }
-        Log.d(TAG, "<<< End netpolicy dump");
-    }
-
     /**
      * Checks whether the network is available as expected.
      *
@@ -528,22 +448,10 @@
         return errors.toString();
     }
 
-    protected boolean isLowRamDevice() {
-        final ActivityManager am = (ActivityManager) mContext.getSystemService(
-            Context.ACTIVITY_SERVICE);
-        return am.isLowRamDevice();
-    }
-
-    protected String executeShellCommand(String command) throws Exception {
-        final String result = runShellCommand(mInstrumentation, command).trim();
-        if (DEBUG) Log.d(TAG, "Command '" + command + "' returned '" + result + "'");
-        return result;
-    }
-
     /**
      * Runs a Shell command which is not expected to generate output.
      */
-    protected void executeSilentShellCommand(String command) throws Exception {
+    protected void executeSilentShellCommand(String command) {
         final String result = executeShellCommand(command);
         assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
     }
@@ -572,10 +480,6 @@
         });
     }
 
-    protected void assertDelayedShellCommand(String command, ExpectResultChecker checker)
-            throws Exception {
-        assertDelayedShellCommand(command, 5, 1, checker);
-    }
     protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
             ExpectResultChecker checker) throws Exception {
         String result = "";
@@ -592,159 +496,6 @@
                 + " attempts. Last result: '" + result + "'");
     }
 
-    /**
-     * Sets the initial metering state for the active network.
-     *
-     * <p>It's called on setup and by default does nothing - it's up to the
-     * subclasses to override.
-     *
-     * @return whether the tests in the subclass are supported on this device.
-     */
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return true;
-    }
-
-    /**
-     * Makes sure the active network is not metered.
-     *
-     * <p>If the device does not supoprt un-metered networks (for example if it
-     * only has cellular data but not wi-fi), it should return {@code false};
-     * otherwise, it should return {@code true} (or fail if the un-metered
-     * network could not be set).
-     *
-     * @return {@code true} if the network is now unmetered.
-     */
-    protected boolean setUnmeteredNetwork() throws Exception {
-        final NetworkInfo info = mCm.getActiveNetworkInfo();
-        assertNotNull("Could not get active network", info);
-        if (!mCm.isActiveNetworkMetered()) {
-            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 not metered: " + info );
-            setWifiMeteredStatus(false);
-        } else {
-            Log.d(TAG, "Active network cannot be set to un-metered: " + info);
-            return false;
-        }
-        assertActiveNetworkMetered(false); // Sanity check.
-        return true;
-    }
-
-    /**
-     * Enables metering on the active network if supported.
-     *
-     * <p>If the device does not support metered networks it should return
-     * {@code false}; otherwise, it should return {@code true} (or fail if the
-     * metered network could not be set).
-     *
-     * @return {@code true} if the network is now metered.
-     */
-    protected boolean setMeteredNetwork() throws Exception {
-        final NetworkInfo info = mCm.getActiveNetworkInfo();
-        final boolean metered = mCm.isActiveNetworkMetered();
-        if (metered) {
-            Log.d(TAG, "Active network already metered: " + info);
-            return true;
-        } else if (info.getType() != ConnectivityManager.TYPE_WIFI) {
-            Log.w(TAG, "Active network does not support metering: " + info);
-            return false;
-        } else {
-            Log.w(TAG, "Active network not metered: " + info);
-        }
-        final String netId = setWifiMeteredStatus(true);
-
-        // Set flag so status is reverted on resetMeteredNetwork();
-        mMeteredWifi = netId;
-        // Sanity check.
-        assertWifiMeteredStatus(netId, true);
-        assertActiveNetworkMetered(true);
-        return true;
-    }
-
-    /**
-     * Resets the device metering state to what it was before the test started.
-     *
-     * <p>This reverts any metering changes made by {@code setMeteredNetwork}.
-     */
-    protected void resetMeteredNetwork() throws Exception {
-        if (mMeteredWifi != null) {
-            Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi
-                    + "' was set as metered by test case; resetting it");
-            setWifiMeteredStatus(mMeteredWifi, false);
-            assertActiveNetworkMetered(false); // Sanity check.
-        }
-    }
-
-    private void assertActiveNetworkMetered(boolean expected) throws Exception {
-        final int maxTries = 5;
-        NetworkInfo info = null;
-        for (int i = 1; i <= maxTries; i++) {
-            info = mCm.getActiveNetworkInfo();
-            if (info == null) {
-                Log.v(TAG, "No active network info on attempt #" + i
-                        + "; sleeping 1s before polling again");
-            } else if (mCm.isActiveNetworkMetered() != expected) {
-                Log.v(TAG, "Wrong metered status for active network " + info + "; expected="
-                        + expected + "; sleeping 1s before polling again");
-            } else {
-                break;
-            }
-            Thread.sleep(SECOND_IN_MS);
-        }
-        assertNotNull("No active network after " + maxTries + " attempts", info);
-        assertEquals("Wrong metered status for active network " + info, expected,
-                mCm.isActiveNetworkMetered());
-    }
-
-    private String setWifiMeteredStatus(boolean metered) throws Exception {
-        // We could call setWifiEnabled() here, but it might take sometime to be in a consistent
-        // state (for example, if one of the saved network is not properly authenticated), so it's
-        // better to let the hostside test take care of that.
-        assertTrue("wi-fi is disabled", mWfm.isWifiEnabled());
-        // TODO: if it's not guaranteed the device has wi-fi, we need to change the tests
-        // to make the actual verification of restrictions optional.
-        final String ssid = mWfm.getConnectionInfo().getSSID();
-        return setWifiMeteredStatus(ssid, metered);
-    }
-
-    private String setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
-        assertNotNull("null SSID", ssid);
-        final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
-        assertFalse("empty SSID", ssid.isEmpty());
-
-        Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
-        final String setCommand = "cmd netpolicy set metered-network " + netId + " " + metered;
-        assertDelayedShellCommand(setCommand, "");
-
-        return netId;
-    }
-
-    private void assertWifiMeteredStatus(String netId, boolean status) throws Exception {
-        final String command = "cmd netpolicy list wifi-networks";
-        final String expectedLine = netId + ";" + status;
-        assertDelayedShellCommand(command, new ExpectResultChecker() {
-
-            @Override
-            public boolean isExpected(String result) {
-                return result.contains(expectedLine);
-            }
-
-            @Override
-            public String getExpected() {
-                return "line containing " + expectedLine;
-            }
-        });
-    }
-
-    protected void setRestrictBackground(boolean enabled) throws Exception {
-        executeShellCommand("cmd netpolicy set restrict-background " + enabled);
-        final String output = executeShellCommand("cmd netpolicy get restrict-background ");
-        final String expectedSuffix = enabled ? "enabled" : "disabled";
-        // TODO: use MoreAsserts?
-        assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
-                output.endsWith(expectedSuffix));
-      }
-
     protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
         executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
         assertRestrictBackgroundWhitelist(uid, true);
@@ -924,7 +675,7 @@
 
     protected void setDozeMode(boolean enabled) throws Exception {
         // Sanity check, since tests should check beforehand....
-        assertTrue("Device does not support Doze Mode", isDozeModeEnabled());
+        assertTrue("Device does not support Doze Mode", isDozeModeSupported());
 
         Log.i(TAG, "Setting Doze Mode to " + enabled);
         if (enabled) {
@@ -944,43 +695,16 @@
         assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
     }
 
-    protected boolean isDozeModeEnabled() throws Exception {
-        final String result = executeShellCommand("cmd deviceidle enabled deep").trim();
-        return result.equals("1");
-    }
-
     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
     }
 
-    private String getUsageStatsDump() throws Exception {
-        final String output = runShellCommand(mInstrumentation, "dumpsys usagestats").trim();
-        final StringBuilder sb = new StringBuilder();
-        final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
-        splitter.setString(output);
-        String str;
-        while (splitter.hasNext()) {
-            str = splitter.next();
-            if (str.contains("package=")
-                    && !str.contains(TEST_PKG) && !str.contains(TEST_APP2_PKG)) {
-                continue;
-            }
-            if (str.trim().startsWith("config=") || str.trim().startsWith("time=")) {
-                continue;
-            }
-            sb.append(str).append('\n');
-        }
-        return sb.toString();
-    }
-
     protected void assertAppIdle(boolean enabled) throws Exception {
         try {
             assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled);
         } catch (Throwable e) {
-            Log.d(TAG, "UsageStats dump:\n" + getUsageStatsDump());
-            executeShellCommand("settings get global app_idle_constants");
             throw e;
         }
     }
@@ -1061,12 +785,10 @@
                         // App didn't come to foreground when the activity is started, so try again.
                         assertForegroundNetworkAccess();
                     } else {
-                        dumpOnFailure();
                         fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
                     }
                 }
             } else {
-                dumpOnFailure();
                 fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
             }
         } else {
@@ -1150,19 +872,6 @@
         }
     }
 
-    private String toString(int status) {
-        switch (status) {
-            case RESTRICT_BACKGROUND_STATUS_DISABLED:
-                return "DISABLED";
-            case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
-                return "WHITELISTED";
-            case RESTRICT_BACKGROUND_STATUS_ENABLED:
-                return "ENABLED";
-            default:
-                return "UNKNOWN_STATUS_" + status;
-        }
-    }
-
     private ProcessState getProcessStateByUid(int uid) throws Exception {
         return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
     }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
index 622d993..f1858d6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
@@ -16,15 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
 public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
-
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected void tearDownMeteredNetwork() throws Exception {
-        resetMeteredNetwork();
-    }
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
index bde71f9..e737a6d 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
@@ -16,9 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
 public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setUnmeteredNetwork();
-    }
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
index 3071cfe..c78ca2e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
@@ -16,15 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
 public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
-
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected void tearDownMeteredNetwork() throws Exception {
-        resetMeteredNetwork();
-    }
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
index 6d3076f..fb52a54 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -16,10 +16,9 @@
 
 package com.android.cts.net.hostside;
 
-public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
 
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setUnmeteredNetwork();
-    }
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index cfe6a73..aa2c914 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -20,24 +20,33 @@
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 
-import android.util.Log;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NO_DATA_SAVER_MODE;
+
+import static org.junit.Assert.fail;
 
 import com.android.compatibility.common.util.CddTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import androidx.test.filters.LargeTest;
+
+@RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+@LargeTest
 public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
 
     private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
         "com.android.providers.downloads"
     };
 
-    private boolean mIsDataSaverSupported;
-
-    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        mIsDataSaverSupported = isDataSaverSupported();
-
         // Set initial state.
         setRestrictBackground(false);
         removeRestrictBackgroundWhitelist(mUid);
@@ -47,36 +56,15 @@
         assertRestrictBackgroundChangedReceived(0);
    }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
-        try {
-            resetMeteredNetwork();
-        } finally {
-            setRestrictBackground(false);
-        }
+        setRestrictBackground(false);
     }
 
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected boolean isSupported() throws Exception {
-        if (!mIsDataSaverSupported) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Data Saver Mode");
-        }
-        return mIsDataSaverSupported && super.isSupported();
-    }
-
+    @Test
     public void testGetRestrictBackgroundStatus_disabled() throws Exception {
-        if (!isSupported()) return;
-
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
 
         // Sanity check: make sure status is always disabled, never whitelisted
@@ -88,9 +76,8 @@
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
-        if (!isSupported()) return;
-
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(1);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -107,9 +94,8 @@
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_enabled() throws Exception {
-        if (!isSupported()) return;
-
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(1);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -142,9 +128,8 @@
         assertBackgroundNetworkAccess(false);
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
-        if (!isSupported()) return;
-
         addRestrictBackgroundBlacklist(mUid);
         assertRestrictBackgroundChangedReceived(1);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -180,9 +165,8 @@
         assertsForegroundAlwaysHasNetworkAccess();
     }
 
+    @Test
     public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
-        if (!isSupported()) return;
-
         final StringBuilder error = new StringBuilder();
         for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
             int uid = -1;
@@ -202,10 +186,10 @@
         }
     }
 
+    @RequiredProperties({NO_DATA_SAVER_MODE})
     @CddTest(requirement="7.4.7/C-2-2")
+    @Test
     public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
-        if (isSupported()) return;
-
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(0);
 
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
index e4189af..4306c99 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
@@ -16,15 +16,8 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
 public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
-
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setMeteredNetwork();
-    }
-
-    @Override
-    protected void tearDownMeteredNetwork() throws Exception {
-        resetMeteredNetwork();
-    }
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
index edbbb9e..1e89f15 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
@@ -16,10 +16,8 @@
 
 package com.android.cts.net.hostside;
 
-public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
 
-    @Override
-    protected boolean setUpActiveNetworkMeteringState() throws Exception {
-        return setUnmeteredNetwork();
-    }
+@RequiredProperties({NON_METERED_NETWORK})
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
new file mode 100644
index 0000000..cedd62a
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
+
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.compatibility.common.util.OnFailureRule;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class DumpOnFailureRule extends OnFailureRule {
+    private File mDumpDir = new File(Environment.getExternalStorageDirectory(),
+            "CtsHostsideNetworkTests");
+
+    @Override
+    public void onTestFailure(Statement base, Description description, Throwable throwable) {
+        final String testName = description.getClassName() + "_" + description.getMethodName();
+
+        if (throwable instanceof AssumptionViolatedException) {
+            Log.d(TAG, "Skipping test " + testName + ": " + throwable);
+            return;
+        }
+
+        prepareDumpRootDir();
+        final File dumpFile = new File(mDumpDir, "dump-" + testName);
+        Log.i(TAG, "Dumping debug info for " + description + ": " + dumpFile.getPath());
+        try (FileOutputStream out = new FileOutputStream(dumpFile)) {
+            for (String cmd : new String[] {
+                    "dumpsys netpolicy",
+                    "dumpsys network_management",
+                    "dumpsys usagestats " + TEST_PKG,
+                    "dumpsys usagestats appstandby",
+            }) {
+                dumpCommandOutput(out, cmd);
+            }
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Error opening file: " + dumpFile, e);
+        } catch (IOException e) {
+            Log.e(TAG, "Error closing file: " + dumpFile, e);
+        }
+    }
+
+    void dumpCommandOutput(FileOutputStream out, String cmd) {
+        final ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation().executeShellCommand(cmd);
+        try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            out.write(("Output of '" + cmd + "':\n").getBytes(StandardCharsets.UTF_8));
+            FileUtils.copy(in, out);
+            out.write("\n\n=================================================================\n\n"
+                    .getBytes(StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            Log.e(TAG, "Error dumping '" + cmd + "'", e);
+        }
+    }
+
+    void prepareDumpRootDir() {
+        if (!mDumpDir.exists() && !mDumpDir.mkdir()) {
+            Log.e(TAG, "Error creating " + mDumpDir);
+        }
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
new file mode 100644
index 0000000..8fadf9e
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.resetMeteredNetwork;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setupMeteredNetwork;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class MeterednessConfigurationRule extends BeforeAfterRule {
+    private Pair<String, Boolean> mSsidAndInitialMeteredness;
+
+    @Override
+    public void onBefore(Statement base, Description description) throws Throwable {
+        final ArraySet<Property> requiredProperties
+                = RequiredPropertiesRule.getRequiredProperties();
+        if (requiredProperties.contains(METERED_NETWORK)) {
+            configureNetworkMeteredness(true);
+        } else if (requiredProperties.contains(NON_METERED_NETWORK)) {
+            configureNetworkMeteredness(false);
+        }
+    }
+
+    @Override
+    public void onAfter(Statement base, Description description) throws Throwable {
+        resetNetworkMeteredness();
+    }
+
+    public void configureNetworkMeteredness(boolean metered) throws Exception {
+        mSsidAndInitialMeteredness = setupMeteredNetwork(metered);
+    }
+
+    public void resetNetworkMeteredness() throws Exception {
+        if (mSsidAndInitialMeteredness != null) {
+            resetMeteredNetwork(mSsidAndInitialMeteredness.first,
+                    mSsidAndInitialMeteredness.second);
+        }
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
index b1a2186..c9edda6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -15,9 +15,21 @@
  */
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
 import android.os.SystemClock;
 import android.util.Log;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 /**
  * Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
  * and Data Saver Mode) are applied simultaneously.
@@ -29,12 +41,10 @@
 public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
     private static final String TAG = "MixedModesTest";
 
-    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        if (!isSupported()) return;
-
         // Set initial state.
         removeRestrictBackgroundWhitelist(mUid);
         removeRestrictBackgroundBlacklist(mUid);
@@ -44,12 +54,10 @@
         registerBroadcastReceiver();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         super.tearDown();
 
-        if (!isSupported()) return;
-
         try {
             setRestrictBackground(false);
         } finally {
@@ -57,34 +65,15 @@
         }
     }
 
-    @Override
-    public boolean isSupported() throws Exception {
-        if (!isDozeModeEnabled()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Doze Mode");
-            return false;
-        }
-        return true;
-    }
-
     /**
      * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
      */
+    @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+    @Test
     public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) return;
-
-        Log.i(TAG, "testDataAndBatterySaverModes_meteredNetwork() tests");
-        if (!setMeteredNetwork()) {
-            Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
-                    + "device cannot use a metered network");
-            return;
-        }
-
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
             setRestrictBackground(true);
             setBatterySaverMode(true);
@@ -137,7 +126,7 @@
             removeRestrictBackgroundBlacklist(mUid);
             removePowerSaveModeWhitelist(TEST_APP2_PKG);
         } finally {
-            resetMeteredNetwork();
+            meterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
@@ -145,86 +134,75 @@
      * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
      * networks.
      */
+    @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, NON_METERED_NETWORK})
+    @Test
     public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(false);
+        try {
+            setRestrictBackground(true);
+            setBatterySaverMode(true);
+
+            Log.v(TAG, "Not whitelisted for any.");
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+
+            Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+            addRestrictBackgroundWhitelist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundWhitelist(mUid);
+
+            Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            removeRestrictBackgroundWhitelist(mUid);
+            assertBackgroundNetworkAccess(true);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(true);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+            Log.v(TAG, "Whitelisted for both.");
+            addRestrictBackgroundWhitelist(mUid);
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(true);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(true);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundWhitelist(mUid);
+
+            Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+            addRestrictBackgroundBlacklist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundBlacklist(mUid);
+
+            Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+            addRestrictBackgroundBlacklist(mUid);
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(true);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(true);
+            removeRestrictBackgroundBlacklist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
         }
-        if (!isSupported()) return;
-
-        if (!setUnmeteredNetwork()) {
-            Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because network"
-                    + " is metered");
-            return;
-        }
-        Log.i(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() tests");
-        setRestrictBackground(true);
-        setBatterySaverMode(true);
-
-        Log.v(TAG, "Not whitelisted for any.");
-        assertBackgroundNetworkAccess(false);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-
-        Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
-        addRestrictBackgroundWhitelist(mUid);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(false);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-        removeRestrictBackgroundWhitelist(mUid);
-
-        Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
-        addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        removeRestrictBackgroundWhitelist(mUid);
-        assertBackgroundNetworkAccess(true);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(true);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-
-        Log.v(TAG, "Whitelisted for both.");
-        addRestrictBackgroundWhitelist(mUid);
-        addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(true);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(true);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(false);
-        removeRestrictBackgroundWhitelist(mUid);
-
-        Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
-        addRestrictBackgroundBlacklist(mUid);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(false);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(false);
-        removeRestrictBackgroundBlacklist(mUid);
-
-        Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
-        addRestrictBackgroundBlacklist(mUid);
-        addPowerSaveModeWhitelist(TEST_APP2_PKG);
-        assertBackgroundNetworkAccess(true);
-        assertsForegroundAlwaysHasNetworkAccess();
-        assertBackgroundNetworkAccess(true);
-        removeRestrictBackgroundBlacklist(mUid);
-        removePowerSaveModeWhitelist(TEST_APP2_PKG);
     }
 
     /**
      * Tests that powersave whitelists works as expected when doze and battery saver modes
      * are enabled.
      */
+    @RequiredProperties({DOZE_MODE, BATTERY_SAVER_MODE})
+    @Test
     public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) {
-            return;
-        }
-
         setBatterySaverMode(true);
         setDozeMode(true);
 
@@ -250,11 +228,9 @@
      * Tests that powersave whitelists works as expected when doze and appIdle modes
      * are enabled.
      */
+    @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+    @Test
     public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -276,11 +252,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+    @Test
     public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -299,16 +273,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+    @Test
     public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) {
-            return;
-        }
-
         setBatterySaverMode(true);
         setAppIdle(true);
 
@@ -330,11 +297,9 @@
     /**
      * Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
      */
+    @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+    @Test
     public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -353,11 +318,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+    @Test
     public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
-        if (!isSupported()) {
-            return;
-        }
-
         setDozeMode(true);
         setAppIdle(true);
 
@@ -380,16 +343,9 @@
         }
     }
 
+    @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+    @Test
     public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
-        if (!isBatterySaverSupported()) {
-            Log.i(TAG, "Skipping " + getClass() + "." + getName()
-                    + "() because device does not support Battery saver mode");
-            return;
-        }
-        if (!isSupported()) {
-            return;
-        }
-
         setBatterySaverMode(true);
         setAppIdle(true);
 
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index 24dde9d..ed397b9 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,15 +16,26 @@
 
 package com.android.cts.net.hostside;
 
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
 import android.net.Network;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import java.util.Objects;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
 
-    private boolean mIsDataSaverSupported;
     private Network mNetwork;
     private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
 
@@ -132,108 +143,122 @@
         }
     }
 
-    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        mIsDataSaverSupported = isDataSaverSupported();
-
         mNetwork = mCm.getActiveNetwork();
 
-        // Set initial state.
-        setBatterySaverMode(false);
         registerBroadcastReceiver();
 
-        if (!mIsDataSaverSupported) return;
-        setRestrictBackground(false);
         removeRestrictBackgroundWhitelist(mUid);
         removeRestrictBackgroundBlacklist(mUid);
         assertRestrictBackgroundChangedReceived(0);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         super.tearDown();
 
-        if (!mIsDataSaverSupported) return;
+        setRestrictBackground(false);
+        setBatterySaverMode(false);
+    }
 
+    @RequiredProperties({DATA_SAVER_MODE})
+    @Test
+    public void testOnBlockedStatusChanged_dataSaver() throws Exception {
+        // Initial state
+        setBatterySaverMode(false);
+        setRestrictBackground(false);
+
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
-            resetMeteredNetwork();
+            // Register callback
+            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+            mTestNetworkCallback.expectAvailableCallback(mNetwork);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+            // Enable restrict background
+            setRestrictBackground(true);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+            // Add to whitelist
+            addRestrictBackgroundWhitelist(mUid);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+            // Remove from whitelist
+            removeRestrictBackgroundWhitelist(mUid);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
         } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
+        }
+
+        // Set to non-metered network
+        meterednessConfiguration.configureNetworkMeteredness(false);
+        try {
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+            // Disable restrict background, should not trigger callback
             setRestrictBackground(false);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.assertNoCallback();
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
-    public void testOnBlockedStatusChanged_data_saver() throws Exception {
-        if (!mIsDataSaverSupported) return;
-
-        // Prepare metered wifi
-        if (!setMeteredNetwork()) return;
-
-        // Register callback
-        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-        mTestNetworkCallback.expectAvailableCallback(mNetwork);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Enable restrict background
-        setRestrictBackground(true);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
-        // Add to whitelist
-        addRestrictBackgroundWhitelist(mUid);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Remove from whitelist
-        removeRestrictBackgroundWhitelist(mUid);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
-        // Set to non-metered network
-        setUnmeteredNetwork();
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Disable restrict background, should not trigger callback
+    @RequiredProperties({BATTERY_SAVER_MODE})
+    @Test
+    public void testOnBlockedStatusChanged_powerSaver() throws Exception {
+        // Set initial state.
+        setBatterySaverMode(false);
         setRestrictBackground(false);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.assertNoCallback();
-    }
 
+        final MeterednessConfigurationRule meterednessConfiguration
+                = new MeterednessConfigurationRule();
+        meterednessConfiguration.configureNetworkMeteredness(true);
+        try {
+            // Register callback
+            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+            mTestNetworkCallback.expectAvailableCallback(mNetwork);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
 
-    public void testOnBlockedStatusChanged_power_saver() throws Exception {
-        // Prepare metered wifi
-        if (!setMeteredNetwork()) return;
+            // Enable Power Saver
+            setBatterySaverMode(true);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
 
-        // Register callback
-        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-        mTestNetworkCallback.expectAvailableCallback(mNetwork);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
-        // Enable Power Saver
-        setBatterySaverMode(true);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
-        // Disable Power Saver
-        setBatterySaverMode(false);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            // Disable Power Saver
+            setBatterySaverMode(false);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
+        }
 
         // Set to non-metered network
-        setUnmeteredNetwork();
-        mTestNetworkCallback.assertNoCallback();
+        meterednessConfiguration.configureNetworkMeteredness(false);
+        try {
+            mTestNetworkCallback.assertNoCallback();
 
-        // Enable Power Saver
-        setBatterySaverMode(true);
-        assertBackgroundNetworkAccess(false);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            // Enable Power Saver
+            setBatterySaverMode(true);
+            assertBackgroundNetworkAccess(false);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
 
-        // Disable Power Saver
-        setBatterySaverMode(false);
-        assertBackgroundNetworkAccess(true);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            // Disable Power Saver
+            setBatterySaverMode(false);
+            assertBackgroundNetworkAccess(true);
+            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+        } finally {
+            meterednessConfiguration.resetNetworkMeteredness();
+        }
     }
 
     // TODO: 1. test against VPN lockdown.
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
new file mode 100644
index 0000000..ca2864c
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class NetworkPolicyTestUtils {
+
+    private static final int TIMEOUT_CHANGE_METEREDNESS_MS = 5000;
+
+    private static ConnectivityManager mCm;
+    private static WifiManager mWm;
+
+    private static Boolean mBatterySaverSupported;
+    private static Boolean mDataSaverSupported;
+    private static Boolean mDozeModeSupported;
+    private static Boolean mAppStandbySupported;
+
+    private NetworkPolicyTestUtils() {}
+
+    public static boolean isBatterySaverSupported() {
+        if (mBatterySaverSupported == null) {
+            mBatterySaverSupported = BatteryUtils.isBatterySaverSupported();
+        }
+        return mBatterySaverSupported;
+    }
+
+    /**
+     * As per CDD requirements, if the device doesn't support data saver mode then
+     * ConnectivityManager.getRestrictBackgroundStatus() will always return
+     * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
+     * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
+     * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
+     */
+    public static boolean isDataSaverSupported() {
+        if (mDataSaverSupported == null) {
+            assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+            try {
+                setRestrictBackground(true);
+                mDataSaverSupported = !isMyRestrictBackgroundStatus(
+                        RESTRICT_BACKGROUND_STATUS_DISABLED);
+            } finally {
+                setRestrictBackground(false);
+            }
+        }
+        return mDataSaverSupported;
+    }
+
+    public static boolean isDozeModeSupported() {
+        if (mDozeModeSupported == null) {
+            final String result = executeShellCommand("cmd deviceidle enabled deep");
+            mDozeModeSupported = result.equals("1");
+        }
+        return mDozeModeSupported;
+    }
+
+    public static boolean isAppStandbySupported() {
+        if (mAppStandbySupported == null) {
+            mAppStandbySupported = AppStandbyUtils.isAppStandbyEnabled();
+        }
+        return mAppStandbySupported;
+    }
+
+    public static boolean isLowRamDevice() {
+        final ActivityManager am = (ActivityManager) getContext().getSystemService(
+                Context.ACTIVITY_SERVICE);
+        return am.isLowRamDevice();
+    }
+
+    public static boolean isActiveNetworkMetered(boolean metered) {
+        return getConnectivityManager().isActiveNetworkMetered() == metered;
+    }
+
+    public static boolean canChangeActiveNetworkMeteredness() {
+        final Network activeNetwork = getConnectivityManager().getActiveNetwork();
+        final NetworkCapabilities networkCapabilities
+                = getConnectivityManager().getNetworkCapabilities(activeNetwork);
+        return networkCapabilities.hasTransport(TRANSPORT_WIFI);
+    }
+
+    public static Pair<String, Boolean> setupMeteredNetwork(boolean metered) throws Exception {
+        if (isActiveNetworkMetered(metered)) {
+            return null;
+        }
+        final String ssid = unquoteSSID(getWifiManager().getConnectionInfo().getSSID());
+        setWifiMeteredStatus(ssid, metered);
+        return Pair.create(ssid, !metered);
+    }
+
+    public static void resetMeteredNetwork(String ssid, boolean metered) throws Exception {
+        setWifiMeteredStatus(ssid, metered);
+    }
+
+    public static void setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
+        assertFalse("SSID should not be empty", TextUtils.isEmpty(ssid));
+        final String cmd = "cmd netpolicy set metered-network " + ssid + " " + metered;
+        executeShellCommand(cmd);
+        assertWifiMeteredStatus(ssid, metered);
+        assertActiveNetworkMetered(metered);
+    }
+
+    public static void assertWifiMeteredStatus(String ssid, boolean expectedMeteredStatus) {
+        final String result = executeShellCommand("cmd netpolicy list wifi-networks");
+        final String expectedLine = ssid + ";" + expectedMeteredStatus;
+        assertTrue("Expected line: " + expectedLine + "; Actual result: " + result,
+                result.contains(expectedLine));
+    }
+
+    // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+    public static void assertActiveNetworkMetered(boolean expectedMeteredStatus) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final NetworkCallback networkCallback = new NetworkCallback() {
+            @Override
+            public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+                final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+                if (metered == expectedMeteredStatus) {
+                    latch.countDown();
+                }
+            }
+        };
+        // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+        // with the current setting. Therefore, if the setting has already been changed,
+        // this method will return right away, and if not it will wait for the setting to change.
+        getConnectivityManager().registerDefaultNetworkCallback(networkCallback);
+        if (!latch.await(TIMEOUT_CHANGE_METEREDNESS_MS, TimeUnit.MILLISECONDS)) {
+            fail("Timed out waiting for active network metered status to change to "
+                    + expectedMeteredStatus + " ; network = "
+                    + getConnectivityManager().getActiveNetwork());
+        }
+        getConnectivityManager().unregisterNetworkCallback(networkCallback);
+    }
+
+    public static void setRestrictBackground(boolean enabled) {
+        executeShellCommand("cmd netpolicy set restrict-background " + enabled);
+        final String output = executeShellCommand("cmd netpolicy get restrict-background");
+        final String expectedSuffix = enabled ? "enabled" : "disabled";
+        assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+                output.endsWith(expectedSuffix));
+    }
+
+    public static boolean isMyRestrictBackgroundStatus(int expectedStatus) {
+        final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+        if (expectedStatus != actualStatus) {
+            Log.d(TAG, "MyRestrictBackgroundStatus: "
+                    + "Expected: " + restrictBackgroundValueToString(expectedStatus)
+                    + "; Actual: " + restrictBackgroundValueToString(actualStatus));
+            return false;
+        }
+        return true;
+    }
+
+    // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+    private static String unquoteSSID(String ssid) {
+        // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
+        // Otherwise it's guaranteed not to start with a quote.
+        if (ssid.charAt(0) == '"') {
+            return ssid.substring(1, ssid.length() - 1);
+        } else {
+            return ssid;
+        }
+    }
+
+    public static String restrictBackgroundValueToString(int status) {
+        switch (status) {
+            case RESTRICT_BACKGROUND_STATUS_DISABLED:
+                return "DISABLED";
+            case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+                return "WHITELISTED";
+            case RESTRICT_BACKGROUND_STATUS_ENABLED:
+                return "ENABLED";
+            default:
+                return "UNKNOWN_STATUS_" + status;
+        }
+    }
+
+    public static String executeShellCommand(String command) {
+        final String result = runShellCommand(command).trim();
+        Log.d(TAG, "Output of '" + command + "': '" + result + "'");
+        return result;
+    }
+
+    public static void assertMyRestrictBackgroundStatus(int expectedStatus) {
+        final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+        assertEquals(restrictBackgroundValueToString(expectedStatus),
+                restrictBackgroundValueToString(actualStatus));
+    }
+
+    public static ConnectivityManager getConnectivityManager() {
+        if (mCm == null) {
+            mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        }
+        return mCm;
+    }
+
+    public static WifiManager getWifiManager() {
+        if (mWm == null) {
+            mWm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        }
+        return mWm;
+    }
+
+    public static Context getContext() {
+        return getInstrumentation().getContext();
+    }
+
+    public static Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java
new file mode 100644
index 0000000..18805f9
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isLowRamDevice;
+
+public enum Property {
+    BATTERY_SAVER_MODE(1 << 0) {
+        public boolean isSupported() { return isBatterySaverSupported(); }
+    },
+
+    DATA_SAVER_MODE(1 << 1) {
+        public boolean isSupported() { return isDataSaverSupported(); }
+    },
+
+    NO_DATA_SAVER_MODE(~DATA_SAVER_MODE.getValue()) {
+        public boolean isSupported() { return !isDataSaverSupported(); }
+    },
+
+    DOZE_MODE(1 << 2) {
+        public boolean isSupported() { return isDozeModeSupported(); }
+    },
+
+    APP_STANDBY_MODE(1 << 3) {
+        public boolean isSupported() { return isAppStandbySupported(); }
+    },
+
+    NOT_LOW_RAM_DEVICE(1 << 4) {
+        public boolean isSupported() { return !isLowRamDevice(); }
+    },
+
+    METERED_NETWORK(1 << 5) {
+        public boolean isSupported() {
+            return isActiveNetworkMetered(true) || canChangeActiveNetworkMeteredness();
+        }
+    },
+
+    NON_METERED_NETWORK(~METERED_NETWORK.getValue()) {
+        public boolean isSupported() {
+            return isActiveNetworkMetered(false) || canChangeActiveNetworkMeteredness();
+        }
+    };
+
+    private int mValue;
+
+    Property(int value) { mValue = value; }
+
+    public int getValue() { return mValue; }
+
+    abstract boolean isSupported();
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java
new file mode 100644
index 0000000..96838bb
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({METHOD, TYPE})
+@Inherited
+public @interface RequiredProperties {
+    Property[] value();
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
new file mode 100644
index 0000000..1e33320
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.Assume;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class RequiredPropertiesRule extends BeforeAfterRule {
+
+    private static ArraySet<Property> mRequiredProperties;
+
+    @Override
+    public void onBefore(Statement base, Description description) {
+        mRequiredProperties = getAllRequiredProperties(description);
+
+        final String testName = description.getClassName() + "#" + description.getMethodName();
+        assertTestIsValid(testName, mRequiredProperties);
+        Log.i(TAG, "Running test " + testName + " with required properties: "
+                + propertiesToString(mRequiredProperties));
+    }
+
+    private ArraySet<Property> getAllRequiredProperties(Description description) {
+        final ArraySet<Property> allRequiredProperties = new ArraySet<>();
+        RequiredProperties requiredProperties = description.getAnnotation(RequiredProperties.class);
+        if (requiredProperties != null) {
+            Collections.addAll(allRequiredProperties, requiredProperties.value());
+        }
+
+        for (Class<?> clazz = description.getTestClass();
+                clazz != null; clazz = clazz.getSuperclass()) {
+            requiredProperties = clazz.getDeclaredAnnotation(RequiredProperties.class);
+            if (requiredProperties == null) {
+                continue;
+            }
+            for (Property requiredProperty : requiredProperties.value()) {
+                if (!allRequiredProperties.contains(~requiredProperty.getValue())) {
+                    allRequiredProperties.add(requiredProperty);
+                }
+            }
+        }
+        return allRequiredProperties;
+    }
+
+    private void assertTestIsValid(String testName, ArraySet<Property> requiredProperies) {
+        if (requiredProperies == null) {
+            return;
+        }
+        final ArrayList<Property> unsupportedProperties = new ArrayList<>();
+        for (Property property : requiredProperies) {
+            if (!property.isSupported()) {
+                unsupportedProperties.add(property);
+            }
+        }
+        Assume.assumeTrue("Unsupported properties: "
+                + propertiesToString(unsupportedProperties), unsupportedProperties.isEmpty());
+    }
+
+    public static ArraySet<Property> getRequiredProperties() {
+        return mRequiredProperties;
+    }
+
+    private static String propertiesToString(Iterable<Property> properties) {
+        return "[" + TextUtils.join(",", properties) + "]";
+    }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
index 8d6c4ac..1312085 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
@@ -29,14 +29,14 @@
         uninstallPackage(TEST_APP2_PKG, true);
     }
 
-    public void testOnBlockedStatusChanged_data_saver() throws Exception {
+    public void testOnBlockedStatusChanged_dataSaver() throws Exception {
         runDeviceTests(TEST_PKG,
-                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_data_saver");
+                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_dataSaver");
     }
 
-    public void testOnBlockedStatusChanged_power_saver() throws Exception {
+    public void testOnBlockedStatusChanged_powerSaver() throws Exception {
         runDeviceTests(TEST_PKG,
-                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_power_saver");
+                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver");
     }
 }
 
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index a2443b3..ce20379 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -79,7 +79,8 @@
     protected void installPackage(String apk) throws FileNotFoundException,
             DeviceNotAvailableException {
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
-        assertNull(getDevice().installPackage(buildHelper.getTestFile(apk), false));
+        assertNull(getDevice().installPackage(buildHelper.getTestFile(apk),
+                false /* reinstall */, true /* grantPermissions */));
     }
 
     protected void uninstallPackage(String packageName, boolean shouldSucceed)