1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.pipeline.satellite.data.prod
18 
19 import android.os.OutcomeReceiver
20 import android.os.Process
21 import android.telephony.TelephonyCallback
22 import android.telephony.TelephonyManager
23 import android.telephony.satellite.NtnSignalStrength
24 import android.telephony.satellite.NtnSignalStrengthCallback
25 import android.telephony.satellite.SatelliteCommunicationAllowedStateCallback
26 import android.telephony.satellite.SatelliteManager
27 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED
28 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
29 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING
30 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_IDLE
31 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_LISTENING
32 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED
33 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
34 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
35 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
36 import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR
37 import android.telephony.satellite.SatelliteManager.SatelliteException
38 import android.telephony.satellite.SatelliteModemStateCallback
39 import android.telephony.satellite.SatelliteProvisionStateCallback
40 import android.telephony.satellite.SatelliteSupportedStateCallback
41 import androidx.test.ext.junit.runners.AndroidJUnit4
42 import androidx.test.filters.SmallTest
43 import com.android.systemui.SysuiTestCase
44 import com.android.systemui.coroutines.collectLastValue
45 import com.android.systemui.log.core.FakeLogBuffer
46 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers
47 import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.MIN_UPTIME
48 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
49 import com.android.systemui.util.mockito.any
50 import com.android.systemui.util.mockito.whenever
51 import com.android.systemui.util.mockito.withArgCaptor
52 import com.android.systemui.util.time.FakeSystemClock
53 import com.google.common.truth.Truth.assertThat
54 import java.util.Optional
55 import kotlin.test.Test
56 import kotlinx.coroutines.ExperimentalCoroutinesApi
57 import kotlinx.coroutines.test.StandardTestDispatcher
58 import kotlinx.coroutines.test.TestScope
59 import kotlinx.coroutines.test.runCurrent
60 import kotlinx.coroutines.test.runTest
61 import org.junit.Before
62 import org.junit.runner.RunWith
63 import org.mockito.Mock
64 import org.mockito.Mockito
65 import org.mockito.Mockito.atLeastOnce
66 import org.mockito.Mockito.doAnswer
67 import org.mockito.Mockito.never
68 import org.mockito.Mockito.times
69 import org.mockito.Mockito.verify
70 import org.mockito.MockitoAnnotations
71 import org.mockito.kotlin.doThrow
72 
73 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
74 @OptIn(ExperimentalCoroutinesApi::class)
75 @SmallTest
76 @RunWith(AndroidJUnit4::class)
77 class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
78     private lateinit var underTest: DeviceBasedSatelliteRepositoryImpl
79 
80     @Mock private lateinit var satelliteManager: SatelliteManager
81     @Mock private lateinit var telephonyManager: TelephonyManager
82 
83     private val systemClock = FakeSystemClock()
84     private val dispatcher = StandardTestDispatcher()
85     private val testScope = TestScope(dispatcher)
86 
87     @Before
setUpnull88     fun setUp() {
89         MockitoAnnotations.initMocks(this)
90     }
91 
92     @Test
nullSatelliteManager_usesDefaultValuesnull93     fun nullSatelliteManager_usesDefaultValues() =
94         testScope.runTest {
95             setupDefaultRepo()
96             underTest =
97                 DeviceBasedSatelliteRepositoryImpl(
98                     Optional.empty(),
99                     telephonyManager,
100                     dispatcher,
101                     testScope.backgroundScope,
102                     logBuffer = FakeLogBuffer.Factory.create(),
103                     verboseLogBuffer = FakeLogBuffer.Factory.create(),
104                     systemClock,
105                     context.resources,
106                 )
107 
108             val connectionState by collectLastValue(underTest.connectionState)
109             val strength by collectLastValue(underTest.signalStrength)
110             val allowed by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
111 
112             assertThat(connectionState).isEqualTo(SatelliteConnectionState.Off)
113             assertThat(strength).isEqualTo(0)
114             assertThat(allowed).isFalse()
115         }
116 
117     @Test
connectionState_mapsFromSatelliteModemStatenull118     fun connectionState_mapsFromSatelliteModemState() =
119         testScope.runTest {
120             setupDefaultRepo()
121             val latest by collectLastValue(underTest.connectionState)
122             runCurrent()
123             val callback =
124                 withArgCaptor<SatelliteModemStateCallback> {
125                     verify(satelliteManager).registerForModemStateChanged(any(), capture())
126                 }
127 
128             // Mapping from modem state to SatelliteConnectionState is rote, just run all of the
129             // possibilities here
130 
131             // Off states
132             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_OFF)
133             assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
134             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_UNAVAILABLE)
135             assertThat(latest).isEqualTo(SatelliteConnectionState.Off)
136 
137             // On states
138             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_IDLE)
139             assertThat(latest).isEqualTo(SatelliteConnectionState.On)
140             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_LISTENING)
141             assertThat(latest).isEqualTo(SatelliteConnectionState.On)
142             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_NOT_CONNECTED)
143             assertThat(latest).isEqualTo(SatelliteConnectionState.On)
144 
145             // Connected states
146             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_CONNECTED)
147             assertThat(latest).isEqualTo(SatelliteConnectionState.Connected)
148             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING)
149             assertThat(latest).isEqualTo(SatelliteConnectionState.Connected)
150             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_DATAGRAM_RETRYING)
151             assertThat(latest).isEqualTo(SatelliteConnectionState.Connected)
152 
153             // Unknown states
154             callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_UNKNOWN)
155             assertThat(latest).isEqualTo(SatelliteConnectionState.Unknown)
156             // Garbage value (for completeness' sake)
157             callback.onSatelliteModemStateChanged(123456)
158             assertThat(latest).isEqualTo(SatelliteConnectionState.Unknown)
159         }
160 
161     @Test
signalStrength_readsSatelliteManagerStatenull162     fun signalStrength_readsSatelliteManagerState() =
163         testScope.runTest {
164             setupDefaultRepo()
165             val latest by collectLastValue(underTest.signalStrength)
166             runCurrent()
167             val callback =
168                 withArgCaptor<NtnSignalStrengthCallback> {
169                     verify(satelliteManager).registerForNtnSignalStrengthChanged(any(), capture())
170                 }
171 
172             assertThat(latest).isEqualTo(0)
173 
174             callback.onNtnSignalStrengthChanged(NtnSignalStrength(1))
175             assertThat(latest).isEqualTo(1)
176 
177             callback.onNtnSignalStrengthChanged(NtnSignalStrength(2))
178             assertThat(latest).isEqualTo(2)
179 
180             callback.onNtnSignalStrengthChanged(NtnSignalStrength(3))
181             assertThat(latest).isEqualTo(3)
182 
183             callback.onNtnSignalStrengthChanged(NtnSignalStrength(4))
184             assertThat(latest).isEqualTo(4)
185         }
186 
187     @Test
isSatelliteAllowed_listensToSatelliteManagerCallbacknull188     fun isSatelliteAllowed_listensToSatelliteManagerCallback() =
189         testScope.runTest {
190             setupDefaultRepo()
191 
192             val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
193             runCurrent()
194 
195             val callback =
196                 withArgCaptor<SatelliteCommunicationAllowedStateCallback> {
197                     verify(satelliteManager)
198                         .registerForCommunicationAllowedStateChanged(any(), capture())
199                 }
200 
201             // WHEN satellite manager says it's not available
202             callback.onSatelliteCommunicationAllowedStateChanged(false)
203 
204             // THEN it's not!
205             assertThat(latest).isFalse()
206 
207             // WHEN satellite manager says it's changed to available
208             callback.onSatelliteCommunicationAllowedStateChanged(true)
209 
210             // THEN it is!
211             assertThat(latest).isTrue()
212         }
213 
214     @Test
isSatelliteAllowed_falseWhenErrorOccursnull215     fun isSatelliteAllowed_falseWhenErrorOccurs() =
216         testScope.runTest {
217             setupDefaultRepo()
218 
219             // GIVEN SatelliteManager gon' throw exceptions when we ask to register the callback
220             doThrow(RuntimeException("Test exception"))
221                 .`when`(satelliteManager)
222                 .registerForCommunicationAllowedStateChanged(any(), any())
223 
224             // WHEN the latest value is requested (and thus causes an exception to be thrown)
225             val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
226 
227             // THEN the value is just false, and we didn't crash!
228             assertThat(latest).isFalse()
229         }
230 
231     @Test
isSatelliteAllowed_reRegistersOnTelephonyProcessCrashnull232     fun isSatelliteAllowed_reRegistersOnTelephonyProcessCrash() =
233         testScope.runTest {
234             setupDefaultRepo()
235             val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
236             runCurrent()
237 
238             val callback =
239                 withArgCaptor<SatelliteCommunicationAllowedStateCallback> {
240                     verify(satelliteManager)
241                         .registerForCommunicationAllowedStateChanged(any(), capture())
242                 }
243 
244             val telephonyCallback =
245                 MobileTelephonyHelpers.getTelephonyCallbackForType<
246                     TelephonyCallback.RadioPowerStateListener
247                 >(
248                     telephonyManager
249                 )
250 
251             // GIVEN satellite is currently provisioned
252             callback.onSatelliteCommunicationAllowedStateChanged(true)
253 
254             assertThat(latest).isTrue()
255 
256             // WHEN a crash event happens (detected by radio state change)
257             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
258             runCurrent()
259             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
260             runCurrent()
261 
262             // THEN listener is re-registered
263             verify(satelliteManager, times(2))
264                 .registerForCommunicationAllowedStateChanged(any(), any())
265         }
266 
267     @Test
satelliteProvisioned_notSupported_defaultFalsenull268     fun satelliteProvisioned_notSupported_defaultFalse() =
269         testScope.runTest {
270             // GIVEN satellite is not supported
271             setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
272 
273             assertThat(underTest.isSatelliteProvisioned.value).isFalse()
274         }
275 
276     @Test
satelliteProvisioned_supported_defaultFalsenull277     fun satelliteProvisioned_supported_defaultFalse() =
278         testScope.runTest {
279             // GIVEN satellite is supported
280             setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true)
281 
282             // THEN default provisioned state is false
283             assertThat(underTest.isSatelliteProvisioned.value).isFalse()
284         }
285 
286     @Test
satelliteProvisioned_returnsException_defaultsToFalsenull287     fun satelliteProvisioned_returnsException_defaultsToFalse() =
288         testScope.runTest {
289             // GIVEN satellite is supported on device
290             doAnswer {
291                     val callback: OutcomeReceiver<Boolean, SatelliteException> =
292                         it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
293                     callback.onResult(true)
294                 }
295                 .whenever(satelliteManager)
296                 .requestIsSupported(any(), any())
297 
298             // GIVEN satellite returns an error when asked if provisioned
299             doAnswer {
300                     val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
301                     receiver.onError(SatelliteException(SATELLITE_RESULT_ERROR))
302                     null
303                 }
304                 .whenever(satelliteManager)
305                 .requestIsProvisioned(any(), any<OutcomeReceiver<Boolean, SatelliteException>>())
306 
307             // GIVEN we've been up long enough to start querying
308             systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME)
309 
310             underTest =
311                 DeviceBasedSatelliteRepositoryImpl(
312                     Optional.of(satelliteManager),
313                     telephonyManager,
314                     dispatcher,
315                     testScope.backgroundScope,
316                     logBuffer = FakeLogBuffer.Factory.create(),
317                     verboseLogBuffer = FakeLogBuffer.Factory.create(),
318                     systemClock,
319                     context.resources,
320                 )
321 
322             // WHEN we try to check for provisioned status
323             val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
324 
325             // THEN well, first we don't throw...
326             // AND THEN we assume that it's not provisioned
327             assertThat(provisioned).isFalse()
328         }
329 
330     @Test
satelliteProvisioned_throwsWhenQuerying_defaultsToFalsenull331     fun satelliteProvisioned_throwsWhenQuerying_defaultsToFalse() =
332         testScope.runTest {
333             // GIVEN satellite is supported on device
334             doAnswer {
335                     val callback: OutcomeReceiver<Boolean, SatelliteException> =
336                         it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
337                     callback.onResult(true)
338                 }
339                 .whenever(satelliteManager)
340                 .requestIsSupported(any(), any())
341 
342             // GIVEN satellite throws when asked if provisioned
343             whenever(satelliteManager.requestIsProvisioned(any(), any()))
344                 .thenThrow(SecurityException())
345 
346             // GIVEN we've been up long enough to start querying
347             systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME)
348 
349             underTest =
350                 DeviceBasedSatelliteRepositoryImpl(
351                     Optional.of(satelliteManager),
352                     telephonyManager,
353                     dispatcher,
354                     testScope.backgroundScope,
355                     logBuffer = FakeLogBuffer.Factory.create(),
356                     verboseLogBuffer = FakeLogBuffer.Factory.create(),
357                     systemClock,
358                     context.resources,
359                 )
360 
361             // WHEN we try to check for provisioned status
362             val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
363 
364             // THEN well, first we don't throw...
365             // AND THEN we assume that it's not provisioned
366             assertThat(provisioned).isFalse()
367         }
368 
369     @Test
satelliteProvisioned_supported_provisioned_queriesInitialStateBeforeCallbacksnull370     fun satelliteProvisioned_supported_provisioned_queriesInitialStateBeforeCallbacks() =
371         testScope.runTest {
372             // GIVEN satellite is supported, and provisioned
373             setUpRepo(
374                 uptime = MIN_UPTIME,
375                 satMan = satelliteManager,
376                 satelliteSupported = true,
377                 initialSatelliteIsProvisioned = true,
378             )
379 
380             val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
381 
382             runCurrent()
383 
384             // THEN the current state is requested
385             verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any())
386 
387             // AND the state is correct
388             assertThat(provisioned).isTrue()
389         }
390 
391     @Test
satelliteProvisioned_supported_notProvisioned_queriesInitialStateBeforeCallbacksnull392     fun satelliteProvisioned_supported_notProvisioned_queriesInitialStateBeforeCallbacks() =
393         testScope.runTest {
394             // GIVEN satellite is supported, and provisioned
395             setUpRepo(
396                 uptime = MIN_UPTIME,
397                 satMan = satelliteManager,
398                 satelliteSupported = true,
399                 initialSatelliteIsProvisioned = false,
400             )
401 
402             val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
403 
404             runCurrent()
405 
406             // THEN the current state is requested
407             verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any())
408 
409             // AND the state is correct
410             assertThat(provisioned).isFalse()
411         }
412 
413     @Test
satelliteProvisioned_supported_notInitiallyProvisioned_tracksCallbacknull414     fun satelliteProvisioned_supported_notInitiallyProvisioned_tracksCallback() =
415         testScope.runTest {
416             // GIVEN satellite is not supported
417             setUpRepo(
418                 uptime = MIN_UPTIME,
419                 satMan = satelliteManager,
420                 satelliteSupported = true,
421                 initialSatelliteIsProvisioned = false,
422             )
423 
424             val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
425             runCurrent()
426 
427             val callback =
428                 withArgCaptor<SatelliteProvisionStateCallback> {
429                     verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
430                 }
431 
432             // WHEN provisioning state changes
433             callback.onSatelliteProvisionStateChanged(true)
434 
435             // THEN the value is reflected in the repo
436             assertThat(provisioned).isTrue()
437         }
438 
439     @Test
satelliteProvisioned_supported_tracksCallback_reRegistersOnCrashnull440     fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() =
441         testScope.runTest {
442             // GIVEN satellite is supported
443             setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true)
444 
445             val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
446 
447             runCurrent()
448 
449             val callback =
450                 withArgCaptor<SatelliteProvisionStateCallback> {
451                     verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
452                 }
453             val telephonyCallback =
454                 MobileTelephonyHelpers.getTelephonyCallbackForType<
455                     TelephonyCallback.RadioPowerStateListener
456                 >(
457                     telephonyManager
458                 )
459 
460             // GIVEN satellite is currently provisioned
461             callback.onSatelliteProvisionStateChanged(true)
462 
463             assertThat(provisioned).isTrue()
464 
465             // WHEN a crash event happens (detected by radio state change)
466             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
467             runCurrent()
468             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
469             runCurrent()
470 
471             // THEN listeners are re-registered
472             verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any())
473             // AND the state is queried again
474             verify(satelliteManager, times(2)).requestIsProvisioned(any(), any())
475         }
476 
477     @Test
satelliteNotSupported_listenersAreNotRegisterednull478     fun satelliteNotSupported_listenersAreNotRegistered() =
479         testScope.runTest {
480             // GIVEN satellite is not supported
481             setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
482 
483             // WHEN data is requested from the repo
484             val connectionState by collectLastValue(underTest.connectionState)
485             val signalStrength by collectLastValue(underTest.signalStrength)
486 
487             // THEN the manager is not asked for the information, and default values are returned
488             verify(satelliteManager, never()).registerForModemStateChanged(any(), any())
489             verify(satelliteManager, never()).registerForNtnSignalStrengthChanged(any(), any())
490         }
491 
492     @Test
satelliteSupported_registersCallbackForStateChangesnull493     fun satelliteSupported_registersCallbackForStateChanges() =
494         testScope.runTest {
495             // GIVEN a supported satellite manager.
496             setupDefaultRepo()
497             runCurrent()
498 
499             // THEN the repo registers for state changes of satellite support
500             verify(satelliteManager, times(1)).registerForSupportedStateChanged(any(), any())
501         }
502 
503     @Test
satelliteNotSupported_registersCallbackForStateChangesnull504     fun satelliteNotSupported_registersCallbackForStateChanges() =
505         testScope.runTest {
506             // GIVEN satellite is not supported
507             setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
508 
509             runCurrent()
510             // THEN the repo registers for state changes of satellite support
511             verify(satelliteManager, times(1)).registerForSupportedStateChanged(any(), any())
512         }
513 
514     @Test
satelliteSupportedStateChangedCallbackThrows_doesNotCrashnull515     fun satelliteSupportedStateChangedCallbackThrows_doesNotCrash() =
516         testScope.runTest {
517             // GIVEN, satellite manager throws when registering for supported state changes
518             whenever(satelliteManager.registerForSupportedStateChanged(any(), any()))
519                 .thenThrow(IllegalStateException())
520 
521             // GIVEN a supported satellite manager.
522             setupDefaultRepo()
523             runCurrent()
524 
525             // THEN a listener for satellite supported changed can attempt to register,
526             // with no crash
527             verify(satelliteManager).registerForSupportedStateChanged(any(), any())
528         }
529 
530     @Test
satelliteSupported_supportIsLost_unregistersListenersnull531     fun satelliteSupported_supportIsLost_unregistersListeners() =
532         testScope.runTest {
533             // GIVEN a supported satellite manager.
534             setupDefaultRepo()
535             runCurrent()
536 
537             val callback =
538                 withArgCaptor<SatelliteSupportedStateCallback> {
539                     verify(satelliteManager).registerForSupportedStateChanged(any(), capture())
540                 }
541 
542             // WHEN data is requested from the repo
543             val connectionState by collectLastValue(underTest.connectionState)
544             val signalStrength by collectLastValue(underTest.signalStrength)
545 
546             // THEN the listeners are registered
547             verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any())
548             verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any())
549 
550             // WHEN satellite support turns off
551             callback.onSatelliteSupportedStateChanged(false)
552             runCurrent()
553 
554             // THEN listeners are unregistered
555             verify(satelliteManager, times(1)).unregisterForModemStateChanged(any())
556             verify(satelliteManager, times(1)).unregisterForNtnSignalStrengthChanged(any())
557         }
558 
559     @Test
satelliteNotSupported_supportShowsUp_registersListenersnull560     fun satelliteNotSupported_supportShowsUp_registersListeners() =
561         testScope.runTest {
562             // GIVEN satellite is not supported
563             setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = false)
564             runCurrent()
565 
566             val callback =
567                 withArgCaptor<SatelliteSupportedStateCallback> {
568                     verify(satelliteManager).registerForSupportedStateChanged(any(), capture())
569                 }
570 
571             // WHEN data is requested from the repo
572             val connectionState by collectLastValue(underTest.connectionState)
573             val signalStrength by collectLastValue(underTest.signalStrength)
574 
575             // THEN the listeners are not yet registered
576             verify(satelliteManager, times(0)).registerForModemStateChanged(any(), any())
577             verify(satelliteManager, times(0)).registerForNtnSignalStrengthChanged(any(), any())
578 
579             // WHEN satellite support turns on
580             callback.onSatelliteSupportedStateChanged(true)
581             runCurrent()
582 
583             // THEN listeners are registered
584             verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any())
585             verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any())
586         }
587 
588     @Test
repoDoesNotCheckForSupportUntilMinUptimenull589     fun repoDoesNotCheckForSupportUntilMinUptime() =
590         testScope.runTest {
591             // GIVEN we init 100ms after sysui starts up
592             setUpRepo(uptime = 100, satMan = satelliteManager, satelliteSupported = true)
593 
594             // WHEN data is requested
595             val connectionState by collectLastValue(underTest.connectionState)
596             val signalStrength by collectLastValue(underTest.signalStrength)
597 
598             // THEN we have not yet talked to satellite manager, since we are well before MIN_UPTIME
599             Mockito.verifyNoMoreInteractions(satelliteManager)
600 
601             // WHEN enough time has passed
602             systemClock.advanceTime(MIN_UPTIME)
603             runCurrent()
604 
605             // THEN we finally register with the satellite manager
606             verify(satelliteManager).registerForModemStateChanged(any(), any())
607         }
608 
609     @Test
telephonyCrash_repoReregistersConnectionStateListenernull610     fun telephonyCrash_repoReregistersConnectionStateListener() =
611         testScope.runTest {
612             setupDefaultRepo()
613 
614             // GIVEN connection state is requested
615             val connectionState by collectLastValue(underTest.connectionState)
616 
617             runCurrent()
618 
619             val telephonyCallback =
620                 MobileTelephonyHelpers.getTelephonyCallbackForType<
621                     TelephonyCallback.RadioPowerStateListener
622                 >(
623                     telephonyManager
624                 )
625 
626             // THEN listener is registered once
627             verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any())
628 
629             // WHEN a crash event happens (detected by radio state change)
630             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
631             runCurrent()
632             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
633             runCurrent()
634 
635             // THEN listeners are unregistered and re-registered
636             verify(satelliteManager, times(1)).unregisterForModemStateChanged(any())
637             verify(satelliteManager, times(2)).registerForModemStateChanged(any(), any())
638         }
639 
640     @Test
telephonyCrash_repoReregistersSignalStrengthListenernull641     fun telephonyCrash_repoReregistersSignalStrengthListener() =
642         testScope.runTest {
643             setupDefaultRepo()
644 
645             // GIVEN signal strength is requested
646             val signalStrength by collectLastValue(underTest.signalStrength)
647 
648             runCurrent()
649 
650             val telephonyCallback =
651                 MobileTelephonyHelpers.getTelephonyCallbackForType<
652                     TelephonyCallback.RadioPowerStateListener
653                 >(
654                     telephonyManager
655                 )
656 
657             // THEN listeners are registered the first time
658             verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any())
659 
660             // WHEN a crash event happens (detected by radio state change)
661             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
662             runCurrent()
663             telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
664             runCurrent()
665 
666             // THEN listeners are unregistered and re-registered
667             verify(satelliteManager, times(1)).unregisterForNtnSignalStrengthChanged(any())
668             verify(satelliteManager, times(2)).registerForNtnSignalStrengthChanged(any(), any())
669         }
670 
setUpReponull671     private fun setUpRepo(
672         uptime: Long = MIN_UPTIME,
673         satMan: SatelliteManager? = satelliteManager,
674         satelliteSupported: Boolean = true,
675         initialSatelliteIsProvisioned: Boolean = true,
676     ) {
677         doAnswer {
678                 val callback: OutcomeReceiver<Boolean, SatelliteException> =
679                     it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
680                 callback.onResult(satelliteSupported)
681             }
682             .whenever(satelliteManager)
683             .requestIsSupported(any(), any())
684 
685         doAnswer {
686                 val callback: OutcomeReceiver<Boolean, SatelliteException> =
687                     it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
688                 callback.onResult(initialSatelliteIsProvisioned)
689             }
690             .whenever(satelliteManager)
691             .requestIsProvisioned(any(), any())
692 
693         systemClock.setUptimeMillis(Process.getStartUptimeMillis() + uptime)
694 
695         underTest =
696             DeviceBasedSatelliteRepositoryImpl(
697                 if (satMan != null) Optional.of(satMan) else Optional.empty(),
698                 telephonyManager,
699                 dispatcher,
700                 testScope.backgroundScope,
701                 logBuffer = FakeLogBuffer.Factory.create(),
702                 verboseLogBuffer = FakeLogBuffer.Factory.create(),
703                 systemClock,
704                 context.resources,
705             )
706     }
707 
708     // Set system time to MIN_UPTIME and create a repo with satellite supported
setupDefaultReponull709     private fun setupDefaultRepo() {
710         setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true)
711     }
712 }
713