Merge "Disable non-thread local network matching" into main
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 11442ed..d47f4b3 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -17,6 +17,8 @@
 package android.net;
 
 import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.LOG_COMPAT_CHANGE;
+import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -29,6 +31,7 @@
 import static android.net.TetheringTester.buildUdpPacket;
 import static android.net.TetheringTester.isExpectedIcmpPacket;
 import static android.net.TetheringTester.isExpectedUdpDnsPacket;
+import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS;
 import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
 import static android.system.OsConstants.ICMP_ECHO;
 import static android.system.OsConstants.ICMP_ECHOREPLY;
@@ -52,6 +55,7 @@
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
+import android.app.compat.CompatChanges;
 import android.content.Context;
 import android.net.TetheringManager.TetheringRequest;
 import android.net.TetheringTester.TetheredDevice;
@@ -1244,6 +1248,11 @@
     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     @Test
     public void testLocalAgent_networkCallbacks() throws Exception {
+        final boolean isMatchNonThreadLocalNetworksEnabled = runAsShell(
+                READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE,
+                () -> CompatChanges.isChangeEnabled(ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS));
+        assumeTrue(isMatchNonThreadLocalNetworksEnabled);
+
         mDeviceConfigRule.setConfig(NAMESPACE_TETHERING, TETHERING_LOCAL_NETWORK_AGENT, "1");
         assumeFalse(isInterfaceForTetheringAvailable());
         setIncludeTestInterfaces(true);
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 8355d31..1d9c235 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -360,8 +360,7 @@
         mUnderlyingNetworks = null;
         mEnterpriseId = 0;
         mReservationId = RES_ID_UNSET;
-        // TODO: Change to default disabled when introduce this filtering.
-        mMatchNonThreadLocalNetworks = true;
+        mMatchNonThreadLocalNetworks = false;
     }
 
     /**
@@ -2961,8 +2960,7 @@
      * Flag to control whether a NetworkRequest can match non-thread local networks.
      * @hide
      */
-    // TODO: Change to default disabled when introduce this filtering.
-    private boolean mMatchNonThreadLocalNetworks = true;
+    private boolean mMatchNonThreadLocalNetworks;
 
     /**
      * Returns the match non-thread local networks flag.
diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
index 3b2520e..acf8e85 100644
--- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
+++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
@@ -148,6 +148,17 @@
     @EnabledAfter(targetSdkVersion = 36)
     public static final long RESTRICT_LOCAL_NETWORK = 365139289L;
 
+    /**
+     * Enable match non-threads local networks.
+     *
+     * Apps targeting a release after V can have NetworkRequests matches non-thread local networks.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public static final long ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS = 349487600L;
+
     private ConnectivityCompatChanges() {
     }
 }
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index b9b590b..e0cd279 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -121,6 +121,7 @@
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST;
 import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
 import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_LOCAL_NETWORK;
+import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS;
 import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_SELF_CERTIFIED_CAPABILITIES_DECLARATION;
 import static android.net.connectivity.ConnectivityCompatChanges.NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION;
 import static android.os.Process.INVALID_UID;
@@ -1473,6 +1474,10 @@
             return SdkLevel.isAtLeastV();
         }
 
+        public boolean isAtLeastB() {
+            return SdkLevel.isAtLeastB();
+        }
+
         /**
          * Get system properties to use in ConnectivityService.
          */
