Add HostsideVpnTests for testing setVpnDefaultForUids()

The test will check if the VPN will be the only default network
for the app after ConnectivityManager#setVpnDefaultForUids() is
called.

Bug: 231749077
Test: atest CtsHostsideNetworkTests:HostsideVpnTests
Change-Id: I02758ad7d948342797b6a4b00dfec3acdf44775d
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 8c18a89..a62ef8a 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -97,6 +97,7 @@
 import android.telephony.TelephonyManager;
 import android.test.MoreAsserts;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Range;
 
@@ -108,6 +109,7 @@
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
 import com.android.testutils.RecorderCallback;
+import com.android.testutils.RecorderCallback.CallbackEntry;
 import com.android.testutils.TestableNetworkCallback;
 
 import org.junit.After;
@@ -136,6 +138,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Random;
+import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -839,8 +842,13 @@
         callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED,
                 NETWORK_CALLBACK_TIMEOUT_MS,
                 entry -> (Objects.equals(expectUnderlyingNetworks,
-                        ((RecorderCallback.CallbackEntry.CapabilitiesChanged) entry)
-                                .getCaps().getUnderlyingNetworks())));
+                        entry.getCaps().getUnderlyingNetworks())));
+    }
+
+    private void expectVpnNetwork(TestableNetworkCallback callback) {
+        callback.eventuallyExpect(RecorderCallback.CallbackEntry.NETWORK_CAPS_UPDATED,
+                NETWORK_CALLBACK_TIMEOUT_MS,
+                entry -> entry.getCaps().hasTransport(TRANSPORT_VPN));
     }
 
     @Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
@@ -1622,7 +1630,7 @@
                 mCM.registerDefaultNetworkCallbackForUid(remoteUid, remoteUidCallback,
                         new Handler(Looper.getMainLooper()));
             }, NETWORK_SETTINGS);
-            remoteUidCallback.expectAvailableCallbacks(network);
+            remoteUidCallback.expectAvailableCallbacksWithBlockedReasonNone(network);
 
             // The remote UDP socket can receive packets coming from the TUN interface
             checkBlockIncomingPacket(tunFd, remoteUdpFd, EXPECT_PASS);
@@ -1666,6 +1674,56 @@
             });
     }
 
+    @Test
+    public void testSetVpnDefaultForUids() throws Exception {
+        assumeTrue(supportedHardware());
+        assumeTrue(SdkLevel.isAtLeastU());
+
+        final Network defaultNetwork = mCM.getActiveNetwork();
+        assertNotNull("There must be a default network", defaultNetwork);
+
+        final TestableNetworkCallback defaultNetworkCallback = new TestableNetworkCallback();
+        final String session = UUID.randomUUID().toString();
+        final int myUid = Process.myUid();
+
+        testAndCleanup(() -> {
+            mCM.registerDefaultNetworkCallback(defaultNetworkCallback);
+            defaultNetworkCallback.expectAvailableCallbacks(defaultNetwork);
+
+            final Range<Integer> myUidRange = new Range<>(myUid, myUid);
+            runWithShellPermissionIdentity(() -> {
+                mCM.setVpnDefaultForUids(session, List.of(myUidRange));
+            }, NETWORK_SETTINGS);
+
+            // The VPN will be the only default network for the app, so it's expected to receive
+            // onLost() callback.
+            defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST);
+
+            final ArrayList<Network> underlyingNetworks = new ArrayList<>();
+            underlyingNetworks.add(defaultNetwork);
+            startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"} /* addresses */,
+                    new String[] {"0.0.0.0/0", "::/0"} /* routes */,
+                    "" /* allowedApplications */, "" /* disallowedApplications */,
+                    null /* proxyInfo */, underlyingNetworks, false /* isAlwaysMetered */);
+
+            expectVpnNetwork(defaultNetworkCallback);
+        }, /* cleanup */ () -> {
+                stopVpn();
+                defaultNetworkCallback.eventuallyExpect(CallbackEntry.LOST);
+            }, /* cleanup */ () -> {
+                runWithShellPermissionIdentity(() -> {
+                    mCM.setVpnDefaultForUids(session, new ArraySet<>());
+                }, NETWORK_SETTINGS);
+                // The default network of the app will be changed back to wifi when the VPN network
+                // preference feature is disabled.
+                defaultNetworkCallback.eventuallyExpect(CallbackEntry.AVAILABLE,
+                        NETWORK_CALLBACK_TIMEOUT_MS,
+                        entry -> defaultNetwork.equals(entry.getNetwork()));
+            }, /* cleanup */ () -> {
+                mCM.unregisterNetworkCallback(defaultNetworkCallback);
+            });
+    }
+
     private ByteBuffer buildIpv4UdpPacket(final Inet4Address dstAddr, final Inet4Address srcAddr,
             final short dstPort, final short srcPort, final byte[] payload) throws IOException {
 
@@ -1756,7 +1814,7 @@
     }
 
     private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
-        public void expectAvailableCallbacks(Network network) {
+        public void expectAvailableCallbacksWithBlockedReasonNone(Network network) {
             super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
                     BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
         }
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 4d90a4a..10a2821 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -120,4 +120,8 @@
     public void testBlockIncomingPackets() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testBlockIncomingPackets");
     }
+
+    public void testSetVpnDefaultForUids() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testSetVpnDefaultForUids");
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 3093243..0e04a80 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -2379,7 +2379,7 @@
     }
 
     private class DetailedBlockedStatusCallback extends TestableNetworkCallback {
-        public void expectAvailableCallbacks(Network network) {
+        public void expectAvailableCallbacksWithBlockedReasonNone(Network network) {
             super.expectAvailableCallbacks(network, false /* suspended */, true /* validated */,
                     BLOCKED_REASON_NONE, NETWORK_CALLBACK_TIMEOUT_MS);
         }
@@ -2432,7 +2432,7 @@
         final List<DetailedBlockedStatusCallback> allCallbacks =
                 List.of(myUidCallback, otherUidCallback);
         for (DetailedBlockedStatusCallback callback : allCallbacks) {
-            callback.expectAvailableCallbacks(defaultNetwork);
+            callback.expectAvailableCallbacksWithBlockedReasonNone(defaultNetwork);
         }
 
         final Range<Integer> myUidRange = new Range<>(myUid, myUid);