CTS for default background network restrictions

Apps will now lose network access once they are in top-sleeping or
higher process state. Adding some cts tests to ensure correct behavior.

The tests are using temporarily using junit Assume to skip if the
feature flag is enabled to enable a following code migration.

Test: atest CtsHostsideNetworkTests

BYPASS_INCLUSIVE_LANGUAGE_REASON=Existing method

Bug: 304347838
Change-Id: Iba7c7bd01309fa7a301fdc1524033f0fad5ae512
diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp
index 2688fb8..f6c0430 100644
--- a/tests/cts/hostside/Android.bp
+++ b/tests/cts/hostside/Android.bp
@@ -27,7 +27,10 @@
     name: "CtsHostsideNetworkTests",
     defaults: ["cts_defaults"],
     // Only compile source java files in this apk.
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        ":ArgumentConstants",
+    ],
     libs: [
         "net-tests-utils-host-device-common",
         "cts-tradefed",
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index d555491..cf4afa9 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -36,7 +36,10 @@
         "android.test.runner",
         "android.test.base",
     ],
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+         ":ArgumentConstants",
+    ],
     // Tag this module as a cts test artifact
     test_suites: [
         "general-tests",
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java
new file mode 100644
index 0000000..8a3e790
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 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.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.os.SystemClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Base class for default, always-on network restrictions.
+ */
+abstract class AbstractDefaultRestrictionsTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Before
+    public final void setUp() throws Exception {
+        super.setUp();
+
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+
+        registerBroadcastReceiver();
+        assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+    }
+
+    @After
+    public final void tearDown() throws Exception {
+        super.tearDown();
+
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+    }
+
+    @Test
+    public void testFgsNetworkAccess() throws Exception {
+        assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+        SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+        assertNetworkAccess(false, null);
+
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
+    }
+
+    @Test
+    public void testActivityNetworkAccess() throws Exception {
+        assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+        SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+        assertNetworkAccess(false, null);
+
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+    }
+
+    @Test
+    public void testBackgroundNetworkAccess_inFullAllowlist() throws Exception {
+        assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+        SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+        assertNetworkAccess(false, null);
+
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+        assertNetworkAccess(true, null);
+    }
+
+    @Test
+    public void testBackgroundNetworkAccess_inExceptIdleAllowlist() throws Exception {
+        assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+        SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+        assertNetworkAccess(false, null);
+
+        addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+        assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+        assertNetworkAccess(true, null);
+    }
+}
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 29aac3c..2ca8832 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
@@ -23,6 +23,7 @@
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
 import static android.os.BatteryManager.BATTERY_PLUGGED_ANY;
 
+import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
@@ -65,11 +66,13 @@
 import android.util.Pair;
 
 import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.AmUtils;
 import com.android.compatibility.common.util.BatteryUtils;
 import com.android.compatibility.common.util.DeviceConfigStateHelper;
 import com.android.compatibility.common.util.ThrowingRunnable;
+import com.android.modules.utils.build.SdkLevel;
 
 import org.junit.Rule;
 import org.junit.rules.RuleChain;
@@ -90,6 +93,8 @@
 
     protected static final String TEST_PKG = "com.android.cts.net.hostside";
     protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
+    // TODO(b/321797685): Configure it via device-config once it is available.
+    protected static final long PROCESS_STATE_TRANSITION_DELAY_MS = TimeUnit.SECONDS.toMillis(5);
 
     private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity";
     private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
@@ -97,7 +102,6 @@
 
     private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(
             TEST_APP2_PKG, TEST_APP2_JOB_SERVICE_CLASS);
-
     private static final int TEST_JOB_ID = 7357437;
 
     private static final int SLEEP_TIME_SEC = 1;
@@ -152,8 +156,6 @@
     private static final IntentFilter BATTERY_CHANGED_FILTER =
             new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
 
-    private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg";
-
     protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 20_000; // 20 sec
 
     private static final long BROADCAST_TIMEOUT_MS = 5_000;
@@ -181,7 +183,16 @@
         mUid = getUid(TEST_APP2_PKG);
         mMyUid = getUid(mContext.getPackageName());
         mServiceClient = new MyServiceClient(mContext);
