Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bcf8c31e authored by Evan Laird's avatar Evan Laird
Browse files

[Sat] Change the satellite provisioned check to eager; query when start

SatelliteManager does not fire the current state for registered
callbacks at the time of registration. This change adds an
initialization step to `registerForProvisionStateChanged` such that we
always query the current status of the provisioned state and send it
through the Flow before continuing with the callback registration.

Also, we change the sharing strategy to `Eagerly` since we don't want to
generate unnecessary IPC if the device goes in and out of OOS.

Bug: 347083255
Test: DeviceBasedSatelliteRepositoryImplTest
Flag: NONE bugfix
Change-Id: Ic7025223ae803abd1b772e72fab3ccd11b175929
parent 9fc295d6
Loading
Loading
Loading
Loading
+54 −19
Original line number Diff line number Diff line
@@ -345,10 +345,16 @@ constructor(
                orElse = flowOf(false),
                retrySignal = telephonyProcessCrashedEvent,
            )
            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
            .stateIn(scope, SharingStarted.Eagerly, false)

    private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> =
        conflatedCallbackFlow {
                // 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: " +
@@ -359,6 +365,7 @@ constructor(

                var registered = false
                try {
                    logBuffer.i { "registerForProvisionStateChanged" }
                    sm.registerForProvisionStateChanged(
                        bgDispatcher.asExecutor(),
                        callback,
@@ -374,6 +381,34 @@ constructor(
                    }
                }
            }
            .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)
                        }

                        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)
                }
            }
        }

    /**
     * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there
+143 −1
Original line number Diff line number Diff line
@@ -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 =