1 /*
2  * Copyright (C) 2022 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.systemui.statusbar.pipeline.shared.data.repository
18 
19 import android.net.ConnectivityManager
20 import android.net.Network
21 import android.net.NetworkCapabilities
22 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
23 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
24 import android.net.NetworkCapabilities.TRANSPORT_VPN
25 import android.net.NetworkCapabilities.TRANSPORT_WIFI
26 import android.net.TelephonyNetworkSpecifier
27 import android.net.VpnTransportInfo
28 import android.net.vcn.VcnTransportInfo
29 import android.net.wifi.WifiInfo
30 import android.platform.test.annotations.DisableFlags
31 import android.platform.test.annotations.EnableFlags
32 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
33 import androidx.test.ext.junit.runners.AndroidJUnit4
34 import androidx.test.filters.SmallTest
35 import com.android.systemui.Flags.FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS
36 import com.android.systemui.SysuiTestCase
37 import com.android.systemui.coroutines.collectLastValue
38 import com.android.systemui.dump.dumpManager
39 import com.android.systemui.kosmos.testScope
40 import com.android.systemui.log.core.FakeLogBuffer
41 import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
42 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
43 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
44 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
45 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
46 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
47 import com.android.systemui.testKosmos
48 import com.android.systemui.tuner.TunerService
49 import com.google.common.truth.Truth.assertThat
50 import kotlinx.coroutines.ExperimentalCoroutinesApi
51 import kotlinx.coroutines.test.runCurrent
52 import kotlinx.coroutines.test.runTest
53 import org.junit.Before
54 import org.junit.Test
55 import org.junit.runner.RunWith
56 import org.mockito.kotlin.any
57 import org.mockito.kotlin.argumentCaptor
58 import org.mockito.kotlin.eq
59 import org.mockito.kotlin.mock
60 import org.mockito.kotlin.verify
61 import org.mockito.kotlin.whenever
62 
63 @OptIn(ExperimentalCoroutinesApi::class)
64 @SmallTest
65 @RunWith(AndroidJUnit4::class)
66 class ConnectivityRepositoryImplTest : SysuiTestCase() {
67     private val kosmos = testKosmos()
68 
69     private lateinit var underTest: ConnectivityRepositoryImpl
70 
71     private val connectivityManager = mock<ConnectivityManager>()
72     private val connectivitySlots = mock<ConnectivitySlots>()
73     private val dumpManager = kosmos.dumpManager
74     private val logger = ConnectivityInputLogger(FakeLogBuffer.Factory.create())
75     private val testScope = kosmos.testScope
76     private val tunerService = mock<TunerService>()
77 
78     private val vcnTransportInfo = VcnTransportInfo.Builder().build()
79 
80     @Before
setUpnull81     fun setUp() {
82         createAndSetRepo()
83     }
84 
85     @Test
forceHiddenSlots_initiallyGetsDefaultnull86     fun forceHiddenSlots_initiallyGetsDefault() =
87         testScope.runTest {
88             setUpEthernetWifiMobileSlotNames()
89             context
90                 .getOrCreateTestableResources()
91                 .addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
92             // Re-create our [ConnectivityRepositoryImpl], since it fetches
93             // config_statusBarIconsToExclude when it's first constructed
94             createAndSetRepo()
95 
96             val latest by collectLastValue(underTest.forceHiddenSlots)
97             runCurrent()
98 
99             assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
100         }
101 
102     @Test
forceHiddenSlots_slotNamesAdded_flowHasSlotsnull103     fun forceHiddenSlots_slotNamesAdded_flowHasSlots() =
104         testScope.runTest {
105             setUpEthernetWifiMobileSlotNames()
106 
107             val latest by collectLastValue(underTest.forceHiddenSlots)
108             runCurrent()
109 
110             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
111 
112             assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
113         }
114 
115     @Test
forceHiddenSlots_wrongKey_doesNotUpdatenull116     fun forceHiddenSlots_wrongKey_doesNotUpdate() =
117         testScope.runTest {
118             setUpEthernetWifiMobileSlotNames()
119 
120             val latest by collectLastValue(underTest.forceHiddenSlots)
121             runCurrent()
122 
123             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
124 
125             // WHEN onTuningChanged with the wrong key
126             getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
127 
128             // THEN we didn't update our value and still have the old one
129             assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
130         }
131 
132     @Test
forceHiddenSlots_slotNamesAddedThenNull_flowHasDefaultnull133     fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() =
134         testScope.runTest {
135             setUpEthernetWifiMobileSlotNames()
136             context
137                 .getOrCreateTestableResources()
138                 .addOverride(DEFAULT_HIDDEN_ICONS_RESOURCE, arrayOf(SLOT_WIFI, SLOT_ETHERNET))
139             // Re-create our [ConnectivityRepositoryImpl], since it fetches
140             // config_statusBarIconsToExclude when it's first constructed
141             createAndSetRepo()
142 
143             val latest by collectLastValue(underTest.forceHiddenSlots)
144             runCurrent()
145 
146             // First, update the slots
147             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
148             assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
149 
150             // WHEN we update to a null value
151             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
152 
153             // THEN we go back to our default value
154             assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
155         }
156 
157     @Test
forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnlynull158     fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() =
159         testScope.runTest {
160             val latest by collectLastValue(underTest.forceHiddenSlots)
161             runCurrent()
162 
163             whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
164             whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
165 
166             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
167 
168             assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
169         }
170 
171     @Test
forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnlynull172     fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() =
173         testScope.runTest {
174             setUpEthernetWifiMobileSlotNames()
175 
176             val latest by collectLastValue(underTest.forceHiddenSlots)
177             runCurrent()
178 
179             // WHEN there's empty and blank slot names
180             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,  ,,$SLOT_WIFI")
181 
182             // THEN we skip that slot but still process the other ones
183             assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
184         }
185 
186     @Test
forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmptynull187     fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() =
188         testScope.runTest {
189             val latest by collectLastValue(underTest.forceHiddenSlots)
190             runCurrent()
191 
192             whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
193             whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
194             whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
195 
196             getTunable()
197                 .onTuningChanged(
198                     HIDDEN_ICONS_TUNABLE_KEY,
199                     "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
200                 )
201 
202             assertThat(latest).isEmpty()
203         }
204 
205     @Test
forceHiddenSlots_newSubscriberGetsCurrentValuenull206     fun forceHiddenSlots_newSubscriberGetsCurrentValue() =
207         testScope.runTest {
208             setUpEthernetWifiMobileSlotNames()
209 
210             val latest1 by collectLastValue(underTest.forceHiddenSlots)
211             runCurrent()
212 
213             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
214 
215             assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
216 
217             // WHEN we add a second subscriber after having already emitted a value
218             val latest2 by collectLastValue(underTest.forceHiddenSlots)
219             runCurrent()
220 
221             // THEN the second subscribe receives the already-emitted value
222             assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
223         }
224 
225     @Test
defaultConnections_noTransports_nothingIsDefaultnull226     fun defaultConnections_noTransports_nothingIsDefault() =
227         testScope.runTest {
228             val latest by collectLastValue(underTest.defaultConnections)
229 
230             val capabilities =
231                 mock<NetworkCapabilities>().also {
232                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
233                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
234                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
235                 }
236 
237             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
238 
239             assertThat(latest!!.mobile.isDefault).isFalse()
240             assertThat(latest!!.wifi.isDefault).isFalse()
241             assertThat(latest!!.ethernet.isDefault).isFalse()
242             assertThat(latest!!.carrierMerged.isDefault).isFalse()
243         }
244 
245     @Test
defaultConnections_cellularTransport_mobileIsDefaultnull246     fun defaultConnections_cellularTransport_mobileIsDefault() =
247         testScope.runTest {
248             val latest by collectLastValue(underTest.defaultConnections)
249 
250             val capabilities =
251                 mock<NetworkCapabilities>().also {
252                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
253                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
254                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
255                 }
256 
257             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
258 
259             assertThat(latest!!.mobile.isDefault).isTrue()
260             assertThat(latest!!.wifi.isDefault).isFalse()
261             assertThat(latest!!.ethernet.isDefault).isFalse()
262             assertThat(latest!!.carrierMerged.isDefault).isFalse()
263         }
264 
265     @Test
defaultConnections_wifiTransport_wifiIsDefaultnull266     fun defaultConnections_wifiTransport_wifiIsDefault() =
267         testScope.runTest {
268             val latest by collectLastValue(underTest.defaultConnections)
269 
270             val capabilities =
271                 mock<NetworkCapabilities>().also {
272                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
273                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
274                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
275                 }
276 
277             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
278 
279             assertThat(latest!!.wifi.isDefault).isTrue()
280             assertThat(latest!!.ethernet.isDefault).isFalse()
281             assertThat(latest!!.carrierMerged.isDefault).isFalse()
282             assertThat(latest!!.mobile.isDefault).isFalse()
283         }
284 
285     @Test
defaultConnections_ethernetTransport_ethernetIsDefaultnull286     fun defaultConnections_ethernetTransport_ethernetIsDefault() =
287         testScope.runTest {
288             val latest by collectLastValue(underTest.defaultConnections)
289 
290             val capabilities =
291                 mock<NetworkCapabilities>().also {
292                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
293                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(true)
294                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
295                 }
296 
297             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
298 
299             assertThat(latest!!.ethernet.isDefault).isTrue()
300             assertThat(latest!!.wifi.isDefault).isFalse()
301             assertThat(latest!!.carrierMerged.isDefault).isFalse()
302             assertThat(latest!!.mobile.isDefault).isFalse()
303         }
304 
305     @Test
defaultConnections_carrierMergedViaWifi_wifiAndCarrierMergedDefaultnull306     fun defaultConnections_carrierMergedViaWifi_wifiAndCarrierMergedDefault() =
307         testScope.runTest {
308             val latest by collectLastValue(underTest.defaultConnections)
309 
310             val carrierMergedInfo =
311                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
312             val capabilities =
313                 mock<NetworkCapabilities>().also {
314                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
315                     whenever(it.transportInfo).thenReturn(carrierMergedInfo)
316                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
317                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
318                 }
319 
320             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
321 
322             assertThat(latest!!.wifi.isDefault).isTrue()
323             assertThat(latest!!.carrierMerged.isDefault).isTrue()
324             assertThat(latest!!.mobile.isDefault).isFalse()
325         }
326 
327     @Test
defaultConnections_carrierMergedViaMobile_mobileCarrierMergedWifiDefaultnull328     fun defaultConnections_carrierMergedViaMobile_mobileCarrierMergedWifiDefault() =
329         testScope.runTest {
330             val latest by collectLastValue(underTest.defaultConnections)
331 
332             val carrierMergedInfo =
333                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
334             val capabilities =
335                 mock<NetworkCapabilities>().also {
336                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
337                     whenever(it.transportInfo).thenReturn(carrierMergedInfo)
338                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
339                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
340                 }
341 
342             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
343 
344             assertThat(latest!!.mobile.isDefault).isTrue()
345             assertThat(latest!!.carrierMerged.isDefault).isTrue()
346             assertThat(latest!!.wifi.isDefault).isTrue()
347         }
348 
newWifiNetworknull349     private fun newWifiNetwork(wifiInfo: WifiInfo): Network {
350         val network = mock<Network>()
351         val capabilities =
352             mock<NetworkCapabilities>().also {
353                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
354                 whenever(it.transportInfo).thenReturn(wifiInfo)
355             }
356         whenever(connectivityManager.getNetworkCapabilities(network)).thenReturn(capabilities)
357 
358         return network
359     }
360 
newCellNetworknull361     private fun newCellNetwork(subId: Int): Network {
362         val network = mock<Network>()
363         val capabilities =
364             mock<NetworkCapabilities>().also {
365                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
366                 whenever(it.networkSpecifier).thenReturn(TelephonyNetworkSpecifier(subId))
367             }
368         whenever(connectivityManager.getNetworkCapabilities(network)).thenReturn(capabilities)
369 
370         return network
371     }
372 
373     @Test
defaultConnections_carrierMergedViaWifiWithVcnTransport_wifiAndCarrierMergedDefaultnull374     fun defaultConnections_carrierMergedViaWifiWithVcnTransport_wifiAndCarrierMergedDefault() =
375         testScope.runTest {
376             val latest by collectLastValue(underTest.defaultConnections)
377 
378             val carrierMergedInfo =
379                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
380             val underlyingWifi = newWifiNetwork(carrierMergedInfo)
381             val capabilities =
382                 mock<NetworkCapabilities>().also {
383                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
384                     whenever(it.transportInfo).thenReturn(vcnTransportInfo)
385                     whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
386                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
387                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
388                 }
389 
390             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
391 
392             assertThat(latest!!.wifi.isDefault).isTrue()
393             assertThat(latest!!.carrierMerged.isDefault).isTrue()
394             assertThat(latest!!.mobile.isDefault).isFalse()
395         }
396 
397     /** VCN over W+ (aka VCN over carrier merged). See b/352162710#comment27 scenario #1. */
398     @Test
defaultConnections_carrierMergedViaMobileWithVcnTransport_mobileCarrierMergedWifiDefaultnull399     fun defaultConnections_carrierMergedViaMobileWithVcnTransport_mobileCarrierMergedWifiDefault() =
400         testScope.runTest {
401             val latest by collectLastValue(underTest.defaultConnections)
402 
403             val carrierMergedInfo =
404                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
405             val underlyingWifi = newWifiNetwork(carrierMergedInfo)
406             val capabilities =
407                 mock<NetworkCapabilities>().also {
408                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
409                     whenever(it.transportInfo).thenReturn(vcnTransportInfo)
410                     whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
411                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
412                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
413                 }
414 
415             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
416 
417             assertThat(latest!!.mobile.isDefault).isTrue()
418             assertThat(latest!!.carrierMerged.isDefault).isTrue()
419             assertThat(latest!!.wifi.isDefault).isTrue()
420         }
421 
422     /** VPN over W+ (aka VPN over carrier merged). See b/352162710#comment27 scenario #2. */
423     @Test
424     @EnableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
defaultConnections_vpnOverCarrierMerged_carrierMergedDefaultnull425     fun defaultConnections_vpnOverCarrierMerged_carrierMergedDefault() =
426         testScope.runTest {
427             val latest by collectLastValue(underTest.defaultConnections)
428 
429             // Underlying carrier merged network
430             val underlyingCarrierMergedNetwork = mock<Network>()
431             val carrierMergedInfo =
432                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
433             val underlyingCapabilities =
434                 mock<NetworkCapabilities>().also {
435                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
436                     whenever(it.transportInfo).thenReturn(carrierMergedInfo)
437                 }
438             whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
439                 .thenReturn(underlyingCapabilities)
440 
441             val mainCapabilities =
442                 mock<NetworkCapabilities>().also {
443                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
444                     // Transports are WIFI|VPN, *not* CELLULAR.
445                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
446                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
447                     whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
448                     whenever(it.transportInfo).thenReturn(VpnTransportInfo(0, null, false, false))
449                     whenever(it.underlyingNetworks)
450                         .thenReturn(listOf(underlyingCarrierMergedNetwork))
451                 }
452 
453             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
454 
455             assertThat(latest!!.carrierMerged.isDefault).isTrue()
456         }
457 
458     @Test
defaultConnections_notCarrierMergedViaWifi_carrierMergedNotDefaultnull459     fun defaultConnections_notCarrierMergedViaWifi_carrierMergedNotDefault() =
460         testScope.runTest {
461             val latest by collectLastValue(underTest.defaultConnections)
462 
463             val carrierMergedInfo =
464                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) }
465             val capabilities =
466                 mock<NetworkCapabilities>().also {
467                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
468                     whenever(it.transportInfo).thenReturn(carrierMergedInfo)
469                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
470                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
471                 }
472 
473             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
474 
475             assertThat(latest!!.carrierMerged.isDefault).isFalse()
476         }
477 
478     @Test
defaultConnections_notCarrierMergedViaMobile_carrierMergedNotDefaultnull479     fun defaultConnections_notCarrierMergedViaMobile_carrierMergedNotDefault() =
480         testScope.runTest {
481             val latest by collectLastValue(underTest.defaultConnections)
482 
483             val carrierMergedInfo =
484                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) }
485             val capabilities =
486                 mock<NetworkCapabilities>().also {
487                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
488                     whenever(it.transportInfo).thenReturn(carrierMergedInfo)
489                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
490                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
491                 }
492 
493             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
494 
495             assertThat(latest!!.carrierMerged.isDefault).isFalse()
496         }
497 
498     @Test
defaultConnections_transportInfoNotWifi_wifiNotDefaultnull499     fun defaultConnections_transportInfoNotWifi_wifiNotDefault() =
500         testScope.runTest {
501             val latest by collectLastValue(underTest.defaultConnections)
502 
503             val capabilities =
504                 mock<NetworkCapabilities>().also {
505                     whenever(it.transportInfo).thenReturn(mock())
506                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
507                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
508                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
509                 }
510 
511             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
512 
513             assertThat(latest!!.wifi.isDefault).isFalse()
514         }
515 
516     @Test
defaultConnections_nullUnderlyingInfo_noErrornull517     fun defaultConnections_nullUnderlyingInfo_noError() {
518         val mainCapabilities =
519             mock<NetworkCapabilities>().also {
520                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
521                 whenever(it.transportInfo).thenReturn(null)
522                 whenever(it.underlyingNetworks).thenReturn(null)
523             }
524 
525         getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
526         // No assert, just verify no error
527     }
528 
529     @Test
defaultConnections_underlyingInfoHasNullCapabilities_noErrornull530     fun defaultConnections_underlyingInfoHasNullCapabilities_noError() {
531         val underlyingNetworkWithNull = mock<Network>()
532         whenever(connectivityManager.getNetworkCapabilities(underlyingNetworkWithNull))
533             .thenReturn(null)
534 
535         val mainCapabilities =
536             mock<NetworkCapabilities>().also {
537                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
538                 whenever(it.transportInfo).thenReturn(null)
539                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetworkWithNull))
540             }
541 
542         getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
543         // No assert, just verify no error
544     }
545 
546     // This test verifies our internal API for completeness, but we don't expect this case to ever
547     // happen in practice.
548     @Test
defaultConnections_cellular_underlyingCarrierMergedViaWifi_allDefaultnull549     fun defaultConnections_cellular_underlyingCarrierMergedViaWifi_allDefault() =
550         testScope.runTest {
551             val latest by collectLastValue(underTest.defaultConnections)
552 
553             // Underlying carrier merged network
554             val underlyingCarrierMergedNetwork = mock<Network>()
555             val carrierMergedInfo =
556                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
557             val underlyingCapabilities =
558                 mock<NetworkCapabilities>().also {
559                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
560                     whenever(it.transportInfo).thenReturn(carrierMergedInfo)
561                 }
562             whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
563                 .thenReturn(underlyingCapabilities)
564 
565             // Main network with underlying network
566             val mainCapabilities =
567                 mock<NetworkCapabilities>().also {
568                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
569                     whenever(it.transportInfo).thenReturn(null)
570                     whenever(it.underlyingNetworks)
571                         .thenReturn(listOf(underlyingCarrierMergedNetwork))
572                 }
573 
574             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
575 
576             assertThat(latest!!.mobile.isDefault).isTrue()
577             assertThat(latest!!.carrierMerged.isDefault).isTrue()
578             assertThat(latest!!.wifi.isDefault).isTrue()
579         }
580 
581     /**
582      * Test for b/225902574: VPN over VCN over W+ (aka VPN over VCN over carrier merged).
583      *
584      * Also see b/352162710#comment27 scenario #3 and b/352162710#comment30.
585      */
586     @Test
defaultConnections_cellular_underlyingCarrierMergedViaMobileWithVcnTransport_allDefaultnull587     fun defaultConnections_cellular_underlyingCarrierMergedViaMobileWithVcnTransport_allDefault() =
588         testScope.runTest {
589             val latest by collectLastValue(underTest.defaultConnections)
590 
591             // Underlying carrier merged network
592             val underlyingCarrierMergedNetwork = mock<Network>()
593             val carrierMergedInfo =
594                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
595             val underlyingWifi = newWifiNetwork(carrierMergedInfo)
596             val underlyingCapabilities =
597                 mock<NetworkCapabilities>().also {
598                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
599                     whenever(it.transportInfo).thenReturn(vcnTransportInfo)
600                     whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
601                 }
602             whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
603                 .thenReturn(underlyingCapabilities)
604 
605             // Main network with underlying network
606             val mainCapabilities =
607                 mock<NetworkCapabilities>().also {
608                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
609                     whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
610                     whenever(it.transportInfo).thenReturn(null)
611                     whenever(it.underlyingNetworks)
612                         .thenReturn(listOf(underlyingCarrierMergedNetwork))
613                 }
614 
615             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
616 
617             assertThat(latest!!.mobile.isDefault).isTrue()
618             assertThat(latest!!.carrierMerged.isDefault).isTrue()
619             assertThat(latest!!.wifi.isDefault).isTrue()
620         }
621 
622     @Test
defaultConnections_multipleTransports_multipleDefaultnull623     fun defaultConnections_multipleTransports_multipleDefault() =
624         testScope.runTest {
625             val latest by collectLastValue(underTest.defaultConnections)
626 
627             val capabilities =
628                 mock<NetworkCapabilities>().also {
629                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
630                     whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(true)
631                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
632                 }
633 
634             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
635 
636             assertThat(latest!!.mobile.isDefault).isTrue()
637             assertThat(latest!!.ethernet.isDefault).isTrue()
638             assertThat(latest!!.wifi.isDefault).isTrue()
639         }
640 
641     @Test
defaultConnections_hasValidated_isValidatedTruenull642     fun defaultConnections_hasValidated_isValidatedTrue() =
643         testScope.runTest {
644             val latest by collectLastValue(underTest.defaultConnections)
645 
646             val capabilities =
647                 mock<NetworkCapabilities>().also {
648                     whenever(it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
649                         .thenReturn(true)
650                 }
651 
652             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
653 
654             assertThat(latest!!.isValidated).isTrue()
655         }
656 
657     @Test
defaultConnections_noValidated_isValidatedFalsenull658     fun defaultConnections_noValidated_isValidatedFalse() =
659         testScope.runTest {
660             val latest by collectLastValue(underTest.defaultConnections)
661 
662             val capabilities =
663                 mock<NetworkCapabilities>().also {
664                     whenever(it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
665                         .thenReturn(false)
666                 }
667 
668             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
669 
670             assertThat(latest!!.isValidated).isFalse()
671         }
672 
673     @Test
vcnSubId_initiallyNullnull674     fun vcnSubId_initiallyNull() {
675         assertThat(underTest.vcnSubId.value).isNull()
676     }
677 
678     @Test
vcnSubId_tracksVcnTransportInfonull679     fun vcnSubId_tracksVcnTransportInfo() =
680         testScope.runTest {
681             val underlyingCell = newCellNetwork(SUB_1_ID)
682 
683             val latest by collectLastValue(underTest.vcnSubId)
684 
685             val capabilities =
686                 mock<NetworkCapabilities>().also {
687                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
688                     whenever(it.transportInfo).thenReturn(vcnTransportInfo)
689                     whenever(it.underlyingNetworks).thenReturn(listOf(underlyingCell))
690                 }
691 
692             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
693 
694             assertThat(latest).isEqualTo(SUB_1_ID)
695         }
696 
697     @Test
vcnSubId_filersOutInvalidnull698     fun vcnSubId_filersOutInvalid() =
699         testScope.runTest {
700             val underlyingCell = newCellNetwork(INVALID_SUBSCRIPTION_ID)
701 
702             val latest by collectLastValue(underTest.vcnSubId)
703 
704             val capabilities =
705                 mock<NetworkCapabilities>().also {
706                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
707                     whenever(it.transportInfo).thenReturn(vcnTransportInfo)
708                     whenever(it.underlyingNetworks).thenReturn(listOf(underlyingCell))
709                 }
710 
711             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
712 
713             assertThat(latest).isNull()
714         }
715 
716     @Test
vcnSubId_nullIfNoTransportInfonull717     fun vcnSubId_nullIfNoTransportInfo() =
718         testScope.runTest {
719             val latest by collectLastValue(underTest.vcnSubId)
720 
721             val capabilities =
722                 mock<NetworkCapabilities>().also {
723                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
724                     whenever(it.transportInfo).thenReturn(null)
725                 }
726 
727             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
728 
729             assertThat(latest).isNull()
730         }
731 
732     @Test
vcnSubId_nullIfVcnInfoIsNotCellularnull733     fun vcnSubId_nullIfVcnInfoIsNotCellular() =
734         testScope.runTest {
735             // If the underlying network of the VCN is a WiFi network, then there is no subId that
736             // could disagree with telephony's active data subscription id.
737 
738             val latest by collectLastValue(underTest.vcnSubId)
739 
740             val wifiInfo = mock<WifiInfo>()
741             val underlyingWifi = newWifiNetwork(wifiInfo)
742             val capabilities =
743                 mock<NetworkCapabilities>().also {
744                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
745                     whenever(it.transportInfo).thenReturn(vcnTransportInfo)
746                     whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
747                 }
748 
749             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
750 
751             assertThat(latest).isNull()
752         }
753 
754     @Test
vcnSubId_changingVcnInfoIsTrackednull755     fun vcnSubId_changingVcnInfoIsTracked() =
756         testScope.runTest {
757             val latest by collectLastValue(underTest.vcnSubId)
758 
759             val wifiInfo = mock<WifiInfo>()
760             val underlyingWifi = newWifiNetwork(wifiInfo)
761             val underlyingCell1 = newCellNetwork(SUB_1_ID)
762             val underlyingCell2 = newCellNetwork(SUB_2_ID)
763 
764             val capabilities =
765                 mock<NetworkCapabilities>().also {
766                     whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
767                     whenever(it.transportInfo).thenReturn(vcnTransportInfo)
768                     whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
769                 }
770 
771             // WIFI VCN info
772             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
773 
774             assertThat(latest).isNull()
775 
776             // Cellular VCN info with subId 1
777             whenever(capabilities.hasTransport(eq(TRANSPORT_CELLULAR))).thenReturn(true)
778             whenever(capabilities.transportInfo).thenReturn(vcnTransportInfo)
779             whenever(capabilities.underlyingNetworks).thenReturn(listOf(underlyingCell1))
780 
781             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
782 
783             assertThat(latest).isEqualTo(SUB_1_ID)
784 
785             // Cellular VCN info with subId 2
786             whenever(capabilities.transportInfo).thenReturn(vcnTransportInfo)
787             whenever(capabilities.underlyingNetworks).thenReturn(listOf(underlyingCell2))
788 
789             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
790 
791             assertThat(latest).isEqualTo(SUB_2_ID)
792 
793             // No VCN anymore
794             whenever(capabilities.transportInfo).thenReturn(null)
795 
796             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
797 
798             assertThat(latest).isNull()
799         }
800 
801     @Test
getMainOrUnderlyingWifiInfo_wifi_hasInfonull802     fun getMainOrUnderlyingWifiInfo_wifi_hasInfo() {
803         val wifiInfo = mock<WifiInfo>()
804         val capabilities =
805             mock<NetworkCapabilities>().also {
806                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
807                 whenever(it.transportInfo).thenReturn(wifiInfo)
808             }
809 
810         val result = capabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
811 
812         assertThat(result).isEqualTo(wifiInfo)
813     }
814 
815     @Test
getMainOrUnderlyingWifiInfo_vcnWithWifi_hasInfonull816     fun getMainOrUnderlyingWifiInfo_vcnWithWifi_hasInfo() {
817         val wifiInfo = mock<WifiInfo>()
818         val underlyingWifi = newWifiNetwork(wifiInfo)
819         val capabilities =
820             mock<NetworkCapabilities>().also {
821                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
822                 whenever(it.transportInfo).thenReturn(vcnTransportInfo)
823                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingWifi))
824             }
825 
826         val result = capabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
827 
828         assertThat(result).isEqualTo(wifiInfo)
829     }
830 
831     @Test
getMainOrUnderlyingWifiInfo_notCellularOrWifiTransport_noInfonull832     fun getMainOrUnderlyingWifiInfo_notCellularOrWifiTransport_noInfo() {
833         val capabilities =
834             mock<NetworkCapabilities>().also {
835                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
836                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
837                 whenever(it.transportInfo).thenReturn(mock<WifiInfo>())
838             }
839 
840         val result = capabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
841 
842         assertThat(result).isNull()
843     }
844 
845     @Test
getMainOrUnderlyingWifiInfo_cellular_underlyingWifi_hasInfonull846     fun getMainOrUnderlyingWifiInfo_cellular_underlyingWifi_hasInfo() {
847         val underlyingNetwork = mock<Network>()
848         val underlyingWifiInfo = mock<WifiInfo>()
849         val underlyingWifiCapabilities =
850             mock<NetworkCapabilities>().also {
851                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
852                 whenever(it.transportInfo).thenReturn(underlyingWifiInfo)
853             }
854         whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
855             .thenReturn(underlyingWifiCapabilities)
856 
857         // WHEN the main capabilities have an underlying wifi network
858         val mainCapabilities =
859             mock<NetworkCapabilities>().also {
860                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
861                 whenever(it.transportInfo).thenReturn(null)
862                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
863             }
864 
865         val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
866 
867         // THEN we fetch the underlying wifi info
868         assertThat(result).isEqualTo(underlyingWifiInfo)
869     }
870 
871     @Test
872     @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
getMainOrUnderlyingWifiInfo_notCellular_underlyingWifi_noInfonull873     fun getMainOrUnderlyingWifiInfo_notCellular_underlyingWifi_noInfo() {
874         val underlyingNetwork = mock<Network>()
875         val underlyingWifiInfo = mock<WifiInfo>()
876         val underlyingWifiCapabilities =
877             mock<NetworkCapabilities>().also {
878                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
879                 whenever(it.transportInfo).thenReturn(underlyingWifiInfo)
880             }
881         whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
882             .thenReturn(underlyingWifiCapabilities)
883 
884         // WHEN the main capabilities have an underlying wifi network but is *not* CELLULAR
885         val mainCapabilities =
886             mock<NetworkCapabilities>().also {
887                 whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(true)
888                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
889                 whenever(it.transportInfo).thenReturn(null)
890                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
891             }
892 
893         val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
894 
895         // THEN we DON'T fetch the underlying wifi info
896         assertThat(result).isNull()
897     }
898 
899     @Test
getMainOrUnderlyingWifiInfo_cellular_underlyingVcnWithWifi_hasInfonull900     fun getMainOrUnderlyingWifiInfo_cellular_underlyingVcnWithWifi_hasInfo() {
901         val wifiInfo = mock<WifiInfo>()
902         val underlyingNetwork = mock<Network>()
903 
904         // The Wifi network that is under the VCN network
905         val physicalWifiNetwork = newWifiNetwork(wifiInfo)
906 
907         val underlyingWifiCapabilities =
908             mock<NetworkCapabilities>().also {
909                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
910                 whenever(it.transportInfo).thenReturn(vcnTransportInfo)
911                 whenever(it.underlyingNetworks).thenReturn(listOf(physicalWifiNetwork))
912             }
913         whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
914             .thenReturn(underlyingWifiCapabilities)
915 
916         // WHEN the main capabilities have an underlying VCN network with wifi
917         val mainCapabilities =
918             mock<NetworkCapabilities>().also {
919                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
920                 whenever(it.transportInfo).thenReturn(null)
921                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
922             }
923 
924         val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
925 
926         // THEN we fetch the wifi info
927         assertThat(result).isEqualTo(wifiInfo)
928     }
929 
930     @Test
931     @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
getMainOrUnderlyingWifiInfo_notCellular_underlyingVcnWithWifi_noInfonull932     fun getMainOrUnderlyingWifiInfo_notCellular_underlyingVcnWithWifi_noInfo() {
933         val underlyingNetwork = mock<Network>()
934 
935         // The Wifi network that is under the VCN network
936         val physicalWifiNetwork = newWifiNetwork(mock<WifiInfo>())
937 
938         val underlyingWifiCapabilities =
939             mock<NetworkCapabilities>().also {
940                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
941                 whenever(it.transportInfo).thenReturn(vcnTransportInfo)
942                 whenever(it.underlyingNetworks).thenReturn(listOf(physicalWifiNetwork))
943             }
944         whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
945             .thenReturn(underlyingWifiCapabilities)
946 
947         // WHEN the main capabilities have an underlying wifi network but it is *not* CELLULAR
948         val mainCapabilities =
949             mock<NetworkCapabilities>().also {
950                 whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(true)
951                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
952                 whenever(it.transportInfo).thenReturn(null)
953                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
954             }
955 
956         val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
957 
958         // THEN we DON'T fetch the underlying wifi info
959         assertThat(result).isNull()
960     }
961 
962     @Test
getMainOrUnderlyingWifiInfo_cellular_underlyingCellularWithCarrierMerged_hasInfonull963     fun getMainOrUnderlyingWifiInfo_cellular_underlyingCellularWithCarrierMerged_hasInfo() {
964         // Underlying carrier merged network
965         val underlyingCarrierMergedNetwork = mock<Network>()
966         val carrierMergedInfo =
967             mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
968 
969         // The Wifi network that is under the VCN network
970         val physicalWifiNetwork = newWifiNetwork(carrierMergedInfo)
971 
972         val underlyingCapabilities =
973             mock<NetworkCapabilities>().also {
974                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
975                 whenever(it.transportInfo).thenReturn(vcnTransportInfo)
976                 whenever(it.underlyingNetworks).thenReturn(listOf(physicalWifiNetwork))
977             }
978         whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
979             .thenReturn(underlyingCapabilities)
980 
981         // Main network with underlying network
982         val mainCapabilities =
983             mock<NetworkCapabilities>().also {
984                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
985                 whenever(it.transportInfo).thenReturn(null)
986                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingCarrierMergedNetwork))
987             }
988 
989         val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
990 
991         assertThat(result).isEqualTo(carrierMergedInfo)
992         assertThat(result!!.isCarrierMerged).isTrue()
993     }
994 
995     @Test
getMainOrUnderlyingWifiInfo_multipleUnderlying_usesFirstNonNullnull996     fun getMainOrUnderlyingWifiInfo_multipleUnderlying_usesFirstNonNull() {
997         // First underlying: Not wifi
998         val underlyingNotWifiNetwork = mock<Network>()
999         val underlyingNotWifiCapabilities =
1000             mock<NetworkCapabilities>().also {
1001                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
1002                 whenever(it.transportInfo).thenReturn(null)
1003             }
1004         whenever(connectivityManager.getNetworkCapabilities(underlyingNotWifiNetwork))
1005             .thenReturn(underlyingNotWifiCapabilities)
1006 
1007         // Second underlying: wifi
1008         val underlyingWifiNetwork1 = mock<Network>()
1009         val underlyingWifiInfo1 = mock<WifiInfo>()
1010         val underlyingWifiCapabilities1 =
1011             mock<NetworkCapabilities>().also {
1012                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
1013                 whenever(it.transportInfo).thenReturn(underlyingWifiInfo1)
1014             }
1015         whenever(connectivityManager.getNetworkCapabilities(underlyingWifiNetwork1))
1016             .thenReturn(underlyingWifiCapabilities1)
1017 
1018         // Third underlying: also wifi
1019         val underlyingWifiNetwork2 = mock<Network>()
1020         val underlyingWifiInfo2 = mock<WifiInfo>()
1021         val underlyingWifiCapabilities2 =
1022             mock<NetworkCapabilities>().also {
1023                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
1024                 whenever(it.transportInfo).thenReturn(underlyingWifiInfo2)
1025             }
1026         whenever(connectivityManager.getNetworkCapabilities(underlyingWifiNetwork2))
1027             .thenReturn(underlyingWifiCapabilities2)
1028 
1029         // WHEN the main capabilities has multiple underlying networks
1030         val mainCapabilities =
1031             mock<NetworkCapabilities>().also {
1032                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
1033                 whenever(it.transportInfo).thenReturn(null)
1034                 whenever(it.underlyingNetworks)
1035                     .thenReturn(
1036                         listOf(
1037                             underlyingNotWifiNetwork,
1038                             underlyingWifiNetwork1,
1039                             underlyingWifiNetwork2,
1040                         )
1041                     )
1042             }
1043 
1044         val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
1045 
1046         // THEN the first wifi one is used
1047         assertThat(result).isEqualTo(underlyingWifiInfo1)
1048     }
1049 
1050     @Test
getMainOrUnderlyingWifiInfo_nestedUnderlying_doesNotLookAtNestednull1051     fun getMainOrUnderlyingWifiInfo_nestedUnderlying_doesNotLookAtNested() {
1052         // WHEN there are two layers of underlying networks...
1053 
1054         // Nested network
1055         val nestedUnderlyingNetwork = mock<Network>()
1056         val nestedWifiInfo = mock<WifiInfo>()
1057         val nestedCapabilities =
1058             mock<NetworkCapabilities>().also {
1059                 whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
1060                 whenever(it.transportInfo).thenReturn(nestedWifiInfo)
1061             }
1062         whenever(connectivityManager.getNetworkCapabilities(nestedUnderlyingNetwork))
1063             .thenReturn(nestedCapabilities)
1064 
1065         // Underlying network containing the nested network
1066         val underlyingNetwork = mock<Network>()
1067         val underlyingCapabilities =
1068             mock<NetworkCapabilities>().also {
1069                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
1070                 whenever(it.transportInfo).thenReturn(null)
1071                 whenever(it.underlyingNetworks).thenReturn(listOf(nestedUnderlyingNetwork))
1072             }
1073         whenever(connectivityManager.getNetworkCapabilities(underlyingNetwork))
1074             .thenReturn(underlyingCapabilities)
1075 
1076         // Main network containing the underlying network, which contains the nested network
1077         val mainCapabilities =
1078             mock<NetworkCapabilities>().also {
1079                 whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
1080                 whenever(it.transportInfo).thenReturn(null)
1081                 whenever(it.underlyingNetworks).thenReturn(listOf(underlyingNetwork))
1082             }
1083 
1084         val result = mainCapabilities.getMainOrUnderlyingWifiInfo(connectivityManager)
1085 
1086         // THEN only the first layer is checked, and the first layer has no wifi info
1087         assertThat(result).isNull()
1088     }
1089 
createAndSetReponull1090     private fun createAndSetRepo() {
1091         underTest =
1092             ConnectivityRepositoryImpl(
1093                 connectivityManager,
1094                 connectivitySlots,
1095                 context,
1096                 dumpManager,
1097                 logger,
1098                 testScope.backgroundScope,
1099                 tunerService,
1100             )
1101         testScope.runCurrent()
1102     }
1103 
getTunablenull1104     private fun getTunable(): TunerService.Tunable {
1105         val callbackCaptor = argumentCaptor<TunerService.Tunable>()
1106         verify(tunerService).addTunable(callbackCaptor.capture(), any())
1107         return callbackCaptor.firstValue
1108     }
1109 
setUpEthernetWifiMobileSlotNamesnull1110     private fun setUpEthernetWifiMobileSlotNames() {
1111         whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET))
1112             .thenReturn(ConnectivitySlot.ETHERNET)
1113         whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
1114         whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(ConnectivitySlot.MOBILE)
1115     }
1116 
getDefaultNetworkCallbacknull1117     private fun getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
1118         val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
1119         verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture())
1120         return callbackCaptor.firstValue
1121     }
1122 
1123     private companion object {
1124         private const val SLOT_ETHERNET = "ethernet"
1125         private const val SLOT_WIFI = "wifi"
1126         private const val SLOT_MOBILE = "mobile"
1127 
1128         private const val SUB_1_ID = 1
1129         private const val SUB_2_ID = 2
1130 
1131         const val NETWORK_ID = 45
<lambda>null1132         val NETWORK = mock<Network>().apply { whenever(this.getNetId()).thenReturn(NETWORK_ID) }
1133     }
1134 }
1135