Merge "Remove framework-connectivity-annotations library" into sc-dev
diff --git a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
index 1611f9d..2fb5985 100644
--- a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
@@ -183,7 +183,7 @@
             return false;
         default:
             jniThrowExceptionFmt(env, "java/io/IOException",
-                                 "Unknown hardware address type %s on interface %s", rv,
+                                 "Unknown hardware address type %d on interface %s", rv,
                                  interface.c_str());
             return false;
     }
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 1c9aba1..8e2b310 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -306,6 +306,11 @@
             return foundCallback
         }
 
+        inline fun <reified T : CallbackEntry> eventuallyExpect() =
+                history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also {
+                    assertNotNull(it, "Callback ${T::class} not received")
+        } as T
+
         fun assertNoCallback() {
             assertTrue(waitForIdle(DEFAULT_TIMEOUT_MS),
                     "Handler didn't became idle after ${DEFAULT_TIMEOUT_MS}ms")
@@ -383,13 +388,12 @@
         callback.expectAvailableThenValidatedCallbacks(agent.network)
         agent.expectEmptySignalStrengths()
         agent.expectNoInternetValidationStatus()
-        agent.unregister()
+
+        unregister(agent)
         callback.expectCallback<Lost>(agent.network)
-        agent.expectCallback<OnNetworkUnwanted>()
         assertFailsWith<IllegalStateException>("Must not be able to register an agent twice") {
             agent.register()
         }
-        agent.expectCallback<OnNetworkDestroyed>()
     }
 
     @Test
@@ -400,7 +404,7 @@
         agent.expectNoInternetValidationStatus()
         mCM.requestBandwidthUpdate(agent.network)
         agent.expectCallback<OnBandwidthUpdateRequested>()
-        agent.unregister()
+        unregister(agent)
     }
 
     @Test
@@ -648,10 +652,16 @@
             }
         }
 
-        agent.unregister()
+        unregister(agent)
         callback.expectCallback<Lost>(agent.network)
     }
 
