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.SatelliteManager 26 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED 27 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING 28 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING 29 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_IDLE 30 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_LISTENING 31 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED 32 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF 33 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE 34 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN 35 import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR 36 import android.telephony.satellite.SatelliteManager.SatelliteException 37 import android.telephony.satellite.SatelliteModemStateCallback 38 import android.telephony.satellite.SatelliteProvisionStateCallback 39 import android.telephony.satellite.SatelliteSupportedStateCallback 40 import androidx.test.filters.SmallTest 41 import com.android.systemui.SysuiTestCase 42 import com.android.systemui.coroutines.collectLastValue 43 import com.android.systemui.log.core.FakeLogBuffer 44 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers 45 import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.MIN_UPTIME 46 import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.POLLING_INTERVAL_MS 47 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState 48 import com.android.systemui.util.mockito.any 49 import com.android.systemui.util.mockito.whenever 50 import com.android.systemui.util.mockito.withArgCaptor 51 import com.android.systemui.util.time.FakeSystemClock 52 import com.google.common.truth.Truth.assertThat 53 import java.util.Optional 54 import kotlin.test.Test 55 import kotlinx.coroutines.ExperimentalCoroutinesApi 56 import kotlinx.coroutines.flow.launchIn 57 import kotlinx.coroutines.flow.onEach 58 import kotlinx.coroutines.test.StandardTestDispatcher 59 import kotlinx.coroutines.test.TestScope 60 import kotlinx.coroutines.test.advanceTimeBy 61 import kotlinx.coroutines.test.runCurrent 62 import kotlinx.coroutines.test.runTest 63 import org.junit.Before 64 import org.mockito.Mock 65 import org.mockito.Mockito 66 import org.mockito.Mockito.atLeastOnce 67 import org.mockito.Mockito.doAnswer 68 import org.mockito.Mockito.never 69 import org.mockito.Mockito.times 70 import org.mockito.Mockito.verify 71 import org.mockito.MockitoAnnotations 72 73 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") 74 @OptIn(ExperimentalCoroutinesApi::class) 75 @SmallTest 76 class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { 77 private lateinit var underTest: DeviceBasedSatelliteRepositoryImpl 78 79 @Mock private lateinit var satelliteManager: SatelliteManager 80 @Mock private lateinit var telephonyManager: TelephonyManager 81 82 private val systemClock = FakeSystemClock() 83 private val dispatcher = StandardTestDispatcher() 84 private val testScope = TestScope(dispatcher) 85 86 @Before setUpnull87 fun setUp() { 88 MockitoAnnotations.initMocks(this) 89 } 90 91 @Test nullSatelliteManager_usesDefaultValuesnull92 fun nullSatelliteManager_usesDefaultValues() = 93 testScope.runTest { 94 setupDefaultRepo() 95 underTest = 96 DeviceBasedSatelliteRepositoryImpl( 97 Optional.empty(), 98 telephonyManager, 99 dispatcher, 100 testScope.backgroundScope, 101 logBuffer = FakeLogBuffer.Factory.create(), 102 verboseLogBuffer = FakeLogBuffer.Factory.create(), 103 systemClock, 104 ) 105 106 val connectionState by collectLastValue(underTest.connectionState) 107 val strength by collectLastValue(underTest.signalStrength) 108 val allowed by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation) 109 110 assertThat(connectionState).isEqualTo(SatelliteConnectionState.Off) 111 assertThat(strength).isEqualTo(0) 112 assertThat(allowed).isFalse() 113 } 114 115 @Test connectionState_mapsFromSatelliteModemStatenull116 fun connectionState_mapsFromSatelliteModemState() = 117 testScope.runTest { 118 setupDefaultRepo() 119 val latest by collectLastValue(underTest.connectionState) 120 runCurrent() 121 val callback = 122 withArgCaptor<SatelliteModemStateCallback> { 123 verify(satelliteManager).registerForModemStateChanged(any(), capture()) 124 } 125 126 // Mapping from modem state to SatelliteConnectionState is rote, just run all of the 127 // possibilities here 128 129 // Off states 130 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_OFF) 131 assertThat(latest).isEqualTo(SatelliteConnectionState.Off) 132 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_UNAVAILABLE) 133 assertThat(latest).isEqualTo(SatelliteConnectionState.Off) 134 135 // On states 136 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_IDLE) 137 assertThat(latest).isEqualTo(SatelliteConnectionState.On) 138 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_LISTENING) 139 assertThat(latest).isEqualTo(SatelliteConnectionState.On) 140 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_NOT_CONNECTED) 141 assertThat(latest).isEqualTo(SatelliteConnectionState.On) 142 143 // Connected states 144 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_CONNECTED) 145 assertThat(latest).isEqualTo(SatelliteConnectionState.Connected) 146 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING) 147 assertThat(latest).isEqualTo(SatelliteConnectionState.Connected) 148 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_DATAGRAM_RETRYING) 149 assertThat(latest).isEqualTo(SatelliteConnectionState.Connected) 150 151 // Unknown states 152 callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_UNKNOWN) 153 assertThat(latest).isEqualTo(SatelliteConnectionState.Unknown) 154 // Garbage value (for completeness' sake) 155 callback.onSatelliteModemStateChanged(123456) 156 assertThat(latest).isEqualTo(SatelliteConnectionState.Unknown) 157 } 158 159 @Test signalStrength_readsSatelliteManagerStatenull160 fun signalStrength_readsSatelliteManagerState() = 161 testScope.runTest { 162 setupDefaultRepo() 163 val latest by collectLastValue(underTest.signalStrength) 164 runCurrent() 165 val callback = 166 withArgCaptor<NtnSignalStrengthCallback> { 167 verify(satelliteManager).registerForNtnSignalStrengthChanged(any(), capture()) 168 } 169 170 assertThat(latest).isEqualTo(0) 171 172 callback.onNtnSignalStrengthChanged(NtnSignalStrength(1)) 173 assertThat(latest).isEqualTo(1) 174 175 callback.onNtnSignalStrengthChanged(NtnSignalStrength(2)) 176 assertThat(latest).isEqualTo(2) 177 178 callback.onNtnSignalStrengthChanged(NtnSignalStrength(3)) 179 assertThat(latest).isEqualTo(3) 180 181 callback.onNtnSignalStrengthChanged(NtnSignalStrength(4)) 182 assertThat(latest).isEqualTo(4) 183 } 184 185 @Test isSatelliteAllowed_readsSatelliteManagerState_enablednull186 fun isSatelliteAllowed_readsSatelliteManagerState_enabled() = 187 testScope.runTest { 188 setupDefaultRepo() 189 // GIVEN satellite is allowed in this location 190 val allowed = true 191 192 doAnswer { 193 val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException> 194 receiver.onResult(allowed) 195 null 196 } 197 .`when`(satelliteManager) 198 .requestIsCommunicationAllowedForCurrentLocation( 199 any(), 200 any<OutcomeReceiver<Boolean, SatelliteException>>() 201 ) 202 203 val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation) 204 205 assertThat(latest).isTrue() 206 } 207 208 @Test isSatelliteAllowed_readsSatelliteManagerState_disablednull209 fun isSatelliteAllowed_readsSatelliteManagerState_disabled() = 210 testScope.runTest { 211 setupDefaultRepo() 212 // GIVEN satellite is not allowed in this location 213 val allowed = false 214 215 doAnswer { 216 val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException> 217 receiver.onResult(allowed) 218 null 219 } 220 .`when`(satelliteManager) 221 .requestIsCommunicationAllowedForCurrentLocation( 222 any(), 223 any<OutcomeReceiver<Boolean, SatelliteException>>() 224 ) 225 226 val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation) 227 228 assertThat(latest).isFalse() 229 } 230 231 @Test isSatelliteAllowed_pollsOnTimeoutnull232 fun isSatelliteAllowed_pollsOnTimeout() = 233 testScope.runTest { 234 setupDefaultRepo() 235 // GIVEN satellite is not allowed in this location 236 var allowed = false 237 238 doAnswer { 239 val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException> 240 receiver.onResult(allowed) 241 null 242 } 243 .`when`(satelliteManager) 244 .requestIsCommunicationAllowedForCurrentLocation( 245 any(), 246 any<OutcomeReceiver<Boolean, SatelliteException>>() 247 ) 248 249 val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation) 250 251 assertThat(latest).isFalse() 252 253 // WHEN satellite becomes enabled 254 allowed = true 255 256 // WHEN the timeout has not yet been reached 257 advanceTimeBy(POLLING_INTERVAL_MS / 2) 258 259 // THEN the value is still false 260 assertThat(latest).isFalse() 261 262 // WHEN time advances beyond the polling interval 263 advanceTimeBy(POLLING_INTERVAL_MS / 2 + 1) 264 265 // THEN then new value is emitted 266 assertThat(latest).isTrue() 267 } 268 269 @Test isSatelliteAllowed_pollingRestartsWhenCollectionRestartsnull270 fun isSatelliteAllowed_pollingRestartsWhenCollectionRestarts() = 271 testScope.runTest { 272 setupDefaultRepo() 273 // Use the old school launch/cancel so we can simulate subscribers arriving and leaving 274 275 var latest: Boolean? = false 276 var job = 277 underTest.isSatelliteAllowedForCurrentLocation.onEach { latest = it }.launchIn(this) 278 279 // GIVEN satellite is not allowed in this location 280 var allowed = false 281 282 doAnswer { 283 val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException> 284 receiver.onResult(allowed) 285 null 286 } 287 .`when`(satelliteManager) 288 .requestIsCommunicationAllowedForCurrentLocation( 289 any(), 290 any<OutcomeReceiver<Boolean, SatelliteException>>() 291 ) 292 293 assertThat(latest).isFalse() 294 295 // WHEN satellite becomes enabled 296 allowed = true 297 298 // WHEN the job is restarted 299 advanceTimeBy(POLLING_INTERVAL_MS / 2) 300 301 job.cancel() 302 job = 303 underTest.isSatelliteAllowedForCurrentLocation.onEach { latest = it }.launchIn(this) 304 305 // THEN the value is re-fetched 306 assertThat(latest).isTrue() 307 308 job.cancel() 309 } 310 311 @Test isSatelliteAllowed_falseWhenErrorOccursnull312 fun isSatelliteAllowed_falseWhenErrorOccurs() = 313 testScope.runTest { 314 setupDefaultRepo() 315 doAnswer { 316 val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException> 317 receiver.onError(SatelliteException(1 /* unused */)) 318 null 319 } 320 .`when`(satelliteManager) 321 .requestIsCommunicationAllowedForCurrentLocation( 322 any(), 323 any<OutcomeReceiver<Boolean, SatelliteException>>() 324 ) 325 326 val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation) 327 328 assertThat(latest).isFalse() 329 } 330 331 @Test satelliteProvisioned_notSupported_defaultFalsenull332 fun satelliteProvisioned_notSupported_defaultFalse() = 333 testScope.runTest { 334 // GIVEN satellite is not supported 335 setUpRepo( 336 uptime = MIN_UPTIME, 337 satMan = satelliteManager, 338 satelliteSupported = false, 339 ) 340 341 assertThat(underTest.isSatelliteProvisioned.value).isFalse() 342 } 343 344 @Test satelliteProvisioned_supported_defaultFalsenull345 fun satelliteProvisioned_supported_defaultFalse() = 346 testScope.runTest { 347 // GIVEN satellite is supported 348 setUpRepo( 349 uptime = MIN_UPTIME, 350 satMan = satelliteManager, 351 satelliteSupported = true, 352 ) 353 354 // THEN default provisioned state is false 355 assertThat(underTest.isSatelliteProvisioned.value).isFalse() 356 } 357 358 @Test satelliteProvisioned_returnsException_defaultsToFalsenull359 fun satelliteProvisioned_returnsException_defaultsToFalse() = 360 testScope.runTest { 361 // GIVEN satellite is supported on device 362 doAnswer { 363 val callback: OutcomeReceiver<Boolean, SatelliteException> = 364 it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException> 365 callback.onResult(true) 366 } 367 .whenever(satelliteManager) 368 .requestIsSupported(any(), any()) 369 370 // GIVEN satellite returns an error when asked if provisioned 371 doAnswer { 372 val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException> 373 receiver.onError(SatelliteException(SATELLITE_RESULT_ERROR)) 374 null 375 } 376 .whenever(satelliteManager) 377 .requestIsProvisioned( 378 any(), 379 any<OutcomeReceiver<Boolean, SatelliteException>>() 380 ) 381 382 // GIVEN we've been up long enough to start querying 383 systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME) 384 385 underTest = 386 DeviceBasedSatelliteRepositoryImpl( 387 Optional.of(satelliteManager), 388 telephonyManager, 389 dispatcher, 390 testScope.backgroundScope, 391 logBuffer = FakeLogBuffer.Factory.create(), 392 verboseLogBuffer = FakeLogBuffer.Factory.create(), 393 systemClock, 394 ) 395 396 // WHEN we try to check for provisioned status 397 val provisioned by collectLastValue(underTest.isSatelliteProvisioned) 398 399 // THEN well, first we don't throw... 400 // AND THEN we assume that it's not provisioned 401 assertThat(provisioned).isFalse() 402 } 403 404 @Test satelliteProvisioned_throwsWhenQuerying_defaultsToFalsenull405 fun satelliteProvisioned_throwsWhenQuerying_defaultsToFalse() = 406 testScope.runTest { 407 // GIVEN satellite is supported on device 408 doAnswer { 409 val callback: OutcomeReceiver<Boolean, SatelliteException> = 410 it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException> 411 callback.onResult(true) 412 } 413 .whenever(satelliteManager) 414 .requestIsSupported(any(), any()) 415 416 // GIVEN satellite throws when asked if provisioned 417 whenever(satelliteManager.requestIsProvisioned(any(), any())) 418 .thenThrow(SecurityException()) 419 420 // GIVEN we've been up long enough to start querying 421 systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME) 422 423 underTest = 424 DeviceBasedSatelliteRepositoryImpl( 425 Optional.of(satelliteManager), 426 telephonyManager, 427 dispatcher, 428 testScope.backgroundScope, 429 logBuffer = FakeLogBuffer.Factory.create(), 430 verboseLogBuffer = FakeLogBuffer.Factory.create(), 431 systemClock, 432 ) 433 434 // WHEN we try to check for provisioned status 435 val provisioned by collectLastValue(underTest.isSatelliteProvisioned) 436 437 // THEN well, first we don't throw... 438 // AND THEN we assume that it's not provisioned 439 assertThat(provisioned).isFalse() 440 } 441 442 @Test satelliteProvisioned_supported_provisioned_queriesInitialStateBeforeCallbacksnull443 fun satelliteProvisioned_supported_provisioned_queriesInitialStateBeforeCallbacks() = 444 testScope.runTest { 445 // GIVEN satellite is supported, and provisioned 446 setUpRepo( 447 uptime = MIN_UPTIME, 448 satMan = satelliteManager, 449 satelliteSupported = true, 450 initialSatelliteIsProvisioned = true, 451 ) 452 453 val provisioned by collectLastValue(underTest.isSatelliteProvisioned) 454 455 runCurrent() 456 457 // THEN the current state is requested 458 verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any()) 459 460 // AND the state is correct 461 assertThat(provisioned).isTrue() 462 } 463 464 @Test satelliteProvisioned_supported_notProvisioned_queriesInitialStateBeforeCallbacksnull465 fun satelliteProvisioned_supported_notProvisioned_queriesInitialStateBeforeCallbacks() = 466 testScope.runTest { 467 // GIVEN satellite is supported, and provisioned 468 setUpRepo( 469 uptime = MIN_UPTIME, 470 satMan = satelliteManager, 471 satelliteSupported = true, 472 initialSatelliteIsProvisioned = false, 473 ) 474 475 val provisioned by collectLastValue(underTest.isSatelliteProvisioned) 476 477 runCurrent() 478 479 // THEN the current state is requested 480 verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any()) 481 482 // AND the state is correct 483 assertThat(provisioned).isFalse() 484 } 485 486 @Test satelliteProvisioned_supported_notInitiallyProvisioned_tracksCallbacknull487 fun satelliteProvisioned_supported_notInitiallyProvisioned_tracksCallback() = 488 testScope.runTest { 489 // GIVEN satellite is not supported 490 setUpRepo( 491 uptime = MIN_UPTIME, 492 satMan = satelliteManager, 493 satelliteSupported = true, 494 initialSatelliteIsProvisioned = false, 495 ) 496 497 val provisioned by collectLastValue(underTest.isSatelliteProvisioned) 498 runCurrent() 499 500 val callback = 501 withArgCaptor<SatelliteProvisionStateCallback> { 502 verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) 503 } 504 505 // WHEN provisioning state changes 506 callback.onSatelliteProvisionStateChanged(true) 507 508 // THEN the value is reflected in the repo 509 assertThat(provisioned).isTrue() 510 } 511 512 @Test satelliteProvisioned_supported_tracksCallback_reRegistersOnCrashnull513 fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() = 514 testScope.runTest { 515 // GIVEN satellite is supported 516 setUpRepo( 517 uptime = MIN_UPTIME, 518 satMan = satelliteManager, 519 satelliteSupported = true, 520 ) 521 522 val provisioned by collectLastValue(underTest.isSatelliteProvisioned) 523 524 runCurrent() 525 526 val callback = 527 withArgCaptor<SatelliteProvisionStateCallback> { 528 verify(satelliteManager).registerForProvisionStateChanged(any(), capture()) 529 } 530 val telephonyCallback = 531 MobileTelephonyHelpers.getTelephonyCallbackForType< 532 TelephonyCallback.RadioPowerStateListener 533 >( 534 telephonyManager 535 ) 536 537 // GIVEN satellite is currently provisioned 538 callback.onSatelliteProvisionStateChanged(true) 539 540 assertThat(provisioned).isTrue() 541 542 // WHEN a crash event happens (detected by radio state change) 543 telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON) 544 runCurrent() 545 telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF) 546 runCurrent() 547 548 // THEN listeners are re-registered 549 verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any()) 550 // AND the state is queried again 551 verify(satelliteManager, times(2)).requestIsProvisioned(any(), any()) 552 } 553 554 @Test satelliteNotSupported_listenersAreNotRegisterednull555 fun satelliteNotSupported_listenersAreNotRegistered() = 556 testScope.runTest { 557 // GIVEN satellite is not supported 558 setUpRepo( 559 uptime = MIN_UPTIME, 560 satMan = satelliteManager, 561 satelliteSupported = false, 562 ) 563 564 // WHEN data is requested from the repo 565 val connectionState by collectLastValue(underTest.connectionState) 566 val signalStrength by collectLastValue(underTest.signalStrength) 567 568 // THEN the manager is not asked for the information, and default values are returned 569 verify(satelliteManager, never()).registerForModemStateChanged(any(), any()) 570 verify(satelliteManager, never()).registerForNtnSignalStrengthChanged(any(), any()) 571 } 572 573 @Test satelliteSupported_registersCallbackForStateChangesnull574 fun satelliteSupported_registersCallbackForStateChanges() = 575 testScope.runTest { 576 // GIVEN a supported satellite manager. 577 setupDefaultRepo() 578 runCurrent() 579 580 // THEN the repo registers for state changes of satellite support 581 verify(satelliteManager, times(1)).registerForSupportedStateChanged(any(), any()) 582 } 583 584 @Test satelliteNotSupported_registersCallbackForStateChangesnull585 fun satelliteNotSupported_registersCallbackForStateChanges() = 586 testScope.runTest { 587 // GIVEN satellite is not supported 588 setUpRepo( 589 uptime = MIN_UPTIME, 590 satMan = satelliteManager, 591 satelliteSupported = false, 592 ) 593 594 runCurrent() 595 // THEN the repo registers for state changes of satellite support 596 verify(satelliteManager, times(1)).registerForSupportedStateChanged(any(), any()) 597 } 598 599 @Test satelliteSupportedStateChangedCallbackThrows_doesNotCrashnull600 fun satelliteSupportedStateChangedCallbackThrows_doesNotCrash() = 601 testScope.runTest { 602 // GIVEN, satellite manager throws when registering for supported state changes 603 whenever(satelliteManager.registerForSupportedStateChanged(any(), any())) 604 .thenThrow(IllegalStateException()) 605 606 // GIVEN a supported satellite manager. 607 setupDefaultRepo() 608 runCurrent() 609 610 // THEN a listener for satellite supported changed can attempt to register, 611 // with no crash 612 verify(satelliteManager).registerForSupportedStateChanged(any(), any()) 613 } 614 615 @Test satelliteSupported_supportIsLost_unregistersListenersnull616 fun satelliteSupported_supportIsLost_unregistersListeners() = 617 testScope.runTest { 618 // GIVEN a supported satellite manager. 619 setupDefaultRepo() 620 runCurrent() 621 622 val callback = 623 withArgCaptor<SatelliteSupportedStateCallback> { 624 verify(satelliteManager).registerForSupportedStateChanged(any(), capture()) 625 } 626 627 // WHEN data is requested from the repo 628 val connectionState by collectLastValue(underTest.connectionState) 629 val signalStrength by collectLastValue(underTest.signalStrength) 630 631 // THEN the listeners are registered 632 verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any()) 633 verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any()) 634 635 // WHEN satellite support turns off 636 callback.onSatelliteSupportedStateChanged(false) 637 runCurrent() 638 639 // THEN listeners are unregistered 640 verify(satelliteManager, times(1)).unregisterForModemStateChanged(any()) 641 verify(satelliteManager, times(1)).unregisterForNtnSignalStrengthChanged(any()) 642 } 643 644 @Test satelliteNotSupported_supportShowsUp_registersListenersnull645 fun satelliteNotSupported_supportShowsUp_registersListeners() = 646 testScope.runTest { 647 // GIVEN satellite is not supported 648 setUpRepo( 649 uptime = MIN_UPTIME, 650 satMan = satelliteManager, 651 satelliteSupported = false, 652 ) 653 runCurrent() 654 655 val callback = 656 withArgCaptor<SatelliteSupportedStateCallback> { 657 verify(satelliteManager).registerForSupportedStateChanged(any(), capture()) 658 } 659 660 // WHEN data is requested from the repo 661 val connectionState by collectLastValue(underTest.connectionState) 662 val signalStrength by collectLastValue(underTest.signalStrength) 663 664 // THEN the listeners are not yet registered 665 verify(satelliteManager, times(0)).registerForModemStateChanged(any(), any()) 666 verify(satelliteManager, times(0)).registerForNtnSignalStrengthChanged(any(), any()) 667 668 // WHEN satellite support turns on 669 callback.onSatelliteSupportedStateChanged(true) 670 runCurrent() 671 672 // THEN listeners are registered 673 verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any()) 674 verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any()) 675 } 676 677 @Test repoDoesNotCheckForSupportUntilMinUptimenull678 fun repoDoesNotCheckForSupportUntilMinUptime() = 679 testScope.runTest { 680 // GIVEN we init 100ms after sysui starts up 681 setUpRepo( 682 uptime = 100, 683 satMan = satelliteManager, 684 satelliteSupported = true, 685 ) 686 687 // WHEN data is requested 688 val connectionState by collectLastValue(underTest.connectionState) 689 val signalStrength by collectLastValue(underTest.signalStrength) 690 691 // THEN we have not yet talked to satellite manager, since we are well before MIN_UPTIME 692 Mockito.verifyZeroInteractions(satelliteManager) 693 694 // WHEN enough time has passed 695 systemClock.advanceTime(MIN_UPTIME) 696 runCurrent() 697 698 // THEN we finally register with the satellite manager 699 verify(satelliteManager).registerForModemStateChanged(any(), any()) 700 } 701 702 @Test telephonyCrash_repoReregistersConnectionStateListenernull703 fun telephonyCrash_repoReregistersConnectionStateListener() = 704 testScope.runTest { 705 setupDefaultRepo() 706 707 // GIVEN connection state is requested 708 val connectionState by collectLastValue(underTest.connectionState) 709 710 runCurrent() 711 712 val telephonyCallback = 713 MobileTelephonyHelpers.getTelephonyCallbackForType< 714 TelephonyCallback.RadioPowerStateListener 715 >( 716 telephonyManager 717 ) 718 719 // THEN listener is registered once 720 verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any()) 721 722 // WHEN a crash event happens (detected by radio state change) 723 telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON) 724 runCurrent() 725 telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF) 726 runCurrent() 727 728 // THEN listeners are unregistered and re-registered 729 verify(satelliteManager, times(1)).unregisterForModemStateChanged(any()) 730 verify(satelliteManager, times(2)).registerForModemStateChanged(any(), any()) 731 } 732 733 @Test telephonyCrash_repoReregistersSignalStrengthListenernull734 fun telephonyCrash_repoReregistersSignalStrengthListener() = 735 testScope.runTest { 736 setupDefaultRepo() 737 738 // GIVEN signal strength is requested 739 val signalStrength by collectLastValue(underTest.signalStrength) 740 741 runCurrent() 742 743 val telephonyCallback = 744 MobileTelephonyHelpers.getTelephonyCallbackForType< 745 TelephonyCallback.RadioPowerStateListener 746 >( 747 telephonyManager 748 ) 749 750 // THEN listeners are registered the first time 751 verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any()) 752 753 // WHEN a crash event happens (detected by radio state change) 754 telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON) 755 runCurrent() 756 telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF) 757 runCurrent() 758 759 // THEN listeners are unregistered and re-registered 760 verify(satelliteManager, times(1)).unregisterForNtnSignalStrengthChanged(any()) 761 verify(satelliteManager, times(2)).registerForNtnSignalStrengthChanged(any(), any()) 762 } 763 setUpReponull764 private fun setUpRepo( 765 uptime: Long = MIN_UPTIME, 766 satMan: SatelliteManager? = satelliteManager, 767 satelliteSupported: Boolean = true, 768 initialSatelliteIsProvisioned: Boolean = true, 769 ) { 770 doAnswer { 771 val callback: OutcomeReceiver<Boolean, SatelliteException> = 772 it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException> 773 callback.onResult(satelliteSupported) 774 } 775 .whenever(satelliteManager) 776 .requestIsSupported(any(), any()) 777 778 doAnswer { 779 val callback: OutcomeReceiver<Boolean, SatelliteException> = 780 it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException> 781 callback.onResult(initialSatelliteIsProvisioned) 782 } 783 .whenever(satelliteManager) 784 .requestIsProvisioned(any(), any()) 785 786 systemClock.setUptimeMillis(Process.getStartUptimeMillis() + uptime) 787 788 underTest = 789 DeviceBasedSatelliteRepositoryImpl( 790 if (satMan != null) Optional.of(satMan) else Optional.empty(), 791 telephonyManager, 792 dispatcher, 793 testScope.backgroundScope, 794 logBuffer = FakeLogBuffer.Factory.create(), 795 verboseLogBuffer = FakeLogBuffer.Factory.create(), 796 systemClock, 797 ) 798 } 799 800 // Set system time to MIN_UPTIME and create a repo with satellite supported setupDefaultReponull801 private fun setupDefaultRepo() { 802 setUpRepo(uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true) 803 } 804 } 805