• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 
17 package com.android.server
18 
19 import android.net.IpPrefix
20 import android.net.LinkAddress
21 import android.net.LinkProperties
22 import android.net.LocalNetworkConfig
23 import android.net.MulticastRoutingConfig
24 import android.net.MulticastRoutingConfig.CONFIG_FORWARD_NONE
25 import android.net.NetworkCapabilities
26 import android.net.NetworkCapabilities.NET_CAPABILITY_DUN
27 import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
28 import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK
29 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
30 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
31 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
32 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
33 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
34 import android.net.NetworkCapabilities.TRANSPORT_THREAD
35 import android.net.NetworkCapabilities.TRANSPORT_WIFI
36 import android.net.NetworkRequest
37 import android.net.NetworkScore
38 import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
39 import android.net.NetworkScore.KEEP_CONNECTED_LOCAL_NETWORK
40 import android.net.RouteInfo
41 import android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_LOCAL_NETWORK
42 import android.os.Build
43 import com.android.testutils.DevSdkIgnoreRule
44 import com.android.testutils.DevSdkIgnoreRunner
45 import com.android.testutils.RecorderCallback.CallbackEntry.LocalInfoChanged
46 import com.android.testutils.RecorderCallback.CallbackEntry.Lost
47 import com.android.testutils.TestableNetworkCallback
48 import kotlin.test.assertFailsWith
49 import org.junit.Test
50 import org.junit.runner.RunWith
51 import org.mockito.ArgumentMatchers.any
52 import org.mockito.Mockito.clearInvocations
53 import org.mockito.Mockito.eq
54 import org.mockito.Mockito.inOrder
55 import org.mockito.Mockito.never
56 import org.mockito.Mockito.timeout
57 import org.mockito.Mockito.times
58 import org.mockito.Mockito.verify
59 import org.mockito.Mockito.verifyNoMoreInteractions
60 
61 private const val TIMEOUT_MS = 200L
62 private const val MEDIUM_TIMEOUT_MS = 1_000L
63 private const val LONG_TIMEOUT_MS = 5_000
64 
<lambda>null65 private fun nc(transport: Int, vararg caps: Int) = NetworkCapabilities.Builder().apply {
66     addTransportType(transport)
67     caps.forEach {
68         addCapability(it)
69     }
70     // Useful capabilities for everybody
71     addCapability(NET_CAPABILITY_NOT_RESTRICTED)
72     addCapability(NET_CAPABILITY_NOT_SUSPENDED)
73     addCapability(NET_CAPABILITY_NOT_ROAMING)
74     addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
75 }.build()
76 
<lambda>null77 private fun lp(iface: String) = LinkProperties().apply {
78     interfaceName = iface
79     addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
80     addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
81 }
82 
83 // This allows keeping all the networks connected without having to file individual requests
84 // for them.
keepScorenull85 private fun keepScore() = FromS(
86         NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build()
87 )
88 
89 @DevSdkIgnoreRunner.MonitorThreadLeak
90 @RunWith(DevSdkIgnoreRunner::class)
91 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
92 class CSLocalAgentTests : CSTest() {
93     val multicastRoutingConfigMinScope =
94                 MulticastRoutingConfig.Builder(MulticastRoutingConfig.FORWARD_WITH_MIN_SCOPE, 4)
95                 .build()
96     val multicastRoutingConfigSelected =
97                 MulticastRoutingConfig.Builder(MulticastRoutingConfig.FORWARD_SELECTED)
98                 .build()
99     val upstreamSelectorAny = NetworkRequest.Builder()
100                 .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
101                 .build()
102     val upstreamSelectorWifi = NetworkRequest.Builder()
103                 .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
104                 .addTransportType(TRANSPORT_WIFI)
105                 .build()
106     val upstreamSelectorCell = NetworkRequest.Builder()
107                 .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
108                 .addTransportType(TRANSPORT_CELLULAR)
109                 .build()
110 
111     @Test
112     fun testBadAgents() {
113         deps.setBuildSdk(VERSION_V)
114 
115         assertFailsWith<IllegalArgumentException> {
116             Agent(nc = NetworkCapabilities.Builder()
117                     .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
118                     .build(),
119                     lnc = null)
120         }
121         assertFailsWith<IllegalArgumentException> {
122             Agent(nc = NetworkCapabilities.Builder().build(),
123                     lnc = FromS(LocalNetworkConfig.Builder().build()))
124         }
125     }
126 
127     @Test
128     fun testStructuralConstraintViolation() {
129         deps.setBuildSdk(VERSION_V)
130 
131         val cb = TestableNetworkCallback()
132         cm.requestNetwork(NetworkRequest.Builder()
133                 .clearCapabilities()
134                 .build(),
135                 cb)
136         val agent = Agent(nc = NetworkCapabilities.Builder()
137                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
138                 .build(),
139                 lnc = FromS(LocalNetworkConfig.Builder().build()))
140         agent.connect()
141         cb.expectAvailableCallbacks(agent.network, validated = false)
142         agent.sendNetworkCapabilities(NetworkCapabilities.Builder().build())
143         cb.expect<Lost>(agent.network)
144 
145         val agent2 = Agent(nc = NetworkCapabilities.Builder()
146                 .build(),
147                 lnc = null)
148         agent2.connect()
149         cb.expectAvailableCallbacks(agent2.network, validated = false)
150         agent2.sendNetworkCapabilities(NetworkCapabilities.Builder()
151                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
152                 .build())
153         cb.expect<Lost>(agent2.network)
154     }
155 
156     @Test
157     fun testUpdateLocalAgentConfig() {
158         deps.setBuildSdk(VERSION_V)
159 
160         val cb = TestableNetworkCallback()
161         cm.requestNetwork(NetworkRequest.Builder()
162                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
163                 .build(),
164                 cb)
165 
166         // Set up a local agent that should forward its traffic to the best DUN upstream.
167         val localAgent = Agent(
168                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
169                 lp = lp("local0"),
170                 lnc = FromS(LocalNetworkConfig.Builder().build()),
171         )
172         localAgent.connect()
173 
174         cb.expectAvailableCallbacks(localAgent.network, validated = false)
175 
176         val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
177                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
178         wifiAgent.connect()
179 
180         val newLnc = LocalNetworkConfig.Builder()
181                 .setUpstreamSelector(NetworkRequest.Builder()
182                         .addTransportType(TRANSPORT_WIFI)
183                         .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
184                         .build())
185                 .build()
186         localAgent.sendLocalNetworkConfig(newLnc)
187 
188         cb.expect<LocalInfoChanged>(localAgent.network) {
189             it.info.upstreamNetwork == wifiAgent.network
190         }
191 
192         localAgent.sendLocalNetworkConfig(LocalNetworkConfig.Builder().build())
193         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
194 
195         localAgent.sendLocalNetworkConfig(newLnc)
196         cb.expect<LocalInfoChanged>(localAgent.network) {
197             it.info.upstreamNetwork == wifiAgent.network
198         }
199 
200         wifiAgent.disconnect()
201         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
202 
203         localAgent.disconnect()
204     }
205 
206     private fun createLocalAgent(name: String, localNetworkConfig: FromS<LocalNetworkConfig>):
207                 CSAgentWrapper {
208         val localAgent = Agent(
209                 nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
210                 lp = lp(name),
211                 lnc = localNetworkConfig,
212                 score = FromS(NetworkScore.Builder()
213                         .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
214                         .build())
215         )
216         return localAgent
217     }
218 
219     private fun createWifiAgent(name: String): CSAgentWrapper {
220         return Agent(score = keepScore(), lp = lp(name),
221                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
222     }
223 
224     private fun createCellAgent(name: String): CSAgentWrapper {
225         return Agent(score = keepScore(), lp = lp(name),
226                 nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET))
227     }
228 
229     private fun sendLocalNetworkConfig(
230             localAgent: CSAgentWrapper,
231             upstreamSelector: NetworkRequest?,
232             upstreamConfig: MulticastRoutingConfig,
233             downstreamConfig: MulticastRoutingConfig
234     ) {
235         val newLnc = LocalNetworkConfig.Builder()
236                 .setUpstreamSelector(upstreamSelector)
237                 .setUpstreamMulticastRoutingConfig(upstreamConfig)
238                 .setDownstreamMulticastRoutingConfig(downstreamConfig)
239                 .build()
240         localAgent.sendLocalNetworkConfig(newLnc)
241     }
242 
243     @Test
244     fun testMulticastRoutingConfig() {
245         deps.setBuildSdk(VERSION_V)
246         val cb = TestableNetworkCallback()
247         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
248         val inOrder = inOrder(multicastRoutingCoordinatorService)
249 
250         val lnc = FromS(LocalNetworkConfig.Builder()
251                 .setUpstreamSelector(upstreamSelectorWifi)
252                 .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
253                 .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
254                 .build()
255         )
256         val localAgent = createLocalAgent("local0", lnc)
257         localAgent.connect()
258 
259         cb.expectAvailableCallbacks(localAgent.network, validated = false)
260 
261         val wifiAgent = createWifiAgent("wifi0")
262         wifiAgent.connect()
263         cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
264         cb.expect<LocalInfoChanged>(localAgent.network) {
265             it.info.upstreamNetwork == wifiAgent.network
266         }
267 
268         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
269                 "local0", "wifi0", multicastRoutingConfigMinScope)
270         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
271                 "wifi0", "local0", multicastRoutingConfigSelected)
272 
273         wifiAgent.disconnect()
274 
275         inOrder.verify(multicastRoutingCoordinatorService)
276                 .applyMulticastRoutingConfig("local0", "wifi0", CONFIG_FORWARD_NONE)
277         inOrder.verify(multicastRoutingCoordinatorService)
278                 .applyMulticastRoutingConfig("wifi0", "local0", CONFIG_FORWARD_NONE)
279 
280         localAgent.disconnect()
281     }
282 
283     @Test
284     fun testMulticastRoutingConfig_2LocalNetworks() {
285         deps.setBuildSdk(VERSION_V)
286         val inOrder = inOrder(multicastRoutingCoordinatorService)
287         val lnc = FromS(LocalNetworkConfig.Builder()
288                 .setUpstreamSelector(upstreamSelectorWifi)
289                 .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
290                 .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
291                 .build()
292         )
293         val localAgent0 = createLocalAgent("local0", lnc)
294         localAgent0.connect()
295 
296         val wifiAgent = createWifiAgent("wifi0")
297         wifiAgent.connect()
298         waitForIdle()
299 
300         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
301                 "local0", "wifi0", multicastRoutingConfigMinScope)
302         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
303                 "wifi0", "local0", multicastRoutingConfigSelected)
304 
305         val localAgent1 = createLocalAgent("local1", lnc)
306         localAgent1.connect()
307         waitForIdle()
308 
309         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
310                 "local1", "wifi0", multicastRoutingConfigMinScope)
311         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
312                 "wifi0", "local1", multicastRoutingConfigSelected)
313 
314         localAgent0.disconnect()
315         localAgent1.disconnect()
316         wifiAgent.disconnect()
317     }
318 
319     @Test
320     fun testMulticastRoutingConfig_UpstreamNetworkCellToWifi() {
321         deps.setBuildSdk(VERSION_V)
322         val cb = TestableNetworkCallback()
323         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
324                         .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
325                         .build(), cb)
326         val inOrder = inOrder(multicastRoutingCoordinatorService)
327         val lnc = FromS(LocalNetworkConfig.Builder()
328                 .setUpstreamSelector(upstreamSelectorAny)
329                 .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
330                 .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
331                 .build()
332         )
333         val localAgent = createLocalAgent("local0", lnc)
334         val wifiAgent = createWifiAgent("wifi0")
335         val cellAgent = createCellAgent("cell0")
336 
337         localAgent.connect()
338         cb.expectAvailableCallbacks(localAgent.network, validated = false)
339 
340         cellAgent.connect()
341         cb.expect<LocalInfoChanged>(localAgent.network) {
342             it.info.upstreamNetwork == cellAgent.network
343         }
344 
345         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
346                 "local0", "cell0", multicastRoutingConfigMinScope)
347         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
348                 "cell0", "local0", multicastRoutingConfigSelected)
349 
350         wifiAgent.connect()
351 
352         cb.expect<LocalInfoChanged>(localAgent.network) {
353             it.info.upstreamNetwork == wifiAgent.network
354         }
355 
356         // upstream should have been switched to wifi
357         inOrder.verify(multicastRoutingCoordinatorService)
358                 .applyMulticastRoutingConfig("local0", "cell0", CONFIG_FORWARD_NONE)
359         inOrder.verify(multicastRoutingCoordinatorService)
360                 .applyMulticastRoutingConfig("cell0", "local0", CONFIG_FORWARD_NONE)
361         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
362                 "local0", "wifi0", multicastRoutingConfigMinScope)
363         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
364                 "wifi0", "local0", multicastRoutingConfigSelected)
365 
366         localAgent.disconnect()
367         cellAgent.disconnect()
368         wifiAgent.disconnect()
369     }
370 
371     @Test
372     fun testMulticastRoutingConfig_UpstreamSelectorCellToWifi() {
373         deps.setBuildSdk(VERSION_V)
374         val cb = TestableNetworkCallback()
375         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
376                         .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
377                         .build(), cb)
378         val inOrder = inOrder(multicastRoutingCoordinatorService)
379         val lnc = FromS(LocalNetworkConfig.Builder()
380                 .setUpstreamSelector(upstreamSelectorCell)
381                 .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
382                 .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
383                 .build()
384         )
385         val localAgent = createLocalAgent("local0", lnc)
386         val wifiAgent = createWifiAgent("wifi0")
387         val cellAgent = createCellAgent("cell0")
388 
389         localAgent.connect()
390         cellAgent.connect()
391         wifiAgent.connect()
392         cb.expectAvailableCallbacks(localAgent.network, validated = false)
393         cb.expect<LocalInfoChanged>(localAgent.network) {
394             it.info.upstreamNetwork == cellAgent.network
395         }
396 
397         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
398                 "local0", "cell0", multicastRoutingConfigMinScope)
399         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
400                 "cell0", "local0", multicastRoutingConfigSelected)
401 
402         sendLocalNetworkConfig(localAgent, upstreamSelectorWifi, multicastRoutingConfigMinScope,
403                 multicastRoutingConfigSelected)
404         cb.expect<LocalInfoChanged>(localAgent.network) {
405             it.info.upstreamNetwork == wifiAgent.network
406         }
407 
408         // upstream should have been switched to wifi
409         inOrder.verify(multicastRoutingCoordinatorService)
410                 .applyMulticastRoutingConfig("local0", "cell0", CONFIG_FORWARD_NONE)
411         inOrder.verify(multicastRoutingCoordinatorService)
412                 .applyMulticastRoutingConfig("cell0", "local0", CONFIG_FORWARD_NONE)
413         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
414                 "local0", "wifi0", multicastRoutingConfigMinScope)
415         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
416                 "wifi0", "local0", multicastRoutingConfigSelected)
417 
418         localAgent.disconnect()
419         cellAgent.disconnect()
420         wifiAgent.disconnect()
421     }
422 
423     @Test
424     fun testMulticastRoutingConfig_UpstreamSelectorWifiToNull() {
425         deps.setBuildSdk(VERSION_V)
426         val cb = TestableNetworkCallback()
427         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
428                         .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
429                         .build(), cb)
430         val inOrder = inOrder(multicastRoutingCoordinatorService)
431         val lnc = FromS(LocalNetworkConfig.Builder()
432                 .setUpstreamSelector(upstreamSelectorWifi)
433                 .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
434                 .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
435                 .build()
436         )
437         val localAgent = createLocalAgent("local0", lnc)
438         localAgent.connect()
439         val wifiAgent = createWifiAgent("wifi0")
440         wifiAgent.connect()
441         cb.expectAvailableCallbacks(localAgent.network, validated = false)
442         cb.expect<LocalInfoChanged>(localAgent.network) {
443             it.info.upstreamNetwork == wifiAgent.network
444         }
445 
446         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
447                 "local0", "wifi0", multicastRoutingConfigMinScope)
448         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
449                 "wifi0", "local0", multicastRoutingConfigSelected)
450 
451         sendLocalNetworkConfig(localAgent, null, multicastRoutingConfigMinScope,
452                 multicastRoutingConfigSelected)
453         cb.expect<LocalInfoChanged>(localAgent.network) {
454             it.info.upstreamNetwork == null
455         }
456 
457         // upstream should have been switched to null
458         inOrder.verify(multicastRoutingCoordinatorService)
459                 .applyMulticastRoutingConfig("local0", "wifi0", CONFIG_FORWARD_NONE)
460         inOrder.verify(multicastRoutingCoordinatorService)
461                 .applyMulticastRoutingConfig("wifi0", "local0", CONFIG_FORWARD_NONE)
462         inOrder.verify(multicastRoutingCoordinatorService, never()).applyMulticastRoutingConfig(
463                 eq("local0"), any(), eq(multicastRoutingConfigMinScope))
464         inOrder.verify(multicastRoutingCoordinatorService, never()).applyMulticastRoutingConfig(
465                 any(), eq("local0"), eq(multicastRoutingConfigSelected))
466 
467         localAgent.disconnect()
468         wifiAgent.disconnect()
469     }
470 
471     @Test
472     fun testUnregisterUpstreamAfterReplacement_SameIfaceName() {
473         doTestUnregisterUpstreamAfterReplacement(true)
474     }
475 
476     @Test
477     fun testUnregisterUpstreamAfterReplacement_DifferentIfaceName() {
478         doTestUnregisterUpstreamAfterReplacement(false)
479     }
480 
481     fun doTestUnregisterUpstreamAfterReplacement(sameIfaceName: Boolean) {
482         deps.setBuildSdk(VERSION_V)
483         val cb = TestableNetworkCallback()
484         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
485 
486         // Set up a local agent that should forward its traffic to the best wifi upstream.
487         val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
488                 lp = lp("local0"),
489                 lnc = FromS(LocalNetworkConfig.Builder()
490                         .setUpstreamSelector(upstreamSelectorWifi)
491                         .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
492                         .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
493                         .build()),
494                 score = FromS(NetworkScore.Builder()
495                         .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
496                         .build())
497         )
498         localAgent.connect()
499 
500         cb.expectAvailableCallbacks(localAgent.network, validated = false)
501 
502         val wifiAgent = Agent(lp = lp("wifi0"),
503                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
504         wifiAgent.connect()
505 
506         cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
507         cb.expect<LocalInfoChanged>(localAgent.network) {
508             it.info.upstreamNetwork == wifiAgent.network
509         }
510 
511         clearInvocations(netd)
512         clearInvocations(multicastRoutingCoordinatorService)
513         val inOrder = inOrder(netd, multicastRoutingCoordinatorService)
514         wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
515         waitForIdle()
516         inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0")
517         inOrder.verify(multicastRoutingCoordinatorService)
518                 .applyMulticastRoutingConfig("local0", "wifi0", CONFIG_FORWARD_NONE)
519         inOrder.verify(multicastRoutingCoordinatorService)
520                 .applyMulticastRoutingConfig("wifi0", "local0", CONFIG_FORWARD_NONE)
521         inOrder.verify(netd).networkDestroy(wifiAgent.network.netId)
522 
523         val wifiIface2 = if (sameIfaceName) "wifi0" else "wifi1"
524         val wifiAgent2 = Agent(lp = lp(wifiIface2),
525                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
526         wifiAgent2.connect()
527 
528         cb.expectAvailableCallbacks(wifiAgent2.network, validated = false)
529         cb.expect<LocalInfoChanged> { it.info.upstreamNetwork == wifiAgent2.network }
530         cb.expect<Lost> { it.network == wifiAgent.network }
531 
532         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", wifiIface2)
533         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
534                 "local0", wifiIface2, multicastRoutingConfigMinScope)
535         inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
536                 wifiIface2, "local0", multicastRoutingConfigSelected)
537 
538         inOrder.verify(netd, never()).ipfwdRemoveInterfaceForward(any(), any())
539         inOrder.verify(multicastRoutingCoordinatorService, never())
540                 .applyMulticastRoutingConfig("local0", "wifi0", CONFIG_FORWARD_NONE)
541         inOrder.verify(multicastRoutingCoordinatorService, never())
542                 .applyMulticastRoutingConfig("wifi0", "local0", CONFIG_FORWARD_NONE)
543     }
544 
545     @Test
546     fun testUnregisterUpstreamAfterReplacement_neverReplaced() {
547         deps.setBuildSdk(VERSION_V)
548         val cb = TestableNetworkCallback()
549         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
550 
551         // Set up a local agent that should forward its traffic to the best wifi upstream.
552         val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
553                 lp = lp("local0"),
554                 lnc = FromS(LocalNetworkConfig.Builder()
555                         .setUpstreamSelector(NetworkRequest.Builder()
556                                 .addTransportType(TRANSPORT_WIFI)
557                                 .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
558                                 .build())
559                         .build()),
560                 score = FromS(NetworkScore.Builder()
561                         .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
562                         .build())
563         )
564         localAgent.connect()
565 
566         cb.expectAvailableCallbacks(localAgent.network, validated = false)
567 
568         val wifiAgent = Agent(lp = lp("wifi0"),
569                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
570         wifiAgent.connect()
571 
572         cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
573         cb.expect<LocalInfoChanged>(localAgent.network) {
574             it.info.upstreamNetwork == wifiAgent.network
575         }
576 
577         clearInvocations(netd)
578         wifiAgent.unregisterAfterReplacement(TIMEOUT_MS.toInt())
579         waitForIdle()
580         verify(netd).networkDestroy(wifiAgent.network.netId)
581         verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0")
582 
583         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
584         cb.expect<Lost> { it.network == wifiAgent.network }
585     }
586 
587     @Test
588     fun testUnregisterLocalAgentAfterReplacement() {
589         deps.setBuildSdk(VERSION_V)
590 
591         val localCb = TestableNetworkCallback()
592         cm.requestNetwork(NetworkRequest.Builder().clearCapabilities()
593                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
594                 .build(),
595                 localCb)
596 
597         val cb = TestableNetworkCallback()
598         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
599 
600         val localNc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK)
601         val lnc = FromS(LocalNetworkConfig.Builder()
602                 .setUpstreamSelector(NetworkRequest.Builder()
603                         .addTransportType(TRANSPORT_WIFI)
604                         .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
605                         .build())
606                 .build())
607         val localScore = FromS(NetworkScore.Builder().build())
608 
609         // Set up a local agent that should forward its traffic to the best wifi upstream.
610         val localAgent = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore)
611         localAgent.connect()
612 
613         localCb.expectAvailableCallbacks(localAgent.network, validated = false)
614         cb.expectAvailableCallbacks(localAgent.network, validated = false)
615 
616         val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
617         wifiAgent.connect()
618 
619         cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
620         listOf(cb, localCb).forEach {
621             it.expect<LocalInfoChanged>(localAgent.network) {
622                 it.info.upstreamNetwork == wifiAgent.network
623             }
624         }
625 
626         verify(netd).ipfwdAddInterfaceForward("local0", "wifi0")
627 
628         localAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
629 
630         val localAgent2 = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore)
631         localAgent2.connect()
632 
633         localCb.expectAvailableCallbacks(localAgent2.network,
634                 validated = false, upstream = wifiAgent.network)
635         cb.expectAvailableCallbacks(localAgent2.network,
636                 validated = false, upstream = wifiAgent.network)
637         cb.expect<Lost> { it.network == localAgent.network }
638     }
639 
640     @Test
641     fun testDestroyedNetworkAsSelectedUpstream() {
642         deps.setBuildSdk(VERSION_V)
643         val cb = TestableNetworkCallback()
644         cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
645 
646         val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
647         wifiAgent.connect()
648         cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
649 
650         // Unregister wifi pending replacement, then set up a local agent that would have
651         // this network as its upstream.
652         wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
653         val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
654                 lp = lp("local0"),
655                 lnc = FromS(LocalNetworkConfig.Builder()
656                         .setUpstreamSelector(NetworkRequest.Builder()
657                                 .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
658                                 .addTransportType(TRANSPORT_WIFI)
659                                 .build())
660                         .build()),
661                 score = FromS(NetworkScore.Builder()
662                         .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
663                         .build())
664         )
665 
666         // Connect the local agent. The zombie wifi is its upstream, but the stack doesn't
667         // tell netd to add the forward since the wifi0 interface has gone.
668         localAgent.connect()
669         cb.expectAvailableCallbacks(localAgent.network,
670                 validated = false, upstream = wifiAgent.network)
671 
672         verify(netd, never()).ipfwdAddInterfaceForward("local0", "wifi0")
673 
674         // Disconnect wifi without a replacement. Expect an update with upstream null.
675         wifiAgent.disconnect()
676         verify(netd, never()).ipfwdAddInterfaceForward("local0", "wifi0")
677         cb.expect<LocalInfoChanged> { it.info.upstreamNetwork == null }
678     }
679 
680     @Test
681     fun testForwardingRules() {
682         deps.setBuildSdk(VERSION_V)
683         // Set up a local agent that should forward its traffic to the best DUN upstream.
684         val lnc = FromS(LocalNetworkConfig.Builder()
685                 .setUpstreamSelector(NetworkRequest.Builder()
686                         .addCapability(NET_CAPABILITY_DUN)
687                         .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
688                         .build())
689                 .build())
690         val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
691                 lp = lp("local0"),
692                 lnc = lnc,
693                 score = FromS(NetworkScore.Builder()
694                         .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
695                         .build())
696         )
697         localAgent.connect()
698 
699         val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
700                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
701         val cellAgentDun = Agent(score = keepScore(), lp = lp("cell0"),
702                 nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
703         val wifiAgentDun = Agent(score = keepScore(), lp = lp("wifi1"),
704                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
705 
706         val cb = TestableNetworkCallback()
707         cm.registerNetworkCallback(NetworkRequest.Builder()
708                 .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
709                 .build(),
710                 cb)
711         cb.expectAvailableCallbacks(localAgent.network, validated = false)
712 
713         val inOrder = inOrder(netd)
714         inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any())
715         cb.assertNoCallback()
716 
717         wifiAgent.connect()
718         inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any())
719         cb.assertNoCallback()
720 
721         cellAgentDun.connect()
722         inOrder.verify(netd).ipfwdEnableForwarding(any())
723         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "cell0")
724         cb.expect<LocalInfoChanged>(localAgent.network) {
725             it.info.upstreamNetwork == cellAgentDun.network
726         }
727 
728         wifiAgentDun.connect()
729         inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0")
730         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi1")
731         cb.expect<LocalInfoChanged>(localAgent.network) {
732             it.info.upstreamNetwork == wifiAgentDun.network
733         }
734 
735         // Make sure sending the same config again doesn't do anything
736         repeat(5) {
737             localAgent.sendLocalNetworkConfig(lnc.value)
738         }
739         inOrder.verifyNoMoreInteractions()
740 
741         wifiAgentDun.disconnect()
742         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
743         cb.expect<LocalInfoChanged>(localAgent.network) {
744             it.info.upstreamNetwork == cellAgentDun.network
745         }
746         inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi1")
747         // This can take a little bit of time because it needs to wait for the rematch
748         inOrder.verify(netd, timeout(MEDIUM_TIMEOUT_MS)).ipfwdAddInterfaceForward("local0", "cell0")
749 
750         cellAgentDun.disconnect()
751         inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0")
752         inOrder.verify(netd).ipfwdDisableForwarding(any())
753         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
754 
755         val wifiAgentDun2 = Agent(score = keepScore(), lp = lp("wifi2"),
756                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
757         wifiAgentDun2.connect()
758         inOrder.verify(netd).ipfwdEnableForwarding(any())
759         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi2")
760         cb.expect<LocalInfoChanged>(localAgent.network) {
761             it.info.upstreamNetwork == wifiAgentDun2.network
762         }
763 
764         wifiAgentDun2.disconnect()
765         inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi2")
766         inOrder.verify(netd).ipfwdDisableForwarding(any())
767         cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
768 
769         val wifiAgentDun3 = Agent(score = keepScore(), lp = lp("wifi3"),
770                 nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
771         wifiAgentDun3.connect()
772         inOrder.verify(netd).ipfwdEnableForwarding(any())
773         inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi3")
774         cb.expect<LocalInfoChanged>(localAgent.network) {
775             it.info.upstreamNetwork == wifiAgentDun3.network
776         }
777 
778         localAgent.disconnect()
779         inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi3")
780         inOrder.verify(netd).ipfwdDisableForwarding(any())
781         cb.expect<Lost>(localAgent.network)
782         cb.assertNoCallback()
783     }
784 
785     @Test
786     fun testLocalNetworkUnwanted_withUpstream() {
787         doTestLocalNetworkUnwanted(true)
788     }
789 
790     @Test
791     fun testLocalNetworkUnwanted_withoutUpstream() {
792         doTestLocalNetworkUnwanted(false)
793     }
794 
795     fun doTestLocalNetworkUnwanted(haveUpstream: Boolean) {
796         deps.setBuildSdk(VERSION_V)
797 
798         val nr = NetworkRequest.Builder().addCapability(NET_CAPABILITY_LOCAL_NETWORK).build()
799         val requestCb = TestableNetworkCallback()
800         cm.requestNetwork(nr, requestCb)
801         val listenCb = TestableNetworkCallback()
802         cm.registerNetworkCallback(nr, listenCb)
803 
804         val upstream = if (haveUpstream) {
805             Agent(score = keepScore(), lp = lp("wifi0"),
806                     nc = nc(TRANSPORT_WIFI)).also { it.connect() }
807         } else {
808             null
809         }
810 
811         // Set up a local agent.
812         val lnc = FromS(LocalNetworkConfig.Builder().apply {
813             if (haveUpstream) {
814                 setUpstreamSelector(NetworkRequest.Builder()
815                         .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
816                         .addTransportType(TRANSPORT_WIFI)
817                         .build())
818             }
819         }.build())
820         val localAgent = Agent(nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
821                 lp = lp("local0"),
822                 lnc = lnc,
823                 score = FromS(NetworkScore.Builder().build())
824         )
825         localAgent.connect()
826 
827         requestCb.expectAvailableCallbacks(localAgent.network,
828                 validated = false, upstream = upstream?.network)
829         listenCb.expectAvailableCallbacks(localAgent.network,
830                 validated = false, upstream = upstream?.network)
831 
832         cm.unregisterNetworkCallback(requestCb)
833 
834         listenCb.expect<Lost>()
835     }
836 
837     fun doTestLocalNetworkRequest(
838             request: NetworkRequest,
839             enableMatchLocalNetwork: Boolean,
840             expectCallback: Boolean
841     ) {
842         deps.setBuildSdk(VERSION_V)
843         deps.setChangeIdEnabled(enableMatchLocalNetwork, ENABLE_MATCH_LOCAL_NETWORK)
844 
845         val requestCb = TestableNetworkCallback()
846         val listenCb = TestableNetworkCallback()
847         cm.requestNetwork(request, requestCb)
848         cm.registerNetworkCallback(request, listenCb)
849 
850         val localAgent = createLocalAgent("local0", FromS(LocalNetworkConfig.Builder().build()))
851         localAgent.connect()
852 
853         if (expectCallback) {
854             requestCb.expectAvailableCallbacks(localAgent.network, validated = false)
855             listenCb.expectAvailableCallbacks(localAgent.network, validated = false)
856         } else {
857             waitForIdle()
858             requestCb.assertNoCallback(timeoutMs = 0)
859             listenCb.assertNoCallback(timeoutMs = 0)
860         }
861         localAgent.disconnect()
862     }
863 
864     @Test
865     fun testLocalNetworkRequest() {
866         val request = NetworkRequest.Builder().build()
867         // If ENABLE_MATCH_LOCAL_NETWORK is false, request is not satisfied by local network
868         doTestLocalNetworkRequest(
869                 request,
870                 enableMatchLocalNetwork = false,
871                 expectCallback = false)
872         // If ENABLE_MATCH_LOCAL_NETWORK is true, request is satisfied by local network
873         doTestLocalNetworkRequest(
874                 request,
875                 enableMatchLocalNetwork = true,
876                 expectCallback = true)
877     }
878 
879     @Test
880     fun testLocalNetworkRequest_withCapability() {
881         val request = NetworkRequest.Builder().addCapability(NET_CAPABILITY_LOCAL_NETWORK).build()
882         doTestLocalNetworkRequest(
883                 request,
884                 enableMatchLocalNetwork = false,
885                 expectCallback = true)
886         doTestLocalNetworkRequest(
887                 request,
888                 enableMatchLocalNetwork = true,
889                 expectCallback = true)
890     }
891 }
892