+    private fun unregister(agent: TestableNetworkAgent) {
+        agent.unregister()
+        agent.eventuallyExpect<OnNetworkUnwanted>()
+        agent.eventuallyExpect<OnNetworkDestroyed>()
+    }
+
     @Test
     @IgnoreUpTo(Build.VERSION_CODES.R)
     fun testAgentStartsInConnecting() {
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
new file mode 100644
index 0000000..8f17199
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 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 android.net.cts
+
+import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.LinkProperties
+import android.net.NetworkAgent
+import android.net.NetworkAgentConfig
+import android.net.NetworkCapabilities
+import android.net.NetworkProvider
+import android.net.NetworkRequest
+import android.net.NetworkScore
+import android.net.VpnManager
+import android.net.VpnTransportInfo
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import androidx.test.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.TestableNetworkCallback.HasNetwork
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// This test doesn't really have a constraint on how fast the methods should return. If it's
+// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
+// without affecting the run time of successful runs. Thus, set a very high timeout.
+private const val TIMEOUT_MS = 30_000L
+// When waiting for a NetworkCallback to determine there was no timeout, waiting is the
+// only possible thing (the relevant handler is the one in the real ConnectivityService,
+// and then there is the Binder call), so have a short timeout for this as it will be
+// exhausted every time.
+private const val NO_CALLBACK_TIMEOUT = 200L
+
+private val testContext: Context
+    get() = InstrumentationRegistry.getContext()
+
+private fun score(exiting: Boolean = false, primary: Boolean = false) =
+        NetworkScore.Builder().setExiting(exiting).setTransportPrimary(primary)
+                // TODO : have a constant KEEP_CONNECTED_FOR_TEST ?
+                .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER)
+                .build()
+
+@IgnoreUpTo(Build.VERSION_CODES.R)
+@RunWith(DevSdkIgnoreRunner::class)
+class NetworkScoreTest {
+    private val mCm = testContext.getSystemService(ConnectivityManager::class.java)
+    private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+    private val mHandler by lazy { Handler(mHandlerThread.looper) }
+    private val agentsToCleanUp = mutableListOf<NetworkAgent>()
+    private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
+
+    @Before
+    fun setUp() {
+        mHandlerThread.start()
+    }
+
+    @After
+    fun tearDown() {
+        agentsToCleanUp.forEach { it.unregister() }
+        mHandlerThread.quitSafely()
+        callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
+    }
+
+    // Returns a networkCallback that sends onAvailable on the best network with TRANSPORT_TEST.
+    private fun makeTestNetworkCallback() = TestableNetworkCallback(TIMEOUT_MS).also { cb ->
+        mCm.registerBestMatchingNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), cb, mHandler)
+        callbacksToCleanUp.add(cb)
+    }
+
+    // TestNetworkCallback is made to interact with a wrapper of NetworkAgent, because it's
+    // made for ConnectivityServiceTest.
+    // TODO : have TestNetworkCallback work for NetworkAgent too and remove this class.
+    private class AgentWrapper(val agent: NetworkAgent) : HasNetwork {
+        override val network = agent.network
+        fun sendNetworkScore(s: NetworkScore) = agent.sendNetworkScore(s)
+    }
+
+    private fun createTestNetworkAgent(
+            // The network always has TRANSPORT_TEST, plus optional transports
+        optionalTransports: IntArray = IntArray(size = 0),
+        everUserSelected: Boolean = false,
+        acceptUnvalidated: Boolean = false,
+        isExiting: Boolean = false,
+        isPrimary: Boolean = false
+    ): AgentWrapper {
+        val nc = NetworkCapabilities.Builder().apply {
+            addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+            optionalTransports.forEach { addTransportType(it) }
+            // Add capabilities that are common, just for realism. It's not strictly necessary
+            addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+            addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
+            // Remove capabilities that a test network agent shouldn't have and that are not
+            // needed for the purposes of this test.
+            removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+            removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+            removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+            if (optionalTransports.contains(NetworkCapabilities.TRANSPORT_VPN)) {
+                addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+                removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                setTransportInfo(VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, null))
+            }
+            addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+        }.build()
+        val config = NetworkAgentConfig.Builder()
+                .setExplicitlySelected(everUserSelected)
+                .setUnvalidatedConnectivityAcceptable(acceptUnvalidated)
+                .build()
+        val score = score(exiting = isExiting, primary = isPrimary)
+        val context = testContext
+        val looper = mHandlerThread.looper
+        val agent = object : NetworkAgent(context, looper, "NetworkScore test agent", nc,
+                LinkProperties(), score, config, NetworkProvider(context, looper,
+                "NetworkScore test provider")) {}.also {
+            agentsToCleanUp.add(it)
+        }
+        runWithShellPermissionIdentity({ agent.register() }, MANAGE_TEST_NETWORKS)
+        agent.markConnected()
+        return AgentWrapper(agent)
+    }
+
+    @Test
+    fun testExitingLosesAndOldSatisfierWins() {
+        val cb = makeTestNetworkCallback()
+        val agent1 = createTestNetworkAgent()
+        cb.expectAvailableThenValidatedCallbacks(agent1)
+        val agent2 = createTestNetworkAgent()
+        // Because the existing network must win, the callback stays on agent1.
+        cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+        agent1.sendNetworkScore(score(exiting = true))
+        // Now that agent1 is exiting, the callback is satisfied by agent2.
+        cb.expectAvailableCallbacks(agent2.network)
+        agent1.sendNetworkScore(score(exiting = false))
+        // Agent1 is no longer exiting, but agent2 is the current satisfier.
+        cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+    }
+
+    @Test
+    fun testVpnWins() {
+        val cb = makeTestNetworkCallback()
+        val agent1 = createTestNetworkAgent()
+        cb.expectAvailableThenValidatedCallbacks(agent1.network)
+        val agent2 = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_VPN))
+        // VPN wins out against agent1 even before it's validated (hence the "then validated",
+        // because it becomes the best network for this callback before it validates)
+        cb.expectAvailableThenValidatedCallbacks(agent2.network)
+    }
+
+    @Test
+    fun testEverUserSelectedAcceptUnvalidatedWins() {
+        val cb = makeTestNetworkCallback()
+        val agent1 = createTestNetworkAgent()
+        cb.expectAvailableThenValidatedCallbacks(agent1.network)
+        val agent2 = createTestNetworkAgent(everUserSelected = true, acceptUnvalidated = true)
+        // agent2 wins out against agent1 even before it's validated, because user-selected and
+        // accept unvalidated networks should win against even networks that are validated.
+        cb.expectAvailableThenValidatedCallbacks(agent2.network)
+    }
+
+    @Test
+    fun testPreferredTransportOrder() {
+        val cb = makeTestNetworkCallback()
+        val agentCell = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_CELLULAR))
+        cb.expectAvailableThenValidatedCallbacks(agentCell.network)
+        val agentWifi = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_WIFI))
+        // In the absence of other discriminating factors, agentWifi wins against agentCell because
+        // of its better transport, but only after it validates.
+        cb.expectAvailableDoubleValidatedCallbacks(agentWifi)
+        val agentEth = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_ETHERNET))
+        // Likewise, agentEth wins against agentWifi after validation because of its better
+        // transport.
+        cb.expectAvailableCallbacksValidated(agentEth)
+    }
+
+    @Test
+    fun testTransportPrimary() {
+        val cb = makeTestNetworkCallback()
+        val agent1 = createTestNetworkAgent()
+        cb.expectAvailableThenValidatedCallbacks(agent1)
+        val agent2 = createTestNetworkAgent()
+        // Because the existing network must win, the callback stays on agent1.
+        cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+        agent2.sendNetworkScore(score(primary = true))
+        // Now that agent2 is primary, the callback is satisfied by agent2.
+        cb.expectAvailableCallbacks(agent2.network)
+        agent1.sendNetworkScore(score(primary = true))
+        // Agent1 is primary too, but agent2 is the current satisfier
+        cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+        agent2.sendNetworkScore(score(primary = false))
+        // Now agent1 is primary and agent2 isn't
+        cb.expectAvailableCallbacks(agent1.network)
+    }
+
+    // TODO (b/187929636) : add a test making sure that validated networks win over unvalidated
+    // ones. Right now this is not possible because this CTS can't directly manipulate the
+    // validation state of a network.
+}