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

Commit c6005fb2 authored by Thomas Nguyen's avatar Thomas Nguyen
Browse files

Disable SIM On/Off operation when device is in a Satellite session

Bug: 330585109
Test: SatelliteManagerTestOnMockService SatelliteSessionControllerTest SatelliteControllerTest
Manual test with demo and real mode

Change-Id: Iade6426981f76a0b9b71828e0c86d3088c3e974e
parent 58b70959
Loading
Loading
Loading
Loading
+51 −1
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import androidx.annotation.VisibleForTesting
import androidx.concurrent.futures.CallbackToFutureAdapter
import com.google.common.util.concurrent.Futures.immediateFuture
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
@@ -33,6 +32,7 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOf
import java.util.concurrent.Executor

/**
 * A repository class for interacting with the SatelliteManager API.
@@ -74,6 +74,41 @@ class SatelliteRepository(
        }
    }

    /**
     * Checks if a satellite session has started.
     *
     * @param executor The executor to run the asynchronous operation on
     * @return A ListenableFuture that will resolve to `true` if a satellite session has started,
     *         `false` otherwise.
     */
    fun requestIsSessionStarted(executor: Executor): ListenableFuture<Boolean> {
        val satelliteManager: SatelliteManager? =
            context.getSystemService(SatelliteManager::class.java)
        if (satelliteManager == null) {
            Log.w(TAG, "SatelliteManager is null")
            return immediateFuture(false)
        }

        return CallbackToFutureAdapter.getFuture { completer ->
            val callback = object : SatelliteModemStateCallback {
                override fun onSatelliteModemStateChanged(state: Int) {
                    val isSessionStarted = isSatelliteSessionStarted(state)
                    Log.i(TAG, "Satellite modem state changed: state=$state"
                            + ", isSessionStarted=$isSessionStarted")
                    completer.set(isSessionStarted)
                    satelliteManager.unregisterForModemStateChanged(this)
                }
            }

            val registerResult = satelliteManager.registerForModemStateChanged(executor, callback)
            if (registerResult != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                Log.w(TAG, "Failed to register for satellite modem state change: $registerResult")
                completer.set(false)
            }
            "requestIsSessionStarted"
        }
    }

    /**
     * Provides a Flow that emits the enabled state of the satellite modem. Updates are triggered
     * when the modem state changes.
@@ -134,5 +169,20 @@ class SatelliteRepository(
    companion object {
        private const val TAG: String = "SatelliteRepository"
    }

    /**
     * Check if the modem is in a satellite session.
     *
     * @param state The SatelliteModemState provided by the SatelliteManager.
     * @return `true` if the modem is in a satellite session, `false` otherwise.
     */
    fun isSatelliteSessionStarted(@SatelliteManager.SatelliteModemState state: Int): Boolean {
        return when (state) {
            SatelliteManager.SATELLITE_MODEM_STATE_OFF,
            SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE,
            SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN -> false
            else -> true
        }
    }
}
+11 −10
Original line number Diff line number Diff line
@@ -56,22 +56,23 @@ public class SimSlotChangeReceiver extends BroadcastReceiver {

    public static void runOnBackgroundThread(Context context) {
        if (shouldHandleSlotChange(context)) {
            Log.d(TAG, "Checking satellite enabled status");
            Log.d(TAG, "Checking satellite session status");
            Executor executor = Executors.newSingleThreadExecutor();
            ListenableFuture<Boolean> satelliteEnabledFuture = new SatelliteRepository(context)
                    .requestIsEnabled(executor);
            satelliteEnabledFuture.addListener(() -> {
                boolean isSatelliteEnabled = false;
            ListenableFuture<Boolean> isSatelliteSessionStartedFuture =
                    new SatelliteRepository(context).requestIsSessionStarted(executor);
            isSatelliteSessionStartedFuture.addListener(() -> {
                boolean isSatelliteSessionStarted = false;
                try {
                    isSatelliteEnabled = satelliteEnabledFuture.get();
                    isSatelliteSessionStarted = isSatelliteSessionStartedFuture.get();
                } catch (ExecutionException | InterruptedException e) {
                    Log.w(TAG, "Can't get satellite enabled status", e);
                    Log.w(TAG, "Can't get satellite session status", e);
                }

                if (isSatelliteEnabled) {
                    Log.i(TAG, "Satellite is enabled. Unable to handle SIM slot changes");
                if (isSatelliteSessionStarted) {
                    Log.i(TAG, "Device is in a satellite session. Unable to handle SIM slot"
                            + " changes");
                } else {
                    Log.i(TAG, "Satellite is disabled. Handle slot changes");
                    Log.i(TAG, "Not in a satellite session. Handle slot changes");
                    SimSlotChangeHandler.get().onSlotsStatusChange(context.getApplicationContext());
                }
            }, executor);
+51 −3
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.telephony.satellite.SatelliteModemStateCallback
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.Executor
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertFalse
@@ -35,12 +34,12 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.`when`
import org.mockito.Mockito.*
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.robolectric.RobolectricTestRunner
import java.util.concurrent.Executor


@RunWith(RobolectricTestRunner::class)
@@ -90,6 +89,55 @@ class SatelliteRepositoryTest {
        assertTrue(result.get())
    }

    @Test
    fun requestIsSessionStarted_resultIsTrue() = runBlocking {
        `when`(mockSatelliteManager.registerForModemStateChanged(any(), any())
        ).thenAnswer { invocation ->
            val callback = invocation.getArgument<SatelliteModemStateCallback>(1)
            callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED)
            SatelliteManager.SATELLITE_RESULT_SUCCESS
        }

        val result: ListenableFuture<Boolean> = repository.requestIsSessionStarted(mockExecutor)
        assertTrue(result.get())
        verify(mockSatelliteManager).unregisterForModemStateChanged(any())
    }

    @Test
    fun requestIsSessionStarted_resultIsFalse() = runBlocking {
        `when`(mockSatelliteManager.registerForModemStateChanged(any(), any())
        ).thenAnswer { invocation ->
            val callback = invocation.getArgument<SatelliteModemStateCallback>(1)
            callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_OFF)
            SatelliteManager.SATELLITE_RESULT_SUCCESS
        }

        val result: ListenableFuture<Boolean> = repository.requestIsSessionStarted(mockExecutor)
        assertFalse(result.get())
        verify(mockSatelliteManager).unregisterForModemStateChanged(any())
    }

    @Test
    fun requestIsSessionStarted_registerFailed() = runBlocking {
        `when`(mockSatelliteManager.registerForModemStateChanged(any(), any())
        ).thenAnswer { invocation ->
            SatelliteManager.SATELLITE_RESULT_ERROR
        }

        val result: ListenableFuture<Boolean> = repository.requestIsSessionStarted(mockExecutor)
        assertFalse(result.get())
        verify(mockSatelliteManager, never()).unregisterForModemStateChanged(any())
    }

    @Test
    fun requestIsSessionStarted_nullSatelliteManager() = runBlocking {
        `when`(spyContext.getSystemService(SatelliteManager::class.java)).thenReturn(null)

        val result: ListenableFuture<Boolean> = repository.requestIsSessionStarted(mockExecutor)
        assertFalse(result.get())
        verifyNoInteractions(mockSatelliteManager)
    }

    @Test
    fun requestIsEnabled_resultIsFalse() = runBlocking {
        `when`(