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