-        mServiceClient.bind();
+
+        final Bundle args = InstrumentationRegistry.getArguments();
+        final int bindPriorityFlags;
+        if (Boolean.valueOf(args.getString(ARG_WAIVE_BIND_PRIORITY, "false"))) {
+            bindPriorityFlags = Context.BIND_WAIVE_PRIORITY;
+        } else {
+            bindPriorityFlags = Context.BIND_NOT_FOREGROUND;
+        }
+        mServiceClient.bind(bindPriorityFlags);
+
         mPowerManager = mContext.getSystemService(PowerManager.class);
         executeShellCommand("cmd netpolicy start-watching " + mUid);
         // Some of the test cases assume that Data saver mode is initially disabled, which might not
@@ -205,6 +216,22 @@
         if (null != lock && lock.isHeld()) lock.release();
     }
 
+    /**
+     * Check if the feature blocking network for top_sleeping and lower priority proc-states is
+     * enabled. This is a manual check because the feature flag infrastructure may not be available
+     * in all the branches that will get this code.
+     * TODO: b/322115994 - Use @RequiresFlagsEnabled with
+     * Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE once the tests are moved to cts.
+     */
+    protected boolean isNetworkBlockedForTopSleepingAndAbove() {
+        if (!SdkLevel.isAtLeastV()) {
+            return false;
+        }
+        final String output = executeShellCommand("device_config get backstage_power"
+                + " com.android.server.net.network_blocked_for_top_sleeping_and_above");
+        return Boolean.parseBoolean(output);
+    }
+
     protected int getUid(String packageName) throws Exception {
         return mContext.getPackageManager().getPackageUid(packageName, 0);
     }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
index 4004789..c1d576d 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
@@ -18,6 +18,7 @@
 
 
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
 
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getUiDevice;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
@@ -28,8 +29,13 @@
 import static com.android.cts.net.hostside.Property.METERED_NETWORK;
 import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
 
+import static org.junit.Assume.assumeTrue;
+
+import android.os.SystemClock;
 import android.util.Log;
 
+import com.android.compatibility.common.util.ThrowingRunnable;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -63,14 +69,14 @@
     @RequiredProperties({BATTERY_SAVER_MODE})
     public void testStartActivity_batterySaver() throws Exception {
         setBatterySaverMode(true);
-        assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver");
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver", null);
     }
 
     @Test
     @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
     public void testStartActivity_dataSaver() throws Exception {
         setRestrictBackground(true);
-        assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver");
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver", null);
     }
 
     @Test
@@ -79,7 +85,7 @@
         setDozeMode(true);
         // TODO (235284115): We need to turn on Doze every time before starting
         // the activity.
-        assertLaunchedActivityHasNetworkAccess("testStartActivity_doze");
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_doze", null);
     }
 
     @Test
@@ -89,11 +95,24 @@
         setAppIdle(true);
         // TODO (235284115): We need to put the app into app standby mode every
         // time before starting the activity.
-        assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby");
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby", null);
     }
 