@@ -3186,6 +3191,11 @@
     }
 
     private void maybeDisableLocalNetworkMatching(NetworkCapabilities nc, int callingUid) {
+        // If disabled, NetworkRequest cannot match non-thread local networks even if
+        // specified explicitly. Compat change is enabled by default on apps targeting B+.
+        // Agent should not be visible on U- even if it's rolled out.
+        nc.setMatchNonThreadLocalNetworks(mDeps.isAtLeastV() && mDeps.isChangeEnabled(
+                ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS, callingUid));
         if (mDeps.isChangeEnabled(ENABLE_MATCH_LOCAL_NETWORK, callingUid)) {
             return;
         }
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
index 4aeae19..c0965b4 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
@@ -19,7 +19,7 @@
 import android.net.LocalNetworkConfig
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkCapabilities.TRANSPORT_THREAD
 import android.net.NetworkRequest
 import android.net.NetworkScore
 import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
@@ -42,7 +42,7 @@
     fun testKeepConnectedLocalAgent() {
         deps.setBuildSdk(VERSION_V)
         val nc = NetworkCapabilities.Builder()
-                .addTransportType(TRANSPORT_WIFI)
+                .addTransportType(TRANSPORT_THREAD)
                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
                 .build()
         val keepConnectedAgent = Agent(
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
index 6805d9a..6dc9d2d 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
@@ -18,14 +18,11 @@
 
 import android.content.pm.PackageManager.FEATURE_LEANBACK
 import android.net.INetd
-import android.net.LocalNetworkConfig
 import android.net.NativeNetworkConfig
 import android.net.NativeNetworkType
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
 import android.net.NetworkRequest
-import android.net.NetworkScore
-import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
 import android.net.VpnManager
 import android.os.Build
 import androidx.test.filters.SmallTest
@@ -46,11 +43,6 @@
 private const val TIMEOUT_MS = 2_000L
 private const val NO_CALLBACK_TIMEOUT_MS = 200L
 
-private fun keepConnectedScore() =
-        FromS(NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build())
-
-private fun defaultLnc() = FromS(LocalNetworkConfig.Builder().build())
-
 @DevSdkIgnoreRunner.MonitorThreadLeak
 @RunWith(DevSdkIgnoreRunner::class)
 @SmallTest
@@ -103,7 +95,7 @@
         }
         cm.registerNetworkCallback(request.build(), allNetworksCb)
         val ncTemplate = NetworkCapabilities.Builder().run {
-            addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+            addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
             addCapability(NET_CAPABILITY_LOCAL_NETWORK)
         }.build()
         val localAgent = if (params.sdkLevel >= VERSION_V ||
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
index d835155..9468d54 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
@@ -39,6 +39,7 @@
 import android.net.NetworkScore.KEEP_CONNECTED_LOCAL_NETWORK
 import android.net.RouteInfo
 import android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_LOCAL_NETWORK
+import android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS
 import android.os.Build
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRunner
@@ -129,6 +130,7 @@
     @Test
     fun testStructuralConstraintViolation() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
 
         val cb = TestableNetworkCallback()
         cm.requestNetwork(
@@ -162,6 +164,7 @@
     @Test
     fun testUpdateLocalAgentConfig() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
 
         val cb = TestableNetworkCallback()
         cm.requestNetwork(
@@ -428,6 +431,7 @@
     @Test
     fun testMulticastRoutingConfig_UpstreamSelectorCellToWifi() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
         val cb = TestableNetworkCallback()
         cm.registerNetworkCallback(
                 NetworkRequest.Builder().clearCapabilities()
@@ -500,6 +504,7 @@
     @Test
     fun testMulticastRoutingConfig_UpstreamSelectorWifiToNull() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
         val cb = TestableNetworkCallback()
         cm.registerNetworkCallback(
                 NetworkRequest.Builder().clearCapabilities()
@@ -577,6 +582,7 @@
 
     fun doTestUnregisterUpstreamAfterReplacement(sameIfaceName: Boolean) {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
         val cb = TestableNetworkCallback()
         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
 
@@ -657,6 +663,7 @@
     @Test
     fun testUnregisterUpstreamAfterReplacement_neverReplaced() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
         val cb = TestableNetworkCallback()
         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
 
@@ -710,6 +717,7 @@
     @Test
     fun testUnregisterLocalAgentAfterReplacement() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
 
         val localCb = TestableNetworkCallback()
         cm.requestNetwork(
@@ -775,6 +783,7 @@
     @Test
     fun testDestroyedNetworkAsSelectedUpstream() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
         val cb = TestableNetworkCallback()
         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
 
@@ -827,6 +836,7 @@
     @Test
     fun testForwardingRules() {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
         // Set up a local agent that should forward its traffic to the best DUN upstream.
         val lnc = FromS(
                 LocalNetworkConfig.Builder()
@@ -965,6 +975,7 @@
 
     fun doTestLocalNetworkUnwanted(haveUpstream: Boolean) {
         deps.setBuildSdk(VERSION_V)
+        deps.setChangeIdEnabled(true, ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)
 
         val nr = NetworkRequest.Builder().addCapability(NET_CAPABILITY_LOCAL_NETWORK).build()
         val requestCb = TestableNetworkCallback()
@@ -1075,4 +1086,86 @@
                 expectCallback = true
         )
     }
+
+    @Test
+    fun testNonThreadLocalAgentMatches_disabled() {
+        doTestNonThreadLocalAgentMatches(
+                VERSION_V,
+                enableMatchNonThreadLocalNetworks = false,
+                TRANSPORT_WIFI,
+                expectAvailable = false
+        )
+        doTestNonThreadLocalAgentMatches(
+                VERSION_V,
+                enableMatchNonThreadLocalNetworks = false,
+                TRANSPORT_THREAD,
+                expectAvailable = true
+        )
+        doTestNonThreadLocalAgentMatches(
+                VERSION_B,
+                enableMatchNonThreadLocalNetworks = false,
+                TRANSPORT_WIFI,
+                expectAvailable = false
+        )
+        doTestNonThreadLocalAgentMatches(
+                VERSION_B,
+                enableMatchNonThreadLocalNetworks = false,
+                TRANSPORT_THREAD,
+                expectAvailable = true
+        )
+    }
+
+    @Test
+    fun testNonThreadLocalAgentMatches_enabled() {
+        doTestNonThreadLocalAgentMatches(
+                VERSION_V,
+                enableMatchNonThreadLocalNetworks = true,
+                TRANSPORT_WIFI,
+                expectAvailable = true
+        )
+        doTestNonThreadLocalAgentMatches(
+                VERSION_V,
+                enableMatchNonThreadLocalNetworks = true,
+                TRANSPORT_THREAD,
+                expectAvailable = true
+        )
+        doTestNonThreadLocalAgentMatches(
+                VERSION_B,
+                enableMatchNonThreadLocalNetworks = true,
+                TRANSPORT_WIFI,
+                expectAvailable = true
+        )
+        doTestNonThreadLocalAgentMatches(
+                VERSION_B,
+                enableMatchNonThreadLocalNetworks = true,
+                TRANSPORT_THREAD,
+                expectAvailable = true
+        )
+    }
+
+    private fun doTestNonThreadLocalAgentMatches(
+            sdkLevel: Int,
+            enableMatchNonThreadLocalNetworks: Boolean,
+            transport: Int,
+            expectAvailable: Boolean
+    ) {
+        deps.setBuildSdk(sdkLevel)
+        deps.setChangeIdEnabled(
+                enableMatchNonThreadLocalNetworks,
+                ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS
+        )
+
+        val localNcTemplate = NetworkCapabilities.Builder().run {
+            addTransportType(transport)
+            addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+        }.build()
+        val localAgent = Agent(
+                nc = localNcTemplate,
+                score = keepConnectedScore(),
+                lnc = defaultLnc()
+        )
+        // The implementation inside connect() will expect the OnAvailable.
+        localAgent.connect(expectAvailable)
+        localAgent.disconnect(expectAvailable)
+    }
 }
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSNetworkRequestStateStatsMetricsTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSNetworkRequestStateStatsMetricsTests.kt
index 35f8ae5..e6a69c3 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSNetworkRequestStateStatsMetricsTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSNetworkRequestStateStatsMetricsTests.kt
@@ -20,6 +20,7 @@
 import android.net.NetworkRequest
 import android.os.Build
 import android.os.Process
+import android.text.TextUtils
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.TestableNetworkCallback
@@ -54,9 +55,17 @@
         cm.requestNetwork(CELL_INTERNET_NOT_METERED_NR, TestableNetworkCallback())
         waitForIdle()
 
+        // Some fields might be altered by the service, e.g. mMatchNonThreadLocalNetworks.
+        // Check immutable differences instead.
         verify(networkRequestStateStatsMetrics).onNetworkRequestReceived(
-                argThat{req -> req.networkCapabilities.equals(
-                        CELL_INTERNET_NOT_METERED_NR.networkCapabilities)})
+                argThat { req ->
+                    TextUtils.isEmpty(
+                            req.networkCapabilities.describeImmutableDifferences(
+                                    CELL_INTERNET_NOT_METERED_NR.networkCapabilities
+                            )
+                    )
+                }
+        )
     }
 
     @Test
@@ -77,7 +86,13 @@
         cm.unregisterNetworkCallback(cb)
         waitForIdle()
         verify(networkRequestStateStatsMetrics).onNetworkRequestRemoved(
-                argThat{req -> req.networkCapabilities.equals(
-                        CELL_INTERNET_NOT_METERED_NR.networkCapabilities)})
+                argThat { req ->
+                    TextUtils.isEmpty(
+                            req.networkCapabilities.describeImmutableDifferences(
+                                    CELL_INTERNET_NOT_METERED_NR.networkCapabilities
+                            )
+                    )
+                }
+        )
     }
 }
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt
index fc2a06c..6d1858e 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt
@@ -39,6 +39,7 @@
 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_THREAD
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.NetworkPolicyManager.NetworkPolicyCallback
 import android.net.NetworkRequest
@@ -356,6 +357,7 @@
         val localAgent = Agent(
             nc = defaultNc()
                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+                .addTransportType(TRANSPORT_THREAD)
                 .removeCapability(NET_CAPABILITY_INTERNET),
             lp = defaultLp().apply { interfaceName = "local42" },
             lnc = FromS(lnc)
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
index 6e07ac6..a77daa8 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
@@ -137,7 +137,7 @@
         nmCallbacks.notifyNetworkTestedWithExtras(p)
     }
 
-    fun connect() {
+    fun connect(expectAvailable: Boolean = true) {
         val mgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
         val request = NetworkRequest.Builder().apply {
             clearCapabilities()
@@ -149,26 +149,30 @@
         val cb = TestableNetworkCallback()
         mgr.registerNetworkCallback(request, cb)
         agent.markConnected()
-        if (null == cb.poll { it is Available && agent.network == it.network }) {
-            if (!nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) &&
-                    nc.hasTransport(TRANSPORT_CELLULAR)) {
-                // ConnectivityService adds NOT_SUSPENDED by default to all non-cell agents. An
-                // agent without NOT_SUSPENDED will not connect, instead going into the SUSPENDED
-                // state, so this call will not terminate.
-                // Instead of forcefully adding NOT_SUSPENDED to all agents like older tools did,
-                // it's better to let the developer manage it as they see fit but help them
-                // debug if they forget.
-                fail(
-                    "Could not connect the agent. Did you forget to add " +
-                        "NET_CAPABILITY_NOT_SUSPENDED ?"
-                )
+        if (expectAvailable) {
+            if (null == cb.poll { it is Available && agent.network == it.network }) {
+                if (!nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) &&
+                        nc.hasTransport(TRANSPORT_CELLULAR)) {
+                    // ConnectivityService adds NOT_SUSPENDED by default to all non-cell agents. An
+                    // agent without NOT_SUSPENDED will not connect, instead going into the
+                    // SUSPENDED state, so this call will not terminate.
+                    // Instead of forcefully adding NOT_SUSPENDED to all agents like older tools did,
+                    // it's better to let the developer manage it as they see fit but help them
+                    // debug if they forget.
+                    fail(
+                        "Could not connect the agent. Did you forget to add " +
+                            "NET_CAPABILITY_NOT_SUSPENDED ?"
+                    )
+                }
+                fail("Could not connect the agent. Instrumentation failure ?")
             }
-            fail("Could not connect the agent. Instrumentation failure ?")
+        } else {
+            cb.assertNoCallback()
         }
         mgr.unregisterNetworkCallback(cb)
     }
 
-    fun disconnect() {
+    fun disconnect(expectAvailable: Boolean = true) {
         val mgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
         val request = NetworkRequest.Builder().apply {
             clearCapabilities()
@@ -179,9 +183,14 @@
         }.build()
         val cb = TestableNetworkCallback(timeoutMs = SHORT_TIMEOUT_MS)
         mgr.registerNetworkCallback(request, cb)
-        cb.eventuallyExpect<Available> { it.network == agent.network }
-        agent.unregister()
-        cb.eventuallyExpect<Lost> { it.network == agent.network }
+        if (expectAvailable) {
+            cb.eventuallyExpect<Available> { it.network == agent.network }
+            agent.unregister()
+            cb.eventuallyExpect<Lost> { it.network == agent.network }
+        } else {
+            agent.unregister()
+            cb.assertNoCallback()
+        }
     }
 
     fun setTeardownDelayMillis(delayMillis: Int) = agent.setTeardownDelayMillis(delayMillis)
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 48333c5..0fe61ec 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -112,7 +112,8 @@
 internal const val VERSION_T = 3
 internal const val VERSION_U = 4
 internal const val VERSION_V = 5
-internal const val VERSION_MAX = VERSION_V
+internal const val VERSION_B = 6
+internal const val VERSION_MAX = VERSION_B
 
 internal const val CALLING_UID_UNMOCKED = Process.INVALID_UID
 
@@ -388,6 +389,7 @@
         override fun isAtLeastT() = if (isSdkUnmocked) super.isAtLeastT() else sdkLevel >= VERSION_T
         override fun isAtLeastU() = if (isSdkUnmocked) super.isAtLeastU() else sdkLevel >= VERSION_U
         override fun isAtLeastV() = if (isSdkUnmocked) super.isAtLeastV() else sdkLevel >= VERSION_V
+        override fun isAtLeastB() = if (isSdkUnmocked) super.isAtLeastB() else sdkLevel >= VERSION_B
 
         private var callingUid = CALLING_UID_UNMOCKED
 
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
index 5e18843..f28f063 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
@@ -34,6 +34,7 @@
 import android.net.IpPrefix
 import android.net.LinkAddress
 import android.net.LinkProperties
+import android.net.LocalNetworkConfig
 import android.net.NetworkAgentConfig
 import android.net.NetworkCapabilities
 import android.net.NetworkScore
@@ -84,6 +85,11 @@
 
 internal fun defaultScore() = FromS(NetworkScore.Builder().build())
 
+internal fun keepConnectedScore() = FromS(NetworkScore.Builder()
+                .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_TEST).build())
+
+internal fun defaultLnc() = FromS(LocalNetworkConfig.Builder().build())
+
 internal fun defaultLp() = LinkProperties().apply {
     addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
     addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))