1 /*
<lambda>null2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.net.cts
17 
18 import android.app.Instrumentation
19 import android.content.Context
20 import android.net.ConnectivityManager
21 import android.net.KeepalivePacketData
22 import android.net.LinkAddress
23 import android.net.LinkProperties
24 import android.net.Network
25 import android.net.NetworkAgent
26 import android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER
27 import android.net.NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT
28 import android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER
29 import android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS
30 import android.net.NetworkAgent.CMD_SAVE_ACCEPT_UNVALIDATED
31 import android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE
32 import android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE
33 import android.net.NetworkAgent.INVALID_NETWORK
34 import android.net.NetworkAgent.VALID_NETWORK
35 import android.net.NetworkAgentConfig
36 import android.net.NetworkCapabilities
37 import android.net.NetworkProvider
38 import android.net.NetworkRequest
39 import android.net.SocketKeepalive
40 import android.net.StringNetworkSpecifier
41 import android.net.Uri
42 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
43 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
44 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
45 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
46 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
47 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
48 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated
49 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
50 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
51 import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnValidationStatus
52 import android.os.Build
53 import android.os.Bundle
54 import android.os.Handler
55 import android.os.HandlerThread
56 import android.os.Looper
57 import android.os.Message
58 import android.os.Messenger
59 import androidx.test.InstrumentationRegistry
60 import androidx.test.runner.AndroidJUnit4
61 import com.android.internal.util.AsyncChannel
62 import com.android.net.module.util.ArrayTrackRecord
63 import com.android.testutils.DevSdkIgnoreRule
64 import com.android.testutils.RecorderCallback.CallbackEntry.Available
65 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
66 import com.android.testutils.TestableNetworkCallback
67 import org.junit.After
68 import org.junit.Assert.assertArrayEquals
69 import org.junit.Assert.fail
70 import org.junit.Before
71 import org.junit.Rule
72 import org.junit.Test
73 import org.junit.runner.RunWith
74 import java.net.InetAddress
75 import java.time.Duration
76 import java.util.UUID
77 import kotlin.test.assertEquals
78 import kotlin.test.assertFailsWith
79 import kotlin.test.assertFalse
80 import kotlin.test.assertNotNull
81 import kotlin.test.assertNull
82 import kotlin.test.assertTrue
83 
84 // This test doesn't really have a constraint on how fast the methods should return. If it's
85 // going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
86 // without affecting the run time of successful runs. Thus, set a very high timeout.
87 private const val DEFAULT_TIMEOUT_MS = 5000L
88 // When waiting for a NetworkCallback to determine there was no timeout, waiting is the
89 // only possible thing (the relevant handler is the one in the real ConnectivityService,
90 // and then there is the Binder call), so have a short timeout for this as it will be
91 // exhausted every time.
92 private const val NO_CALLBACK_TIMEOUT = 200L
93 // Any legal score (0~99) for the test network would do, as it is going to be kept up by the
94 // requests filed by the test and should never match normal internet requests. 70 is the default
95 // score of Ethernet networks, it's as good a value as any other.
96 private const val TEST_NETWORK_SCORE = 70
97 private const val BETTER_NETWORK_SCORE = 75
98 private const val FAKE_NET_ID = 1098
99 private val instrumentation: Instrumentation
100     get() = InstrumentationRegistry.getInstrumentation()
101 private val context: Context
102     get() = InstrumentationRegistry.getContext()
103 private fun Message(what: Int, arg1: Int, arg2: Int, obj: Any?) = Message.obtain().also {
104     it.what = what
105     it.arg1 = arg1
106     it.arg2 = arg2
107     it.obj = obj
108 }
109 
110 @RunWith(AndroidJUnit4::class)
111 class NetworkAgentTest {
112     @Rule @JvmField
113     val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
114 
115     private val LOCAL_IPV4_ADDRESS = InetAddress.parseNumericAddress("192.0.2.1")
116     private val REMOTE_IPV4_ADDRESS = InetAddress.parseNumericAddress("192.0.2.2")
117 
118     private val mCM = context.getSystemService(ConnectivityManager::class.java)
119     private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
<lambda>null120     private val mFakeConnectivityService by lazy { FakeConnectivityService(mHandlerThread.looper) }
121 
122     private class Provider(context: Context, looper: Looper) :
123             NetworkProvider(context, looper, "NetworkAgentTest NetworkProvider")
124 
125     private val agentsToCleanUp = mutableListOf<NetworkAgent>()
126     private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
127 
128     @Before
setUpnull129     fun setUp() {
130         instrumentation.getUiAutomation().adoptShellPermissionIdentity()
131         mHandlerThread.start()
132     }
133 
134     @After
tearDownnull135     fun tearDown() {
136         agentsToCleanUp.forEach { it.unregister() }
137         callbacksToCleanUp.forEach { mCM.unregisterNetworkCallback(it) }
138         mHandlerThread.quitSafely()
139         instrumentation.getUiAutomation().dropShellPermissionIdentity()
140     }
141 
142     /**
143      * A fake that helps simulating ConnectivityService talking to a harnessed agent.
144      * This fake only supports speaking to one harnessed agent at a time because it
145      * only keeps track of one async channel.
146      */
147     private class FakeConnectivityService(looper: Looper) {
148         private val CMD_EXPECT_DISCONNECT = 1
149         private var disconnectExpected = false
150         private val msgHistory = ArrayTrackRecord<Message>().newReadHead()
151         private val asyncChannel = AsyncChannel()
152         private val handler = object : Handler(looper) {
handleMessagenull153             override fun handleMessage(msg: Message) {
154                 msgHistory.add(Message.obtain(msg)) // make a copy as the original will be recycled
155                 when (msg.what) {
156                     CMD_EXPECT_DISCONNECT -> disconnectExpected = true
157                     AsyncChannel.CMD_CHANNEL_HALF_CONNECTED ->
158                         asyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION)
159                     AsyncChannel.CMD_CHANNEL_DISCONNECTED ->
160                         if (!disconnectExpected) {
161                             fail("Agent unexpectedly disconnected")
162                         } else {
163                             disconnectExpected = false
164                         }
165                 }
166             }
167         }
168 
connectnull169         fun connect(agentMsngr: Messenger) = asyncChannel.connect(context, handler, agentMsngr)
170 
171         fun disconnect() = asyncChannel.disconnect()
172 
173         fun sendMessage(what: Int, arg1: Int = 0, arg2: Int = 0, obj: Any? = null) =
174             asyncChannel.sendMessage(Message(what, arg1, arg2, obj))
175 
176         fun expectMessage(what: Int) =
177             assertNotNull(msgHistory.poll(DEFAULT_TIMEOUT_MS) { it.what == what })
178 
willExpectDisconnectOncenull179         fun willExpectDisconnectOnce() = handler.sendEmptyMessage(CMD_EXPECT_DISCONNECT)
180     }
181 
182     private open class TestableNetworkAgent(
183         looper: Looper,
184         val nc: NetworkCapabilities,
185         val lp: LinkProperties,
186         conf: NetworkAgentConfig
187     ) : NetworkAgent(context, looper, TestableNetworkAgent::class.java.simpleName /* tag */,
188             nc, lp, TEST_NETWORK_SCORE, conf, Provider(context, looper)) {
189         private val history = ArrayTrackRecord<CallbackEntry>().newReadHead()
190 
191         sealed class CallbackEntry {
192             object OnBandwidthUpdateRequested : CallbackEntry()
193             object OnNetworkUnwanted : CallbackEntry()
194             data class OnAddKeepalivePacketFilter(
195                 val slot: Int,
196                 val packet: KeepalivePacketData
197             ) : CallbackEntry()
198             data class OnRemoveKeepalivePacketFilter(val slot: Int) : CallbackEntry()
199             data class OnStartSocketKeepalive(
200                 val slot: Int,
201                 val interval: Int,
202                 val packet: KeepalivePacketData
203             ) : CallbackEntry()
204             data class OnStopSocketKeepalive(val slot: Int) : CallbackEntry()
205             data class OnSaveAcceptUnvalidated(val accept: Boolean) : CallbackEntry()
206             object OnAutomaticReconnectDisabled : CallbackEntry()
207             data class OnValidationStatus(val status: Int, val uri: Uri?) : CallbackEntry()
208             data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
209         }
210 
211         fun getName(): String? = (nc.getNetworkSpecifier() as? StringNetworkSpecifier)?.specifier
212 
213         override fun onBandwidthUpdateRequested() {
214             history.add(OnBandwidthUpdateRequested)
215         }
216 
217         override fun onNetworkUnwanted() {
218             history.add(OnNetworkUnwanted)
219         }
220 
221         override fun onAddKeepalivePacketFilter(slot: Int, packet: KeepalivePacketData) {
222             history.add(OnAddKeepalivePacketFilter(slot, packet))
223         }
224 
225         override fun onRemoveKeepalivePacketFilter(slot: Int) {
226             history.add(OnRemoveKeepalivePacketFilter(slot))
227         }
228 
229         override fun onStartSocketKeepalive(
230             slot: Int,
231             interval: Duration,
232             packet: KeepalivePacketData
233         ) {
234             history.add(OnStartSocketKeepalive(slot, interval.seconds.toInt(), packet))
235         }
236 
237         override fun onStopSocketKeepalive(slot: Int) {
238             history.add(OnStopSocketKeepalive(slot))
239         }
240 
241         override fun onSaveAcceptUnvalidated(accept: Boolean) {
242             history.add(OnSaveAcceptUnvalidated(accept))
243         }
244 
245         override fun onAutomaticReconnectDisabled() {
246             history.add(OnAutomaticReconnectDisabled)
247         }
248 
249         override fun onSignalStrengthThresholdsUpdated(thresholds: IntArray) {
250             history.add(OnSignalStrengthThresholdsUpdated(thresholds))
251         }
252 
253         fun expectEmptySignalStrengths() {
254             expectCallback<OnSignalStrengthThresholdsUpdated>().let {
255                 // intArrayOf() without arguments makes an empty array
256                 assertArrayEquals(intArrayOf(), it.thresholds)
257             }
258         }
259 
260         override fun onValidationStatus(status: Int, uri: Uri?) {
261             history.add(OnValidationStatus(status, uri))
262         }
263 
264         // Expects the initial validation event that always occurs immediately after registering
265         // a NetworkAgent whose network does not require validation (which test networks do
266         // not, since they lack the INTERNET capability). It always contains the default argument
267         // for the URI.
268         fun expectNoInternetValidationStatus() = expectCallback<OnValidationStatus>().let {
269             assertEquals(it.status, VALID_NETWORK)
270             // The returned Uri is parsed from the empty string, which means it's an
271             // instance of the (private) Uri.StringUri. There are no real good ways
272             // to check this, the least bad is to just convert it to a string and
273             // make sure it's empty.
274             assertEquals("", it.uri.toString())
275         }
276 
277         inline fun <reified T : CallbackEntry> expectCallback(): T {
278             val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
279             assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
280             return foundCallback
281         }
282 
283         fun assertNoCallback() {
284             assertTrue(waitForIdle(DEFAULT_TIMEOUT_MS),
285                     "Handler didn't became idle after ${DEFAULT_TIMEOUT_MS}ms")
286             assertNull(history.peek())
287         }
288     }
289 
requestNetworknull290     private fun requestNetwork(request: NetworkRequest, callback: TestableNetworkCallback) {
291         mCM.requestNetwork(request, callback)
292         callbacksToCleanUp.add(callback)
293     }
294 
registerNetworkCallbacknull295     private fun registerNetworkCallback(
296         request: NetworkRequest,
297         callback: TestableNetworkCallback
298     ) {
299         mCM.registerNetworkCallback(request, callback)
300         callbacksToCleanUp.add(callback)
301     }
302 
createNetworkAgentnull303     private fun createNetworkAgent(name: String? = null): TestableNetworkAgent {
304         val nc = NetworkCapabilities().apply {
305             addTransportType(NetworkCapabilities.TRANSPORT_TEST)
306             removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
307             removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
308             addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
309             addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
310             addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
311             if (null != name) {
312                 setNetworkSpecifier(StringNetworkSpecifier(name))
313             }
314         }
315         val lp = LinkProperties().apply {
316             addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 0))
317         }
318         val config = NetworkAgentConfig.Builder().build()
319         return TestableNetworkAgent(mHandlerThread.looper, nc, lp, config).also {
320             agentsToCleanUp.add(it)
321         }
322     }
323 
createConnectedNetworkAgentnull324     private fun createConnectedNetworkAgent(name: String? = null):
325             Pair<TestableNetworkAgent, TestableNetworkCallback> {
326         val request: NetworkRequest = NetworkRequest.Builder()
327                 .clearCapabilities()
328                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
329                 .build()
330         val callback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
331         requestNetwork(request, callback)
332         val agent = createNetworkAgent(name)
333         agent.register()
334         agent.markConnected()
335         return agent to callback
336     }
337 
<lambda>null338     private fun createNetworkAgentWithFakeCS() = createNetworkAgent().also {
339         mFakeConnectivityService.connect(it.registerForTest(Network(FAKE_NET_ID)))
340     }
341 
342     @Test
testConnectAndUnregisternull343     fun testConnectAndUnregister() {
344         val (agent, callback) = createConnectedNetworkAgent()
345         callback.expectAvailableThenValidatedCallbacks(agent.network)
346         agent.expectEmptySignalStrengths()
347         agent.expectNoInternetValidationStatus()
348         agent.unregister()
349         callback.expectCallback<Lost>(agent.network)
350         agent.expectCallback<OnNetworkUnwanted>()
351         assertFailsWith<IllegalStateException>("Must not be able to register an agent twice") {
352             agent.register()
353         }
354     }
355 
356     @Test
testOnBandwidthUpdateRequestednull357     fun testOnBandwidthUpdateRequested() {
358         val (agent, callback) = createConnectedNetworkAgent()
359         callback.expectAvailableThenValidatedCallbacks(agent.network)
360         agent.expectEmptySignalStrengths()
361         agent.expectNoInternetValidationStatus()
362         mCM.requestBandwidthUpdate(agent.network)
363         agent.expectCallback<OnBandwidthUpdateRequested>()
364         agent.unregister()
365     }
366 
367     @Test
testSignalStrengthThresholdsnull368     fun testSignalStrengthThresholds() {
369         val thresholds = intArrayOf(30, 50, 65)
370         val callbacks = thresholds.map { strength ->
371             val request = NetworkRequest.Builder()
372                     .clearCapabilities()
373                     .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
374                     .setSignalStrength(strength)
375                     .build()
376             TestableNetworkCallback(DEFAULT_TIMEOUT_MS).also {
377                 registerNetworkCallback(request, it)
378             }
379         }
380         createConnectedNetworkAgent().let { (agent, callback) ->
381             callback.expectAvailableThenValidatedCallbacks(agent.network)
382             agent.expectCallback<OnSignalStrengthThresholdsUpdated>().let {
383                 assertArrayEquals(it.thresholds, thresholds)
384             }
385             agent.expectNoInternetValidationStatus()
386 
387             // Send signal strength and check that the callbacks are called appropriately.
388             val nc = NetworkCapabilities(agent.nc)
389             nc.setSignalStrength(20)
390             agent.sendNetworkCapabilities(nc)
391             callbacks.forEach { it.assertNoCallback(NO_CALLBACK_TIMEOUT) }
392 
393             nc.setSignalStrength(40)
394             agent.sendNetworkCapabilities(nc)
395             callbacks[0].expectAvailableCallbacks(agent.network)
396             callbacks[1].assertNoCallback(NO_CALLBACK_TIMEOUT)
397             callbacks[2].assertNoCallback(NO_CALLBACK_TIMEOUT)
398 
399             nc.setSignalStrength(80)
400             agent.sendNetworkCapabilities(nc)
401             callbacks[0].expectCapabilitiesThat(agent.network) { it.signalStrength == 80 }
402             callbacks[1].expectAvailableCallbacks(agent.network)
403             callbacks[2].expectAvailableCallbacks(agent.network)
404 
405             nc.setSignalStrength(55)
406             agent.sendNetworkCapabilities(nc)
407             callbacks[0].expectCapabilitiesThat(agent.network) { it.signalStrength == 55 }
408             callbacks[1].expectCapabilitiesThat(agent.network) { it.signalStrength == 55 }
409             callbacks[2].expectCallback<Lost>(agent.network)
410         }
411         callbacks.forEach {
412             mCM.unregisterNetworkCallback(it)
413         }
414     }
415 
416     @Test
agentnull417     fun testSocketKeepalive(): Unit = createNetworkAgentWithFakeCS().let { agent ->
418         val packet = object : KeepalivePacketData(
419                 LOCAL_IPV4_ADDRESS /* srcAddress */, 1234 /* srcPort */,
420                 REMOTE_IPV4_ADDRESS /* dstAddress */, 4567 /* dstPort */,
421                 ByteArray(100 /* size */) { it.toByte() /* init */ }) {}
422         val slot = 4
423         val interval = 37
424 
425         mFakeConnectivityService.sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER,
426                 arg1 = slot, obj = packet)
427         mFakeConnectivityService.sendMessage(CMD_START_SOCKET_KEEPALIVE,
428                 arg1 = slot, arg2 = interval, obj = packet)
429 
430         agent.expectCallback<OnAddKeepalivePacketFilter>().let {
431             assertEquals(it.slot, slot)
432             assertEquals(it.packet, packet)
433         }
434         agent.expectCallback<OnStartSocketKeepalive>().let {
435             assertEquals(it.slot, slot)
436             assertEquals(it.interval, interval)
437             assertEquals(it.packet, packet)
438         }
439 
440         agent.assertNoCallback()
441 
442         // Check that when the agent sends a keepalive event, ConnectivityService receives the
443         // expected message.
444         agent.sendSocketKeepaliveEvent(slot, SocketKeepalive.ERROR_UNSUPPORTED)
445         mFakeConnectivityService.expectMessage(NetworkAgent.EVENT_SOCKET_KEEPALIVE).let() {
446             assertEquals(slot, it.arg1)
447             assertEquals(SocketKeepalive.ERROR_UNSUPPORTED, it.arg2)
448         }
449 
450         mFakeConnectivityService.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, arg1 = slot)
451         mFakeConnectivityService.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, arg1 = slot)
452         agent.expectCallback<OnStopSocketKeepalive>().let {
453             assertEquals(it.slot, slot)
454         }
455         agent.expectCallback<OnRemoveKeepalivePacketFilter>().let {
456             assertEquals(it.slot, slot)
457         }
458     }
459 
460     @Test
agentnull461     fun testSendUpdates(): Unit = createConnectedNetworkAgent().let { (agent, callback) ->
462         callback.expectAvailableThenValidatedCallbacks(agent.network)
463         agent.expectEmptySignalStrengths()
464         agent.expectNoInternetValidationStatus()
465         val ifaceName = "adhocIface"
466         val lp = LinkProperties(agent.lp)
467         lp.setInterfaceName(ifaceName)
468         agent.sendLinkProperties(lp)
469         callback.expectLinkPropertiesThat(agent.network) {
470             it.getInterfaceName() == ifaceName
471         }
472         val nc = NetworkCapabilities(agent.nc)
473         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
474         agent.sendNetworkCapabilities(nc)
475         callback.expectCapabilitiesThat(agent.network) {
476             it.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
477         }
478     }
479 
480     @Test
testSendScorenull481     fun testSendScore() {
482         // This test will create two networks and check that the one with the stronger
483         // score wins out for a request that matches them both.
484         // First create requests to make sure both networks are kept up, using the
485         // specifier so they are specific to each network
486         val name1 = UUID.randomUUID().toString()
487         val name2 = UUID.randomUUID().toString()
488         val request1 = NetworkRequest.Builder()
489                 .clearCapabilities()
490                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
491                 .setNetworkSpecifier(StringNetworkSpecifier(name1))
492                 .build()
493         val request2 = NetworkRequest.Builder()
494                 .clearCapabilities()
495                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
496                 .setNetworkSpecifier(StringNetworkSpecifier(name2))
497                 .build()
498         val callback1 = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
499         val callback2 = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
500         requestNetwork(request1, callback1)
501         requestNetwork(request2, callback2)
502 
503         // Then file the interesting request
504         val request = NetworkRequest.Builder()
505                 .clearCapabilities()
506                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
507                 .build()
508         val callback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
509         requestNetwork(request, callback)
510 
511         // Connect the first Network
512         createConnectedNetworkAgent(name1).let { (agent1, _) ->
513             callback.expectAvailableThenValidatedCallbacks(agent1.network)
514             // Upgrade agent1 to a better score so that there is no ambiguity when
515             // agent2 connects that agent1 is still better
516             agent1.sendNetworkScore(BETTER_NETWORK_SCORE - 1)
517             // Connect the second agent
518             createConnectedNetworkAgent(name2).let { (agent2, _) ->
519                 agent2.markConnected()
520                 // The callback should not see anything yet
521                 callback.assertNoCallback(NO_CALLBACK_TIMEOUT)
522                 // Now update the score and expect the callback now prefers agent2
523                 agent2.sendNetworkScore(BETTER_NETWORK_SCORE)
524                 callback.expectCallback<Available>(agent2.network)
525             }
526         }
527 
528         // tearDown() will unregister the requests and agents
529     }
530 
531     @Test
testSetAcceptUnvalidatednull532     fun testSetAcceptUnvalidated() {
533         createNetworkAgentWithFakeCS().let { agent ->
534             mFakeConnectivityService.sendMessage(CMD_SAVE_ACCEPT_UNVALIDATED, 1)
535             agent.expectCallback<OnSaveAcceptUnvalidated>().let {
536                 assertTrue(it.accept)
537             }
538             agent.assertNoCallback()
539         }
540     }
541 
542     @Test
testSetAcceptUnvalidatedPreventAutomaticReconnectnull543     fun testSetAcceptUnvalidatedPreventAutomaticReconnect() {
544         createNetworkAgentWithFakeCS().let { agent ->
545             mFakeConnectivityService.sendMessage(CMD_SAVE_ACCEPT_UNVALIDATED, 0)
546             mFakeConnectivityService.sendMessage(CMD_PREVENT_AUTOMATIC_RECONNECT)
547             agent.expectCallback<OnSaveAcceptUnvalidated>().let {
548                 assertFalse(it.accept)
549             }
550             agent.expectCallback<OnAutomaticReconnectDisabled>()
551             agent.assertNoCallback()
552             // When automatic reconnect is turned off, the network is torn down and
553             // ConnectivityService sends a disconnect. This in turn causes the agent
554             // to send a DISCONNECTED message to CS.
555             mFakeConnectivityService.willExpectDisconnectOnce()
556             mFakeConnectivityService.disconnect()
557             mFakeConnectivityService.expectMessage(AsyncChannel.CMD_CHANNEL_DISCONNECTED)
558             agent.expectCallback<OnNetworkUnwanted>()
559         }
560     }
561 
562     @Test
testPreventAutomaticReconnectnull563     fun testPreventAutomaticReconnect() {
564         createNetworkAgentWithFakeCS().let { agent ->
565             mFakeConnectivityService.sendMessage(CMD_PREVENT_AUTOMATIC_RECONNECT)
566             agent.expectCallback<OnAutomaticReconnectDisabled>()
567             agent.assertNoCallback()
568             mFakeConnectivityService.willExpectDisconnectOnce()
569             mFakeConnectivityService.disconnect()
570             mFakeConnectivityService.expectMessage(AsyncChannel.CMD_CHANNEL_DISCONNECTED)
571             agent.expectCallback<OnNetworkUnwanted>()
572         }
573     }
574 
575     @Test
agentnull576     fun testValidationStatus() = createNetworkAgentWithFakeCS().let { agent ->
577         val uri = Uri.parse("http://www.google.com")
578         val bundle = Bundle().apply {
579             putString(NetworkAgent.REDIRECT_URL_KEY, uri.toString())
580         }
581         mFakeConnectivityService.sendMessage(CMD_REPORT_NETWORK_STATUS,
582                 arg1 = VALID_NETWORK, obj = bundle)
583         agent.expectCallback<OnValidationStatus>().let {
584             assertEquals(it.status, VALID_NETWORK)
585             assertEquals(it.uri, uri)
586         }
587 
588         mFakeConnectivityService.sendMessage(CMD_REPORT_NETWORK_STATUS,
589                 arg1 = INVALID_NETWORK, obj = Bundle())
590         agent.expectCallback<OnValidationStatus>().let {
591             assertEquals(it.status, INVALID_NETWORK)
592             assertNull(it.uri)
593         }
594     }
595 
596     @Test
testTemporarilyUnmeteredCapabilitynull597     fun testTemporarilyUnmeteredCapability() {
598         // This test will create a networks with/without NET_CAPABILITY_TEMPORARILY_NOT_METERED
599         // and check that the callback reflects the capability changes.
600         // First create a request to make sure the network is kept up
601         val request1 = NetworkRequest.Builder()
602                 .clearCapabilities()
603                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
604                 .build()
605         val callback1 = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS).also {
606             registerNetworkCallback(request1, it)
607         }
608         requestNetwork(request1, callback1)
609 
610         // Then file the interesting request
611         val request = NetworkRequest.Builder()
612                 .clearCapabilities()
613                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
614                 .build()
615         val callback = TestableNetworkCallback(timeoutMs = DEFAULT_TIMEOUT_MS)
616         requestNetwork(request, callback)
617 
618         // Connect the network
619         createConnectedNetworkAgent().let { (agent, _) ->
620             callback.expectAvailableThenValidatedCallbacks(agent.network)
621 
622             // Send TEMP_NOT_METERED and check that the callback is called appropriately.
623             val nc1 = NetworkCapabilities(agent.nc)
624                     .addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
625             agent.sendNetworkCapabilities(nc1)
626             callback.expectCapabilitiesThat(agent.network) {
627                 it.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
628             }
629 
630             // Remove TEMP_NOT_METERED and check that the callback is called appropriately.
631             val nc2 = NetworkCapabilities(agent.nc)
632                     .removeCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
633             agent.sendNetworkCapabilities(nc2)
634             callback.expectCapabilitiesThat(agent.network) {
635                 !it.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
636             }
637         }
638 
639         // tearDown() will unregister the requests and agents
640     }
641 }
642