-    private void assertLaunchedActivityHasNetworkAccess(String testName) throws Exception {
+    @Test
+    public void testStartActivity_default() throws Exception {
+        assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+        assertLaunchedActivityHasNetworkAccess("testStartActivity_default", () -> {
+            assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+            SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+        });
+    }
+
+    private void assertLaunchedActivityHasNetworkAccess(String testName,
+            ThrowingRunnable onBeginIteration) throws Exception {
         for (int i = 0; i < TEST_ITERATION_COUNT; ++i) {
+            if (onBeginIteration != null) {
+                onBeginIteration.run();
+            }
             Log.i(TAG, testName + " start #" + i);
             launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
             getUiDevice().pressHome();
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java
new file mode 100644
index 0000000..f3a1026
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
+public class DefaultRestrictionsMeteredTest extends AbstractDefaultRestrictionsTest {
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java
new file mode 100644
index 0000000..5651dd0
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class DefaultRestrictionsNonMeteredTest extends AbstractDefaultRestrictionsTest {
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
index 93cc911..980ecd5 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -34,26 +34,30 @@
 
     private Context mContext;
     private ServiceConnection mServiceConnection;
-    private IMyService mService;
+    private volatile IMyService mService;
+    private final ConditionVariable mServiceCondition = new ConditionVariable();
 
     public MyServiceClient(Context context) {
         mContext = context;
     }
 
-    public void bind() {
+    /**
+     * Binds to a service in the test app to communicate state.
+     * @param bindPriorityFlags Flags to influence the process-state of the bound app.
+     */
+    public void bind(int bindPriorityFlags) {
         if (mService != null) {
             throw new IllegalStateException("Already bound");
         }
-
-        final ConditionVariable cv = new ConditionVariable();
         mServiceConnection = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
                 mService = IMyService.Stub.asInterface(service);
-                cv.open();
+                mServiceCondition.open();
             }
             @Override
             public void onServiceDisconnected(ComponentName name) {
+                mServiceCondition.close();
                 mService = null;
             }
         };
@@ -63,12 +67,8 @@
         // Needs to use BIND_NOT_FOREGROUND so app2 does not run in
         // the same process state as app
         mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE
-                | Context.BIND_NOT_FOREGROUND);
-        cv.block(TIMEOUT_MS);
-        if (mService == null) {
-            throw new IllegalStateException(
-                    "Could not bind to MyService service after " + TIMEOUT_MS + "ms");
-        }
+                | bindPriorityFlags);
+        ensureServiceConnection();
     }
 
     public void unbind() {
@@ -77,37 +77,56 @@
         }
     }
 
+    private void ensureServiceConnection() {
+        if (mService != null) {
+            return;
+        }
+        mServiceCondition.block(TIMEOUT_MS);
+        if (mService == null) {
+            throw new IllegalStateException(
+                    "Could not bind to MyService service after " + TIMEOUT_MS + "ms");
+        }
+    }
+
     public void registerBroadcastReceiver() throws RemoteException {
+        ensureServiceConnection();
         mService.registerBroadcastReceiver();
     }
 
     public int getCounters(String receiverName, String action) throws RemoteException {
+        ensureServiceConnection();
         return mService.getCounters(receiverName, action);
     }
 
     public String checkNetworkStatus() throws RemoteException {
+        ensureServiceConnection();
         return mService.checkNetworkStatus();
     }
 
     public String getRestrictBackgroundStatus() throws RemoteException {
+        ensureServiceConnection();
         return mService.getRestrictBackgroundStatus();
     }
 
     public void sendNotification(int notificationId, String notificationType)
             throws RemoteException {
+        ensureServiceConnection();
         mService.sendNotification(notificationId, notificationType);
     }
 
     public void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb)
             throws RemoteException {
+        ensureServiceConnection();
         mService.registerNetworkCallback(request, cb);
     }
 
     public void unregisterNetworkCallback() throws RemoteException {
+        ensureServiceConnection();
         mService.unregisterNetworkCallback();
     }
 
     public int scheduleJob(JobInfo jobInfo) throws RemoteException {
+        ensureServiceConnection();
         return mService.scheduleJob(jobInfo);
     }
 }
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 eb2347d..5552b8f 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
@@ -17,6 +17,7 @@
 package com.android.cts.net.hostside;
 
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
 
@@ -34,6 +35,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.cts.util.CtsNetUtils;
+import android.os.SystemClock;
 import android.util.Log;
 
 import com.android.modules.utils.build.SdkLevel;
@@ -43,6 +45,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -145,12 +148,22 @@
         public Network expectAvailableCallbackAndGetNetwork() {
             final CallbackInfo cb = nextCallback(TEST_CONNECT_TIMEOUT_MS);
             if (cb.state != CallbackState.AVAILABLE) {
-                fail("Network is not available. Instead obtained the following callback :"
-                        + cb);
+                fail("Network is not available. Instead obtained the following callback :" + cb);
             }
             return cb.network;
         }
 
+        public void drainAndWaitForIdle() {
+            try {
+                do {
+                    mCallbacks.drainTo(new ArrayList<>());
+                } while (mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS) != null);
+            } catch (InterruptedException ie) {
+                Log.e(TAG, "Interrupted while draining callback queue", ie);
+                Thread.currentThread().interrupt();
+            }
+        }
+
         public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
             expectCallback(CallbackState.BLOCKED_STATUS, expectedNetwork, expectBlocked);
         }
@@ -225,7 +238,7 @@
         // Check that the network is metered.
         mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
                 false /* hasCapability */, NET_CAPABILITY_NOT_METERED);
-        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+        mTestNetworkCallback.drainAndWaitForIdle();
 
         // Before Android T, DNS queries over private DNS should be but are not restricted by Power
         // Saver or Data Saver. The issue is fixed in mainline update and apps can no longer request
@@ -357,6 +370,58 @@
         }
     }
 
