diff options
2 files changed, 197 insertions, 20 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt index ec3af87f2b9b..a7c5f78e5b69 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt @@ -345,32 +345,67 @@ constructor( orElse = flowOf(false), retrySignal = telephonyProcessCrashedEvent, ) - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + .stateIn(scope, SharingStarted.Eagerly, false) private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> = conflatedCallbackFlow { - val callback = SatelliteProvisionStateCallback { provisioned -> - logBuffer.i { - "onSatelliteProvisionStateChanged: " + - if (provisioned) "provisioned" else "not provisioned" + // TODO(b/347992038): SatelliteManager should be sending the current provisioned + // status when we register a callback. Until then, we have to manually query here. + + // First, check to see what the current status is, and send the result to the output + trySend(queryIsSatelliteProvisioned(sm)) + + val callback = SatelliteProvisionStateCallback { provisioned -> + logBuffer.i { + "onSatelliteProvisionStateChanged: " + + if (provisioned) "provisioned" else "not provisioned" + } + trySend(provisioned) } - trySend(provisioned) - } - var registered = false - try { - sm.registerForProvisionStateChanged( - bgDispatcher.asExecutor(), - callback, - ) - registered = true - } catch (e: Exception) { - logBuffer.e("error registering for provisioning state callback", e) + var registered = false + try { + logBuffer.i { "registerForProvisionStateChanged" } + sm.registerForProvisionStateChanged( + bgDispatcher.asExecutor(), + callback, + ) + registered = true + } catch (e: Exception) { + logBuffer.e("error registering for provisioning state callback", e) + } + + awaitClose { + if (registered) { + sm.unregisterForProvisionStateChanged(callback) + } + } } + .flowOn(bgDispatcher) + + /** Check the current satellite provisioning status. */ + private suspend fun queryIsSatelliteProvisioned(sm: SupportedSatelliteManager): Boolean = + withContext(bgDispatcher) { + suspendCancellableCoroutine { continuation -> + val receiver = + object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> { + override fun onResult(result: Boolean) { + logBuffer.i { "requestIsProvisioned.onResult: $result" } + continuation.resume(result) + } - awaitClose { - if (registered) { - sm.unregisterForProvisionStateChanged(callback) + override fun onError(exception: SatelliteManager.SatelliteException) { + logBuffer.e("requestIsProvisioned.onError:", exception) + continuation.resume(false) + } + } + + logBuffer.i { "Query for current satellite provisioned state." } + try { + sm.requestIsProvisioned(bgDispatcher.asExecutor(), receiver) + } catch (e: Exception) { + logBuffer.e("Exception while calling SatelliteManager.requestIsProvisioned:", e) + continuation.resume(false) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt index 890a2e40c94c..f7ff568332ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt @@ -32,6 +32,7 @@ import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CO import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN +import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR import android.telephony.satellite.SatelliteManager.SatelliteException import android.telephony.satellite.SatelliteModemStateCallback import android.telephony.satellite.SatelliteProvisionStateCallback @@ -62,6 +63,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.doAnswer import org.mockito.Mockito.never import org.mockito.Mockito.times @@ -354,13 +356,142 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { } @Test - fun satelliteProvisioned_supported_tracksCallback() = + fun satelliteProvisioned_returnsException_defaultsToFalse() = + testScope.runTest { + // GIVEN satellite is supported on device + doAnswer { + val callback: OutcomeReceiver<Boolean, SatelliteException> = + it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException> + callback.onResult(true) + } + .whenever(satelliteManager) + .requestIsSupported(any(), any()) + + // GIVEN satellite returns an error when asked if provisioned + doAnswer { + val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException> + receiver.onError(SatelliteException(SATELLITE_RESULT_ERROR)) + null + } + .whenever(satelliteManager) + .requestIsProvisioned( + any(), + any<OutcomeReceiver<Boolean, SatelliteException>>() + ) + + // GIVEN we've been up long enough to start querying + systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME) + + underTest = + DeviceBasedSatelliteRepositoryImpl( + Optional.of(satelliteManager), + telephonyManager, + dispatcher, + testScope.backgroundScope, + logBuffer = FakeLogBuffer.Factory.create(), + verboseLogBuffer = FakeLogBuffer.Factory.create(), + systemClock, + ) + + // WHEN we try to check for provisioned status + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + + // THEN well, first we don't throw... + // AND THEN we assume that it's not provisioned + assertThat(provisioned).isFalse() + } + + @Test + fun satelliteProvisioned_throwsWhenQuerying_defaultsToFalse() = + testScope.runTest { + // GIVEN satellite is supported on device + doAnswer { + val callback: OutcomeReceiver<Boolean, SatelliteException> = + it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException> + callback.onResult(true) + } + .whenever(satelliteManager) + .requestIsSupported(any(), any()) + + // GIVEN satellite throws when asked if provisioned + whenever(satelliteManager.requestIsProvisioned(any(), any())) + .thenThrow(SecurityException()) + + // GIVEN we've been up long enough to start querying + systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME) + + underTest = + DeviceBasedSatelliteRepositoryImpl( + Optional.of(satelliteManager), + telephonyManager, + dispatcher, + testScope.backgroundScope, + logBuffer = FakeLogBuffer.Factory.create(), + verboseLogBuffer = FakeLogBuffer.Factory.create(), + systemClock, + ) + + // WHEN we try to check for provisioned status + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + + // THEN well, first we don't throw... + // AND THEN we assume that it's not provisioned + assertThat(provisioned).isFalse() + } + + @Test + fun satelliteProvisioned_supported_provisioned_queriesInitialStateBeforeCallbacks() = + testScope.runTest { + // GIVEN satellite is supported, and provisioned + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + initialSatelliteIsProvisioned = true, + ) + + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + + runCurrent() + + // THEN the current state is requested + verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any()) + + // AND the state is correct + assertThat(provisioned).isTrue() + } + + @Test + fun satelliteProvisioned_supported_notProvisioned_queriesInitialStateBeforeCallbacks() = + testScope.runTest { + // GIVEN satellite is supported, and provisioned + setUpRepo( + uptime = MIN_UPTIME, + satMan = satelliteManager, + satelliteSupported = true, + initialSatelliteIsProvisioned = false, + ) + + val provisioned by collectLastValue(underTest.isSatelliteProvisioned) + + runCurrent() + + // THEN the current state is requested + verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any()) + + // AND the state is correct + assertThat(provisioned).isFalse() + } + + @Test + fun satelliteProvisioned_supported_notInitiallyProvisioned_tracksCallback() = testScope.runTest { // GIVEN satellite is not supported setUpRepo( uptime = MIN_UPTIME, satMan = satelliteManager, satelliteSupported = true, + initialSatelliteIsProvisioned = false, ) val provisioned by collectLastValue(underTest.isSatelliteProvisioned) @@ -416,6 +547,8 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { // THEN listeners are re-registered verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any()) + // AND the state is queried again + verify(satelliteManager, times(2)).requestIsProvisioned(any(), any()) } @Test @@ -632,6 +765,7 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { uptime: Long = MIN_UPTIME, satMan: SatelliteManager? = satelliteManager, satelliteSupported: Boolean = true, + initialSatelliteIsProvisioned: Boolean = true, ) { doAnswer { val callback: OutcomeReceiver<Boolean, SatelliteException> = @@ -641,6 +775,14 @@ class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() { .whenever(satelliteManager) .requestIsSupported(any(), any()) + doAnswer { + val callback: OutcomeReceiver<Boolean, SatelliteException> = + it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException> + callback.onResult(initialSatelliteIsProvisioned) + } + .whenever(satelliteManager) + .requestIsProvisioned(any(), any()) + systemClock.setUptimeMillis(Process.getStartUptimeMillis() + uptime) underTest = |