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.mobile.data.repository.prod
18 
19 import android.content.BroadcastReceiver
20 import android.content.Context
21 import android.content.Intent
22 import android.net.ConnectivityManager
23 import android.net.ConnectivityManager.NetworkCallback
24 import android.platform.test.annotations.EnableFlags
25 import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN
26 import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN
27 import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
28 import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
29 import android.telephony.NetworkRegistrationInfo
30 import android.telephony.NetworkRegistrationInfo.DOMAIN_PS
31 import android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
32 import android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME
33 import android.telephony.ServiceState
34 import android.telephony.ServiceState.STATE_IN_SERVICE
35 import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
36 import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
37 import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
38 import android.telephony.TelephonyCallback
39 import android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener
40 import android.telephony.TelephonyCallback.DataActivityListener
41 import android.telephony.TelephonyCallback.DisplayInfoListener
42 import android.telephony.TelephonyCallback.ServiceStateListener
43 import android.telephony.TelephonyDisplayInfo
44 import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
45 import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
46 import android.telephony.TelephonyManager
47 import android.telephony.TelephonyManager.DATA_ACTIVITY_DORMANT
48 import android.telephony.TelephonyManager.DATA_ACTIVITY_IN
49 import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
50 import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
51 import android.telephony.TelephonyManager.DATA_ACTIVITY_OUT
52 import android.telephony.TelephonyManager.DATA_CONNECTED
53 import android.telephony.TelephonyManager.DATA_CONNECTING
54 import android.telephony.TelephonyManager.DATA_DISCONNECTED
55 import android.telephony.TelephonyManager.DATA_DISCONNECTING
56 import android.telephony.TelephonyManager.DATA_HANDOVER_IN_PROGRESS
57 import android.telephony.TelephonyManager.DATA_SUSPENDED
58 import android.telephony.TelephonyManager.DATA_UNKNOWN
59 import android.telephony.TelephonyManager.ERI_OFF
60 import android.telephony.TelephonyManager.ERI_ON
61 import android.telephony.TelephonyManager.EXTRA_CARRIER_ID
62 import android.telephony.TelephonyManager.EXTRA_PLMN
63 import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
64 import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
65 import android.telephony.TelephonyManager.EXTRA_SPN
66 import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
67 import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
68 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
69 import androidx.test.filters.SmallTest
70 import com.android.settingslib.mobile.MobileMappings
71 import com.android.systemui.SysuiTestCase
72 import com.android.systemui.coroutines.collectLastValue
73 import com.android.systemui.flags.FakeFeatureFlagsClassic
74 import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
75 import com.android.systemui.log.table.TableLogBuffer
76 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
77 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
78 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
79 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
80 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
81 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
82 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
83 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
84 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
85 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
86 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
87 import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
88 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
89 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
90 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
91 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo
92 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
93 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
94 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
95 import com.android.systemui.util.mockito.any
96 import com.android.systemui.util.mockito.argumentCaptor
97 import com.android.systemui.util.mockito.mock
98 import com.android.systemui.util.mockito.whenever
99 import com.android.systemui.util.mockito.withArgCaptor
100 import com.google.common.truth.Truth.assertThat
101 import kotlinx.coroutines.ExperimentalCoroutinesApi
102 import kotlinx.coroutines.flow.MutableStateFlow
103 import kotlinx.coroutines.flow.launchIn
104 import kotlinx.coroutines.flow.onEach
105 import kotlinx.coroutines.test.TestScope
106 import kotlinx.coroutines.test.UnconfinedTestDispatcher
107 import kotlinx.coroutines.test.runTest
108 import org.junit.Before
109 import org.junit.Test
110 import org.mockito.Mock
111 import org.mockito.Mockito.verify
112 import org.mockito.MockitoAnnotations
113 
114 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
115 @OptIn(ExperimentalCoroutinesApi::class)
116 @SmallTest
117 class MobileConnectionRepositoryTest : SysuiTestCase() {
118     private lateinit var underTest: MobileConnectionRepositoryImpl
119     private lateinit var connectionsRepo: FakeMobileConnectionsRepository
120 
121     private val flags =
<lambda>null122         FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
123 
124     @Mock private lateinit var connectivityManager: ConnectivityManager
125     @Mock private lateinit var telephonyManager: TelephonyManager
126     @Mock private lateinit var logger: MobileInputLogger
127     @Mock private lateinit var tableLogger: TableLogBuffer
128     @Mock private lateinit var context: Context
129 
130     private val mobileMappings = FakeMobileMappingsProxy()
131     private val systemUiCarrierConfig =
132         SystemUiCarrierConfig(
133             SUB_1_ID,
134             createTestConfig(),
135         )
136 
137     private val testDispatcher = UnconfinedTestDispatcher()
138     private val testScope = TestScope(testDispatcher)
139 
140     private val subscriptionModel: MutableStateFlow<SubscriptionModel?> =
141         MutableStateFlow(
142             SubscriptionModel(
143                 subscriptionId = SUB_1_ID,
144                 carrierName = DEFAULT_NAME,
145                 profileClass = PROFILE_CLASS_UNSET,
146             )
147         )
148 
149     @Before
setUpnull150     fun setUp() {
151         MockitoAnnotations.initMocks(this)
152         whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
153 
154         connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
155 
156         underTest =
157             MobileConnectionRepositoryImpl(
158                 SUB_1_ID,
159                 context,
160                 subscriptionModel,
161                 DEFAULT_NAME_MODEL,
162                 SEP,
163                 connectivityManager,
164                 telephonyManager,
165                 systemUiCarrierConfig,
166                 fakeBroadcastDispatcher,
167                 mobileMappings,
168                 testDispatcher,
169                 logger,
170                 tableLogger,
171                 flags,
172                 testScope.backgroundScope,
173             )
174     }
175 
176     @Test
emergencyOnlynull177     fun emergencyOnly() =
178         testScope.runTest {
179             var latest: Boolean? = null
180             val job = underTest.isEmergencyOnly.onEach { latest = it }.launchIn(this)
181 
182             val serviceState = ServiceState()
183             serviceState.isEmergencyOnly = true
184 
185             getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
186 
187             assertThat(latest).isEqualTo(true)
188 
189             job.cancel()
190         }
191 
192     @Test
emergencyOnly_togglesnull193     fun emergencyOnly_toggles() =
194         testScope.runTest {
195             var latest: Boolean? = null
196             val job = underTest.isEmergencyOnly.onEach { latest = it }.launchIn(this)
197 
198             val callback = getTelephonyCallbackForType<ServiceStateListener>()
199             callback.onServiceStateChanged(ServiceState().also { it.isEmergencyOnly = true })
200             assertThat(latest).isTrue()
201 
202             callback.onServiceStateChanged(ServiceState().also { it.isEmergencyOnly = false })
203 
204             assertThat(latest).isFalse()
205 
206             job.cancel()
207         }
208 
209     @Test
cdmaLevelUpdatesnull210     fun cdmaLevelUpdates() =
211         testScope.runTest {
212             var latest: Int? = null
213             val job = underTest.cdmaLevel.onEach { latest = it }.launchIn(this)
214 
215             val callback = getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>()
216             var strength = signalStrength(gsmLevel = 1, cdmaLevel = 2, isGsm = true)
217             callback.onSignalStrengthsChanged(strength)
218 
219             assertThat(latest).isEqualTo(2)
220 
221             // gsmLevel updates, no change to cdmaLevel
222             strength = signalStrength(gsmLevel = 3, cdmaLevel = 2, isGsm = true)
223             callback.onSignalStrengthsChanged(strength)
224 
225             assertThat(latest).isEqualTo(2)
226 
227             job.cancel()
228         }
229 
230     @Test
gsmLevelUpdatesnull231     fun gsmLevelUpdates() =
232         testScope.runTest {
233             var latest: Int? = null
234             val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
235 
236             val callback = getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>()
237             var strength = signalStrength(gsmLevel = 1, cdmaLevel = 2, isGsm = true)
238             callback.onSignalStrengthsChanged(strength)
239 
240             assertThat(latest).isEqualTo(1)
241 
242             strength = signalStrength(gsmLevel = 3, cdmaLevel = 2, isGsm = true)
243             callback.onSignalStrengthsChanged(strength)
244 
245             assertThat(latest).isEqualTo(3)
246 
247             job.cancel()
248         }
249 
250     @Test
isGsmnull251     fun isGsm() =
252         testScope.runTest {
253             var latest: Boolean? = null
254             val job = underTest.isGsm.onEach { latest = it }.launchIn(this)
255 
256             val callback = getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>()
257             var strength = signalStrength(gsmLevel = 1, cdmaLevel = 2, isGsm = true)
258             callback.onSignalStrengthsChanged(strength)
259 
260             assertThat(latest).isTrue()
261 
262             strength = signalStrength(gsmLevel = 1, cdmaLevel = 2, isGsm = false)
263             callback.onSignalStrengthsChanged(strength)
264 
265             assertThat(latest).isFalse()
266 
267             job.cancel()
268         }
269 
270     @Test
dataConnectionState_connectednull271     fun dataConnectionState_connected() =
272         testScope.runTest {
273             var latest: DataConnectionState? = null
274             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
275 
276             val callback =
277                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
278             callback.onDataConnectionStateChanged(DATA_CONNECTED, 200 /* unused */)
279 
280             assertThat(latest).isEqualTo(DataConnectionState.Connected)
281 
282             job.cancel()
283         }
284 
285     @Test
dataConnectionState_connectingnull286     fun dataConnectionState_connecting() =
287         testScope.runTest {
288             var latest: DataConnectionState? = null
289             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
290 
291             val callback =
292                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
293             callback.onDataConnectionStateChanged(DATA_CONNECTING, 200 /* unused */)
294 
295             assertThat(latest).isEqualTo(DataConnectionState.Connecting)
296 
297             job.cancel()
298         }
299 
300     @Test
dataConnectionState_disconnectednull301     fun dataConnectionState_disconnected() =
302         testScope.runTest {
303             var latest: DataConnectionState? = null
304             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
305 
306             val callback =
307                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
308             callback.onDataConnectionStateChanged(DATA_DISCONNECTED, 200 /* unused */)
309 
310             assertThat(latest).isEqualTo(DataConnectionState.Disconnected)
311 
312             job.cancel()
313         }
314 
315     @Test
dataConnectionState_disconnectingnull316     fun dataConnectionState_disconnecting() =
317         testScope.runTest {
318             var latest: DataConnectionState? = null
319             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
320 
321             val callback =
322                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
323             callback.onDataConnectionStateChanged(DATA_DISCONNECTING, 200 /* unused */)
324 
325             assertThat(latest).isEqualTo(DataConnectionState.Disconnecting)
326 
327             job.cancel()
328         }
329 
330     @Test
dataConnectionState_suspendednull331     fun dataConnectionState_suspended() =
332         testScope.runTest {
333             var latest: DataConnectionState? = null
334             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
335 
336             val callback =
337                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
338             callback.onDataConnectionStateChanged(DATA_SUSPENDED, 200 /* unused */)
339 
340             assertThat(latest).isEqualTo(DataConnectionState.Suspended)
341 
342             job.cancel()
343         }
344 
345     @Test
dataConnectionState_handoverInProgressnull346     fun dataConnectionState_handoverInProgress() =
347         testScope.runTest {
348             var latest: DataConnectionState? = null
349             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
350 
351             val callback =
352                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
353             callback.onDataConnectionStateChanged(DATA_HANDOVER_IN_PROGRESS, 200 /* unused */)
354 
355             assertThat(latest).isEqualTo(DataConnectionState.HandoverInProgress)
356 
357             job.cancel()
358         }
359 
360     @Test
dataConnectionState_unknownnull361     fun dataConnectionState_unknown() =
362         testScope.runTest {
363             var latest: DataConnectionState? = null
364             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
365 
366             val callback =
367                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
368             callback.onDataConnectionStateChanged(DATA_UNKNOWN, 200 /* unused */)
369 
370             assertThat(latest).isEqualTo(DataConnectionState.Unknown)
371 
372             job.cancel()
373         }
374 
375     @Test
dataConnectionState_invalidnull376     fun dataConnectionState_invalid() =
377         testScope.runTest {
378             var latest: DataConnectionState? = null
379             val job = underTest.dataConnectionState.onEach { latest = it }.launchIn(this)
380 
381             val callback =
382                 getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
383             callback.onDataConnectionStateChanged(45, 200 /* unused */)
384 
385             assertThat(latest).isEqualTo(DataConnectionState.Invalid)
386 
387             job.cancel()
388         }
389 
390     @Test
dataActivitynull391     fun dataActivity() =
392         testScope.runTest {
393             var latest: DataActivityModel? = null
394             val job = underTest.dataActivityDirection.onEach { latest = it }.launchIn(this)
395 
396             val callback = getTelephonyCallbackForType<DataActivityListener>()
397             callback.onDataActivity(DATA_ACTIVITY_INOUT)
398 
399             assertThat(latest).isEqualTo(DATA_ACTIVITY_INOUT.toMobileDataActivityModel())
400 
401             job.cancel()
402         }
403 
404     @Test
carrierId_initialValueCapturednull405     fun carrierId_initialValueCaptured() =
406         testScope.runTest {
407             whenever(telephonyManager.simCarrierId).thenReturn(1234)
408 
409             var latest: Int? = null
410             val job = underTest.carrierId.onEach { latest = it }.launchIn(this)
411 
412             assertThat(latest).isEqualTo(1234)
413 
414             job.cancel()
415         }
416 
417     @Test
carrierId_updatesOnBroadcastnull418     fun carrierId_updatesOnBroadcast() =
419         testScope.runTest {
420             whenever(telephonyManager.simCarrierId).thenReturn(1234)
421 
422             var latest: Int? = null
423             val job = underTest.carrierId.onEach { latest = it }.launchIn(this)
424 
425             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
426                 context,
427                 carrierIdIntent(carrierId = 4321),
428             )
429 
430             assertThat(latest).isEqualTo(4321)
431 
432             job.cancel()
433         }
434 
435     @Test
carrierNetworkChangenull436     fun carrierNetworkChange() =
437         testScope.runTest {
438             var latest: Boolean? = null
439             val job = underTest.carrierNetworkChangeActive.onEach { latest = it }.launchIn(this)
440 
441             val callback = getTelephonyCallbackForType<TelephonyCallback.CarrierNetworkListener>()
442             callback.onCarrierNetworkChange(true)
443 
444             assertThat(latest).isEqualTo(true)
445 
446             job.cancel()
447         }
448 
449     @Test
networkType_defaultnull450     fun networkType_default() =
451         testScope.runTest {
452             var latest: ResolvedNetworkType? = null
453             val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
454 
455             val expected = UnknownNetworkType
456 
457             assertThat(latest).isEqualTo(expected)
458 
459             job.cancel()
460         }
461 
462     @Test
networkType_unknown_hasCorrectKeynull463     fun networkType_unknown_hasCorrectKey() =
464         testScope.runTest {
465             var latest: ResolvedNetworkType? = null
466             val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
467 
468             val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
469             val ti =
470                 telephonyDisplayInfo(
471                     networkType = NETWORK_TYPE_UNKNOWN,
472                     overrideNetworkType = NETWORK_TYPE_UNKNOWN,
473                 )
474 
475             callback.onDisplayInfoChanged(ti)
476 
477             val expected = UnknownNetworkType
478             assertThat(latest).isEqualTo(expected)
479             assertThat(latest!!.lookupKey).isEqualTo(MobileMappings.toIconKey(NETWORK_TYPE_UNKNOWN))
480 
481             job.cancel()
482         }
483 
484     @Test
networkType_updatesUsingDefaultnull485     fun networkType_updatesUsingDefault() =
486         testScope.runTest {
487             var latest: ResolvedNetworkType? = null
488             val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
489 
490             val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
491             val overrideType = OVERRIDE_NETWORK_TYPE_NONE
492             val type = NETWORK_TYPE_LTE
493             val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = overrideType)
494             callback.onDisplayInfoChanged(ti)
495 
496             val expected = DefaultNetworkType(mobileMappings.toIconKey(type))
497             assertThat(latest).isEqualTo(expected)
498 
499             job.cancel()
500         }
501 
502     @Test
networkType_updatesUsingOverridenull503     fun networkType_updatesUsingOverride() =
504         testScope.runTest {
505             var latest: ResolvedNetworkType? = null
506             val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
507 
508             val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
509             val type = OVERRIDE_NETWORK_TYPE_LTE_CA
510             val ti = telephonyDisplayInfo(networkType = type, overrideNetworkType = type)
511             callback.onDisplayInfoChanged(ti)
512 
513             val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
514             assertThat(latest).isEqualTo(expected)
515 
516             job.cancel()
517         }
518 
519     @Test
networkType_unknownNetworkWithOverride_usesOverrideKeynull520     fun networkType_unknownNetworkWithOverride_usesOverrideKey() =
521         testScope.runTest {
522             var latest: ResolvedNetworkType? = null
523             val job = underTest.resolvedNetworkType.onEach { latest = it }.launchIn(this)
524 
525             val callback = getTelephonyCallbackForType<TelephonyCallback.DisplayInfoListener>()
526             val unknown = NETWORK_TYPE_UNKNOWN
527             val type = OVERRIDE_NETWORK_TYPE_LTE_CA
528             val ti = telephonyDisplayInfo(unknown, type)
529             callback.onDisplayInfoChanged(ti)
530 
531             val expected = OverrideNetworkType(mobileMappings.toIconKeyOverride(type))
532             assertThat(latest).isEqualTo(expected)
533 
534             job.cancel()
535         }
536 
537     @Test
dataEnabled_initial_falsenull538     fun dataEnabled_initial_false() =
539         testScope.runTest {
540             whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
541 
542             assertThat(underTest.dataEnabled.value).isFalse()
543         }
544 
545     @Test
isDataEnabled_tracksTelephonyCallbacknull546     fun isDataEnabled_tracksTelephonyCallback() =
547         testScope.runTest {
548             var latest: Boolean? = null
549             val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
550 
551             whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
552             assertThat(underTest.dataEnabled.value).isFalse()
553 
554             val callback = getTelephonyCallbackForType<TelephonyCallback.DataEnabledListener>()
555 
556             callback.onDataEnabledChanged(true, 1)
557             assertThat(latest).isTrue()
558 
559             callback.onDataEnabledChanged(false, 1)
560             assertThat(latest).isFalse()
561 
562             job.cancel()
563         }
564 
565     @Test
numberOfLevels_isDefaultnull566     fun numberOfLevels_isDefault() =
567         testScope.runTest {
568             var latest: Int? = null
569             val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
570 
571             assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS)
572 
573             job.cancel()
574         }
575 
576     @Test
roaming_cdma_queriesTelephonyManagernull577     fun roaming_cdma_queriesTelephonyManager() =
578         testScope.runTest {
579             var latest: Boolean? = null
580             val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
581 
582             val cb = getTelephonyCallbackForType<ServiceStateListener>()
583 
584             // CDMA roaming is off, GSM roaming is on
585             whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
586             cb.onServiceStateChanged(ServiceState().also { it.roaming = true })
587 
588             assertThat(latest).isFalse()
589 
590             // CDMA roaming is on, GSM roaming is off
591             whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_ON)
592             cb.onServiceStateChanged(ServiceState().also { it.roaming = false })
593 
594             assertThat(latest).isTrue()
595 
596             job.cancel()
597         }
598 
599     /**
600      * [TelephonyManager.getCdmaEnhancedRoamingIndicatorDisplayNumber] returns -1 if the service is
601      * not running or if there is an error while retrieving the cdma ERI
602      */
603     @Test
cdmaRoaming_ignoresNegativeOnenull604     fun cdmaRoaming_ignoresNegativeOne() =
605         testScope.runTest {
606             var latest: Boolean? = null
607             val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
608 
609             val serviceState = ServiceState()
610             serviceState.roaming = false
611 
612             val cb = getTelephonyCallbackForType<ServiceStateListener>()
613 
614             // CDMA roaming is unavailable (-1), GSM roaming is off
615             whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(-1)
616             cb.onServiceStateChanged(serviceState)
617 
618             assertThat(latest).isFalse()
619 
620             job.cancel()
621         }
622 
623     @Test
roaming_gsm_queriesDisplayInfo_viaDisplayInfonull624     fun roaming_gsm_queriesDisplayInfo_viaDisplayInfo() =
625         testScope.runTest {
626             // GIVEN flag is true
627             flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true)
628 
629             // Re-create the repository, because the flag is read at init
630             underTest =
631                 MobileConnectionRepositoryImpl(
632                     SUB_1_ID,
633                     context,
634                     subscriptionModel,
635                     DEFAULT_NAME_MODEL,
636                     SEP,
637                     connectivityManager,
638                     telephonyManager,
639                     systemUiCarrierConfig,
640                     fakeBroadcastDispatcher,
641                     mobileMappings,
642                     testDispatcher,
643                     logger,
644                     tableLogger,
645                     flags,
646                     testScope.backgroundScope,
647                 )
648 
649             var latest: Boolean? = null
650             val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
651 
652             val cb = getTelephonyCallbackForType<DisplayInfoListener>()
653 
654             // CDMA roaming is off, GSM roaming is off
655             whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
656             cb.onDisplayInfoChanged(
657                 TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, false)
658             )
659 
660             assertThat(latest).isFalse()
661 
662             // CDMA roaming is off, GSM roaming is on
663             cb.onDisplayInfoChanged(
664                 TelephonyDisplayInfo(NETWORK_TYPE_LTE, NETWORK_TYPE_UNKNOWN, true)
665             )
666 
667             assertThat(latest).isTrue()
668 
669             job.cancel()
670         }
671 
672     @Test
roaming_gsm_queriesDisplayInfo_viaServiceStatenull673     fun roaming_gsm_queriesDisplayInfo_viaServiceState() =
674         testScope.runTest {
675             // GIVEN flag is false
676             flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, false)
677 
678             // Re-create the repository, because the flag is read at init
679             underTest =
680                 MobileConnectionRepositoryImpl(
681                     SUB_1_ID,
682                     context,
683                     subscriptionModel,
684                     DEFAULT_NAME_MODEL,
685                     SEP,
686                     connectivityManager,
687                     telephonyManager,
688                     systemUiCarrierConfig,
689                     fakeBroadcastDispatcher,
690                     mobileMappings,
691                     testDispatcher,
692                     logger,
693                     tableLogger,
694                     flags,
695                     testScope.backgroundScope,
696                 )
697 
698             var latest: Boolean? = null
699             val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
700 
701             val cb = getTelephonyCallbackForType<ServiceStateListener>()
702 
703             // CDMA roaming is off, GSM roaming is off
704             whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
705             cb.onServiceStateChanged(ServiceState().also { it.roaming = false })
706 
707             assertThat(latest).isFalse()
708 
709             // CDMA roaming is off, GSM roaming is on
710             cb.onServiceStateChanged(ServiceState().also { it.roaming = true })
711 
712             assertThat(latest).isTrue()
713 
714             job.cancel()
715         }
716 
717     @Test
activity_updatesFromCallbacknull718     fun activity_updatesFromCallback() =
719         testScope.runTest {
720             var latest: DataActivityModel? = null
721             val job = underTest.dataActivityDirection.onEach { latest = it }.launchIn(this)
722 
723             assertThat(latest)
724                 .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
725 
726             val cb = getTelephonyCallbackForType<DataActivityListener>()
727             cb.onDataActivity(DATA_ACTIVITY_IN)
728             assertThat(latest)
729                 .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = false))
730 
731             cb.onDataActivity(DATA_ACTIVITY_OUT)
732             assertThat(latest)
733                 .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = true))
734 
735             cb.onDataActivity(DATA_ACTIVITY_INOUT)
736             assertThat(latest)
737                 .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
738 
739             cb.onDataActivity(DATA_ACTIVITY_NONE)
740             assertThat(latest)
741                 .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
742 
743             cb.onDataActivity(DATA_ACTIVITY_DORMANT)
744             assertThat(latest)
745                 .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
746 
747             cb.onDataActivity(1234)
748             assertThat(latest)
749                 .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
750 
751             job.cancel()
752         }
753 
754     @Test
networkNameForSubId_updatesnull755     fun networkNameForSubId_updates() =
756         testScope.runTest {
757             var latest: NetworkNameModel? = null
758             val job = underTest.carrierName.onEach { latest = it }.launchIn(this)
759 
760             subscriptionModel.value =
761                 SubscriptionModel(
762                     subscriptionId = SUB_1_ID,
763                     carrierName = DEFAULT_NAME,
764                     profileClass = PROFILE_CLASS_UNSET,
765                 )
766 
767             assertThat(latest?.name).isEqualTo(DEFAULT_NAME)
768 
769             val updatedName = "Derived Carrier"
770             subscriptionModel.value =
771                 SubscriptionModel(
772                     subscriptionId = SUB_1_ID,
773                     carrierName = updatedName,
774                     profileClass = PROFILE_CLASS_UNSET,
775                 )
776 
777             assertThat(latest?.name).isEqualTo(updatedName)
778 
779             job.cancel()
780         }
781 
782     @Test
networkNameForSubId_defaultWhenSubscriptionModelNullnull783     fun networkNameForSubId_defaultWhenSubscriptionModelNull() =
784         testScope.runTest {
785             var latest: NetworkNameModel? = null
786             val job = underTest.carrierName.onEach { latest = it }.launchIn(this)
787 
788             subscriptionModel.value = null
789 
790             assertThat(latest?.name).isEqualTo(DEFAULT_NAME)
791 
792             job.cancel()
793         }
794 
795     @Test
networkName_defaultnull796     fun networkName_default() =
797         testScope.runTest {
798             var latest: NetworkNameModel? = null
799             val job = underTest.networkName.onEach { latest = it }.launchIn(this)
800 
801             assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
802 
803             job.cancel()
804         }
805 
806     @Test
networkName_usesBroadcastInfo_returnsDerivednull807     fun networkName_usesBroadcastInfo_returnsDerived() =
808         testScope.runTest {
809             var latest: NetworkNameModel? = null
810             val job = underTest.networkName.onEach { latest = it }.launchIn(this)
811 
812             val intent = spnIntent()
813             val captor = argumentCaptor<BroadcastReceiver>()
814             verify(context).registerReceiver(captor.capture(), any())
815             captor.value!!.onReceive(context, intent)
816 
817             assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
818 
819             job.cancel()
820         }
821 
822     @Test
networkName_broadcastNotForThisSubId_keepsOldValuenull823     fun networkName_broadcastNotForThisSubId_keepsOldValue() =
824         testScope.runTest {
825             var latest: NetworkNameModel? = null
826             val job = underTest.networkName.onEach { latest = it }.launchIn(this)
827 
828             val intent = spnIntent()
829             val captor = argumentCaptor<BroadcastReceiver>()
830             verify(context).registerReceiver(captor.capture(), any())
831             captor.value!!.onReceive(context, intent)
832 
833             assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
834 
835             // WHEN an intent with a different subId is sent
836             val wrongSubIntent = spnIntent(subId = 101)
837 
838             captor.value!!.onReceive(context, wrongSubIntent)
839 
840             // THEN the previous intent's name is still used
841             assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
842 
843             job.cancel()
844         }
845 
846     @Test
networkName_broadcastHasNoData_updatesToDefaultnull847     fun networkName_broadcastHasNoData_updatesToDefault() =
848         testScope.runTest {
849             var latest: NetworkNameModel? = null
850             val job = underTest.networkName.onEach { latest = it }.launchIn(this)
851 
852             val intent = spnIntent()
853             val captor = argumentCaptor<BroadcastReceiver>()
854             verify(context).registerReceiver(captor.capture(), any())
855             captor.value!!.onReceive(context, intent)
856 
857             assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
858 
859             val intentWithoutInfo =
860                 spnIntent(
861                     showSpn = false,
862                     showPlmn = false,
863                 )
864 
865             captor.value!!.onReceive(context, intentWithoutInfo)
866 
867             assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
868 
869             job.cancel()
870         }
871 
872     @Test
networkName_usingEagerStrategy_retainsNameBetweenSubscribersnull873     fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers() =
874         testScope.runTest {
875             // Use the [StateFlow.value] getter so we can prove that the collection happens
876             // even when there is no [Job]
877 
878             // Starts out default
879             assertThat(underTest.networkName.value).isEqualTo(DEFAULT_NAME_MODEL)
880 
881             val intent = spnIntent()
882             val captor = argumentCaptor<BroadcastReceiver>()
883             verify(context).registerReceiver(captor.capture(), any())
884             captor.value!!.onReceive(context, intent)
885 
886             // The value is still there despite no active subscribers
887             assertThat(underTest.networkName.value).isEqualTo(intent.toNetworkNameModel(SEP))
888         }
889 
890     @Test
operatorAlphaShort_trackednull891     fun operatorAlphaShort_tracked() =
892         testScope.runTest {
893             var latest: String? = null
894 
895             val job = underTest.operatorAlphaShort.onEach { latest = it }.launchIn(this)
896 
897             val shortName = "short name"
898             val serviceState = ServiceState()
899             serviceState.setOperatorName(
900                 /* longName */ "long name",
901                 /* shortName */ shortName,
902                 /* numeric */ "12345",
903             )
904 
905             getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
906 
907             assertThat(latest).isEqualTo(shortName)
908 
909             job.cancel()
910         }
911 
912     @Test
isInService_notIwlannull913     fun isInService_notIwlan() =
914         testScope.runTest {
915             var latest: Boolean? = null
916             val job = underTest.isInService.onEach { latest = it }.launchIn(this)
917 
918             val nriInService =
919                 NetworkRegistrationInfo.Builder()
920                     .setDomain(DOMAIN_PS)
921                     .setTransportType(TRANSPORT_TYPE_WWAN)
922                     .setRegistrationState(REGISTRATION_STATE_HOME)
923                     .build()
924 
925             getTelephonyCallbackForType<ServiceStateListener>()
926                 .onServiceStateChanged(
927                     ServiceState().also {
928                         it.voiceRegState = STATE_IN_SERVICE
929                         it.addNetworkRegistrationInfo(nriInService)
930                     }
931                 )
932 
933             assertThat(latest).isTrue()
934 
935             getTelephonyCallbackForType<ServiceStateListener>()
936                 .onServiceStateChanged(
937                     ServiceState().also {
938                         it.voiceRegState = STATE_OUT_OF_SERVICE
939                         it.addNetworkRegistrationInfo(nriInService)
940                     }
941                 )
942             assertThat(latest).isTrue()
943 
944             val nriNotInService =
945                 NetworkRegistrationInfo.Builder()
946                     .setDomain(DOMAIN_PS)
947                     .setTransportType(TRANSPORT_TYPE_WWAN)
948                     .setRegistrationState(REGISTRATION_STATE_DENIED)
949                     .build()
950             getTelephonyCallbackForType<ServiceStateListener>()
951                 .onServiceStateChanged(
952                     ServiceState().also {
953                         it.voiceRegState = STATE_OUT_OF_SERVICE
954                         it.addNetworkRegistrationInfo(nriNotInService)
955                     }
956                 )
957             assertThat(latest).isFalse()
958 
959             job.cancel()
960         }
961 
962     @Test
isInService_isIwlan_voiceOutOfService_dataInServicenull963     fun isInService_isIwlan_voiceOutOfService_dataInService() =
964         testScope.runTest {
965             var latest: Boolean? = null
966             val job = underTest.isInService.onEach { latest = it }.launchIn(this)
967 
968             val iwlanData =
969                 NetworkRegistrationInfo.Builder()
970                     .setDomain(DOMAIN_PS)
971                     .setTransportType(TRANSPORT_TYPE_WLAN)
972                     .setRegistrationState(REGISTRATION_STATE_HOME)
973                     .build()
974             val serviceState =
975                 ServiceState().also {
976                     it.voiceRegState = STATE_OUT_OF_SERVICE
977                     it.addNetworkRegistrationInfo(iwlanData)
978                 }
979 
980             getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
981             assertThat(latest).isFalse()
982 
983             job.cancel()
984         }
985 
986     @Test
987     @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
isNonTerrestrial_updatesFromCallback0null988     fun isNonTerrestrial_updatesFromCallback0() =
989         testScope.runTest {
990             val latest by collectLastValue(underTest.isNonTerrestrial)
991 
992             // Starts out false
993             assertThat(latest).isFalse()
994 
995             val callback = getTelephonyCallbackForType<CarrierRoamingNtnModeListener>()
996 
997             callback.onCarrierRoamingNtnModeChanged(true)
998             assertThat(latest).isTrue()
999 
1000             callback.onCarrierRoamingNtnModeChanged(false)
1001             assertThat(latest).isFalse()
1002         }
1003 
1004     @Test
numberOfLevels_usesCarrierConfignull1005     fun numberOfLevels_usesCarrierConfig() =
1006         testScope.runTest {
1007             var latest: Int? = null
1008             val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
1009 
1010             assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS)
1011 
1012             systemUiCarrierConfig.processNewCarrierConfig(
1013                 configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
1014             )
1015 
1016             assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS + 1)
1017 
1018             systemUiCarrierConfig.processNewCarrierConfig(
1019                 configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
1020             )
1021 
1022             assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS)
1023 
1024             job.cancel()
1025         }
1026 
1027     @Test
inflateSignalStrength_usesCarrierConfignull1028     fun inflateSignalStrength_usesCarrierConfig() =
1029         testScope.runTest {
1030             val latest by collectLastValue(underTest.inflateSignalStrength)
1031 
1032             assertThat(latest).isEqualTo(false)
1033 
1034             systemUiCarrierConfig.processNewCarrierConfig(
1035                 configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
1036             )
1037 
1038             assertThat(latest).isEqualTo(true)
1039 
1040             systemUiCarrierConfig.processNewCarrierConfig(
1041                 configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
1042             )
1043 
1044             assertThat(latest).isEqualTo(false)
1045         }
1046 
1047     @Test
allowNetworkSliceIndicator_exposesCarrierConfigValuenull1048     fun allowNetworkSliceIndicator_exposesCarrierConfigValue() =
1049         testScope.runTest {
1050             val latest by collectLastValue(underTest.allowNetworkSliceIndicator)
1051 
1052             systemUiCarrierConfig.processNewCarrierConfig(
1053                 configWithOverride(KEY_SHOW_5G_SLICE_ICON_BOOL, true)
1054             )
1055 
1056             assertThat(latest).isTrue()
1057 
1058             systemUiCarrierConfig.processNewCarrierConfig(
1059                 configWithOverride(KEY_SHOW_5G_SLICE_ICON_BOOL, false)
1060             )
1061 
1062             assertThat(latest).isFalse()
1063         }
1064 
1065     @Test
isAllowedDuringAirplaneMode_alwaysFalsenull1066     fun isAllowedDuringAirplaneMode_alwaysFalse() =
1067         testScope.runTest {
1068             val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode)
1069 
1070             assertThat(latest).isFalse()
1071         }
1072 
1073     @Test
hasPrioritizedCaps_defaultFalsenull1074     fun hasPrioritizedCaps_defaultFalse() {
1075         assertThat(underTest.hasPrioritizedNetworkCapabilities.value).isFalse()
1076     }
1077 
1078     @Test
hasPrioritizedCaps_trueWhenAvailablenull1079     fun hasPrioritizedCaps_trueWhenAvailable() =
1080         testScope.runTest {
1081             val latest by collectLastValue(underTest.hasPrioritizedNetworkCapabilities)
1082 
1083             val callback: NetworkCallback =
1084                 withArgCaptor<NetworkCallback> {
1085                     verify(connectivityManager).registerNetworkCallback(any(), capture())
1086                 }
1087 
1088             callback.onAvailable(mock())
1089 
1090             assertThat(latest).isTrue()
1091         }
1092 
1093     @Test
hasPrioritizedCaps_becomesFalseWhenNetworkLostnull1094     fun hasPrioritizedCaps_becomesFalseWhenNetworkLost() =
1095         testScope.runTest {
1096             val latest by collectLastValue(underTest.hasPrioritizedNetworkCapabilities)
1097 
1098             val callback: NetworkCallback =
1099                 withArgCaptor<NetworkCallback> {
1100                     verify(connectivityManager).registerNetworkCallback(any(), capture())
1101                 }
1102 
1103             callback.onAvailable(mock())
1104 
1105             assertThat(latest).isTrue()
1106 
1107             callback.onLost(mock())
1108 
1109             assertThat(latest).isFalse()
1110         }
1111 
getTelephonyCallbackForTypenull1112     private inline fun <reified T> getTelephonyCallbackForType(): T {
1113         return MobileTelephonyHelpers.getTelephonyCallbackForType(telephonyManager)
1114     }
1115 
carrierIdIntentnull1116     private fun carrierIdIntent(
1117         subId: Int = SUB_1_ID,
1118         carrierId: Int,
1119     ): Intent =
1120         Intent(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED).apply {
1121             putExtra(EXTRA_SUBSCRIPTION_ID, subId)
1122             putExtra(EXTRA_CARRIER_ID, carrierId)
1123         }
1124 
spnIntentnull1125     private fun spnIntent(
1126         subId: Int = SUB_1_ID,
1127         showSpn: Boolean = true,
1128         spn: String = SPN,
1129         showPlmn: Boolean = true,
1130         plmn: String = PLMN,
1131     ): Intent =
1132         Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
1133             putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
1134             putExtra(EXTRA_SHOW_SPN, showSpn)
1135             putExtra(EXTRA_SPN, spn)
1136             putExtra(EXTRA_SHOW_PLMN, showPlmn)
1137             putExtra(EXTRA_PLMN, plmn)
1138         }
1139 
1140     companion object {
1141         private const val SUB_1_ID = 1
1142 
1143         private const val DEFAULT_NAME = "Fake Mobile Network"
1144         private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
1145         private const val SEP = "-"
1146 
1147         private const val SPN = "testSpn"
1148         private const val PLMN = "testPlmn"
1149     }
1150 }
1151