+    @Test
+    public void testOnBlockedStatusChanged_default() throws Exception {
+        assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+
+        try {
+            assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+            assertNetworkAccess(false, null);
+            assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
+
+            launchActivity();
+            assertTopState();
+            assertNetworkAccess(true, null);
+            mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+            assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */);
+
+            finishActivity();
+            assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+            SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+            assertNetworkAccess(false, null);
+            mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+            assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */);
+
+        } finally {
+            mMeterednessConfiguration.resetNetworkMeteredness();
+        }
+
+        // Set to non-metered network
+        mMeterednessConfiguration.configureNetworkMeteredness(false);
+        mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork,
+                true /* hasCapability */, NET_CAPABILITY_NOT_METERED);
+        try {
+            assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+            assertNetworkAccess(false, null);
+            assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
+
+            launchActivity();
+            assertTopState();
+            assertNetworkAccess(true, null);
+            mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false);
+            assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */);
+
+            finishActivity();
+            assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+            SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+            assertNetworkAccess(false, null);
+            mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true);
+            assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);
+        } finally {
+            mMeterednessConfiguration.resetNetworkMeteredness();
+        }
+    }
+
     // TODO: 1. test against VPN lockdown.
     //       2. test against multiple networks.
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
index 7aeca77..968e270 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
@@ -17,6 +17,7 @@
 package com.android.cts.net.hostside;
 
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.assertIsUidRestrictedOnMeteredNetworks;
@@ -28,6 +29,9 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.os.SystemClock;
 
 import org.junit.After;
 import org.junit.Before;
@@ -238,4 +242,33 @@
             assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */);
         }
     }
+
+    @Test
+    public void testIsUidNetworkingBlocked_whenInBackground() throws Exception {
+        assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove());
+
+        try {
+            assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+            SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+            assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
+            assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
+
+            launchActivity();
+            assertTopState();
+            assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */);
+            assertFalse(isUidNetworkingBlocked(mUid, NON_METERED));
+
+            finishActivity();
+            assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING);
+            SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS);
+            assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */);
+            assertTrue(isUidNetworkingBlocked(mUid, NON_METERED));
+
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */);
+            assertFalse(isUidNetworkingBlocked(mUid, NON_METERED));
+        } finally {
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        }
+    }
 }
