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
diff --git a/src/com/android/settings/network/SatelliteRepository.kt b/src/com/android/settings/network/SatelliteRepository.kt
index 4145e01..09b7781 100644
--- a/src/com/android/settings/network/SatelliteRepository.kt
+++ b/src/com/android/settings/network/SatelliteRepository.kt
@@ -25,7 +25,6 @@
 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.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.
@@ -75,6 +75,41 @@
     }
 
     /**
+     * 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 @@
     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
+        }
+    }
 }
 
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
index 4920bb8..406e083 100644
--- a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
@@ -56,22 +56,23 @@
 
     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);
diff --git a/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt b/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt
index 432048c..5c6e7eb 100644
--- a/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt
+++ b/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt
@@ -24,7 +24,6 @@
 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.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)
@@ -91,6 +90,55 @@
     }
 
     @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`(
             mockSatelliteManager.requestIsEnabled(