diff --git a/tests/cts/hostside/instrumentation_arguments/Android.bp b/tests/cts/hostside/instrumentation_arguments/Android.bp
new file mode 100644
index 0000000..cdede36
--- /dev/null
+++ b/tests/cts/hostside/instrumentation_arguments/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2024 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+    name: "ArgumentConstants",
+    srcs: ["src/**/*.java"],
+}
diff --git a/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java b/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java
new file mode 100644
index 0000000..472e347
--- /dev/null
+++ b/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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.arguments;
+
+public interface InstrumentationArguments {
+    String ARG_WAIVE_BIND_PRIORITY = "waive_bind_priority";
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
index 849ac7c..880e826 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.net;
 
+import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
 import android.platform.test.annotations.FlakyTest;
 
 import com.android.testutils.SkipPresubmit;
@@ -26,9 +28,12 @@
 
 import org.junit.Test;
 
+import java.util.Map;
+
 @SkipPresubmit(reason = "Out of SLO flakiness")
 public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase {
     private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
+
     @BeforeClassWithInfo
     public static void setUpOnce(TestInformation testInfo) throws Exception {
         uninstallPackage(testInfo, TEST_APP2_PKG, false);
@@ -60,4 +65,11 @@
     public void testStartActivity_appStandby() throws Exception {
         runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby");
     }
+
+    // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
+    @Test
+    public void testStartActivity_default() throws Exception {
+        runDeviceTestsWithArgs(TEST_PKG, TEST_CLASS, "testStartActivity_default",
+                Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+    }
 }
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java
new file mode 100644
index 0000000..0d01fc1
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 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;
+
+import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
+import com.android.testutils.SkipPresubmit;
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side tests.
+@SkipPresubmit(reason = "Monitoring for flakiness")
+public class HostsideDefaultNetworkRestrictionsTests extends HostsideNetworkTestCase {
+    private static final String METERED_TEST_CLASS = TEST_PKG + ".DefaultRestrictionsMeteredTest";
+    private static final String NON_METERED_TEST_CLASS =
+            TEST_PKG + ".DefaultRestrictionsNonMeteredTest";
+
+    @Before
+    public void setUp() throws Exception {
+        uninstallPackage(TEST_APP2_PKG, false);
+        installPackage(TEST_APP2_APK);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        uninstallPackage(TEST_APP2_PKG, true);
+    }
+
+    private void runMeteredTest(String methodName) throws DeviceNotAvailableException {
+        runDeviceTestsWithArgs(TEST_PKG, METERED_TEST_CLASS, methodName,
+                Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+    }
+
+    private void runNonMeteredTest(String methodName) throws DeviceNotAvailableException {
+        runDeviceTestsWithArgs(TEST_PKG, NON_METERED_TEST_CLASS, methodName,
+                Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+    }
+
+    @Test
+    public void testMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess()
+            throws Exception {
+        runMeteredTest("testActivityNetworkAccess");
+    }
+
+    @Test
+    public void testMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess()
+            throws Exception {
+        runMeteredTest("testFgsNetworkAccess");
+    }
+
+    @Test
+    public void testMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception {
+        runMeteredTest("testBackgroundNetworkAccess_inFullAllowlist");
+    }
+
+    @Test
+    public void testMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist()
+            throws Exception {
+        runMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist");
+    }
+
+    @Test
+    public void testNonMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess()
+            throws Exception {
+        runNonMeteredTest("testActivityNetworkAccess");
+    }
+
+    @Test
+    public void testNonMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess()
+            throws Exception {
+        runNonMeteredTest("testFgsNetworkAccess");
+    }
+
+    @Test
+    public void testNonMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception {
+        runNonMeteredTest("testBackgroundNetworkAccess_inFullAllowlist");
+    }
+
+    @Test
+    public void testNonMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist()
+            throws Exception {
+        runNonMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist");
+    }
+}
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 04bd1ad..361f7c7 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
@@ -15,12 +15,16 @@
  */
 package com.android.cts.net;
 
+import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
 import com.android.testutils.SkipPresubmit;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Map;
+
 @SkipPresubmit(reason = "Out of SLO flakiness")
 public class HostsideNetworkCallbackTests extends HostsideNetworkTestCase {
 
@@ -46,5 +50,12 @@
         runDeviceTests(TEST_PKG,
                 TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver");
     }
+
+    // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
+    @Test
+    public void testOnBlockedStatusChanged_default() throws Exception {
+        runDeviceTestsWithArgs(TEST_PKG, TEST_PKG + ".NetworkCallbackTest",
+                "testOnBlockedStatusChanged_default", Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+    }
 }
 
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
index 3ddb88b..e97db58 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java
@@ -16,10 +16,14 @@
 
 package com.android.cts.net;
 
+import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Map;
+
 public class HostsideNetworkPolicyManagerTests extends HostsideNetworkTestCase {
     @Before
     public void setUp() throws Exception {
@@ -71,4 +75,12 @@
         runDeviceTests(TEST_PKG,
                 TEST_PKG + ".NetworkPolicyManagerTest", "testIsUidRestrictedOnMeteredNetworks");
     }
+
+    // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test.
+    @Test
+    public void testIsUidNetworkingBlocked_whenInBackground() throws Exception {
+        runDeviceTestsWithArgs(TEST_PKG, TEST_PKG + ".NetworkPolicyManagerTest",
+                "testIsUidNetworkingBlocked_whenInBackground",
+                Map.of(ARG_WAIVE_BIND_PRIORITY, "true"));
+    }
 }
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 3358fd7..ca95ed6 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -31,10 +31,13 @@
 import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
 import com.android.tradefed.util.RunUtil;
 
 import org.junit.runner.RunWith;
 
+import java.util.Map;
+
 @RunWith(DeviceJUnit4ClassRunner.class)
 abstract class HostsideNetworkTestCase extends BaseHostJUnit4Test {
     protected static final boolean DEBUG = false;
@@ -146,6 +149,17 @@
                 + packageName + ", u=" + currentUser);
     }
 
+    protected boolean runDeviceTestsWithArgs(String packageName, String className,
+            String methodName, Map<String, String> args) throws DeviceNotAvailableException {
+        final DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(packageName)
+                .setTestClassName(className)
+                .setTestMethodName(methodName);
+        for (Map.Entry<String, String> arg : args.entrySet()) {
+            deviceTestRunOptions.addInstrumentationArg(arg.getKey(), arg.getValue());
+        }
+        return runDeviceTests(deviceTestRunOptions);
+    }
+
     protected String runCommand(String command) throws DeviceNotAvailableException {
         Log.d(TAG, "Command: '" + command + "'");
         final String output = getDevice().executeShellCommand(command);