Merge "Check the Satellite modem state instead of enabled state only." into main
diff --git a/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
index d69c87b..2dc2650 100644
--- a/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
@@ -21,6 +21,7 @@
 import android.content.Intent
 import android.os.OutcomeReceiver
 import android.telephony.satellite.SatelliteManager
+import android.telephony.satellite.SatelliteModemStateCallback
 import android.util.Log
 import android.view.WindowManager
 import androidx.lifecycle.LifecycleOwner
@@ -31,12 +32,19 @@
 import kotlinx.coroutines.Dispatchers.Default
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlinx.coroutines.withContext
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.TimeoutException
 import kotlin.coroutines.resume
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOn
 
 /** A util for Satellite dialog */
 object SatelliteDialogUtils {
@@ -70,7 +78,7 @@
             coroutineScope.launch {
                 var isSatelliteModeOn = false
                 try {
-                    isSatelliteModeOn = requestIsEnabled(context)
+                    isSatelliteModeOn = requestIsSessionStarted(context)
                 } catch (e: InterruptedException) {
                     Log.w(TAG, "Error to get satellite status : $e")
                 } catch (e: ExecutionException) {
@@ -134,6 +142,70 @@
         }
     }
 
+    private suspend fun requestIsSessionStarted(
+            context: Context
+    ): Boolean = withContext(Default) {
+        getIsSessionStartedFlow(context).conflate().first()
+    }
+
+    /**
+     * Provides a Flow that emits the session state of the satellite modem. Updates are triggered
+     * when the modem state changes.
+     *
+     * @param defaultDispatcher The CoroutineDispatcher to use (Defaults to `Dispatchers.Default`).
+     * @return A Flow emitting `true` when the session is started and `false` otherwise.
+     */
+    private fun getIsSessionStartedFlow(
+            context: Context
+    ): Flow<Boolean> {
+        val satelliteManager: SatelliteManager? =
+                context.getSystemService(SatelliteManager::class.java)
+        if (satelliteManager == null) {
+            Log.w(TAG, "SatelliteManager is null")
+            return flowOf(false)
+        }
+
+        return callbackFlow {
+            val callback = SatelliteModemStateCallback { state ->
+                val isSessionStarted = isSatelliteSessionStarted(state)
+                Log.i(TAG, "Satellite modem state changed: state=$state"
+                        + ", isSessionStarted=$isSessionStarted")
+                trySend(isSessionStarted)
+            }
+
+            val registerResult = satelliteManager.registerForModemStateChanged(
+                    Default.asExecutor(),
+                    callback
+            )
+
+            if (registerResult != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+                // If the registration failed (e.g., device doesn't support satellite),
+                // SatelliteManager will not emit the current state by callback.
+                // We send `false` value by ourself to make sure the flow has initial value.
+                Log.w(TAG, "Failed to register for satellite modem state change: $registerResult")
+                trySend(false)
+            }
+
+            awaitClose { satelliteManager.unregisterForModemStateChanged(callback) }
+        }.flowOn(Default)
+    }
+
+
+    /**
+     * 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
+        }
+    }
+
     const val TAG = "SatelliteDialogUtils"
 
     const val EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG: String =
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
index aeda1ed6..31d7130 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
@@ -17,11 +17,12 @@
 package com.android.settingslib.satellite
 
 import android.content.Context
-import android.content.Intent
-import android.os.OutcomeReceiver
 import android.platform.test.annotations.RequiresFlagsEnabled
 import android.telephony.satellite.SatelliteManager
-import android.telephony.satellite.SatelliteManager.SatelliteException
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE
+import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
+import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR
+import android.telephony.satellite.SatelliteModemStateCallback
 import android.util.AndroidRuntimeException
 import androidx.test.core.app.ApplicationProvider
 import com.android.internal.telephony.flags.Flags
@@ -67,26 +68,21 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_satelliteIsOn_showWarningDialog() = runBlocking {
-        `when`(
-                satelliteManager.requestIsEnabled(
-                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
-                )
-        )
+        `when`(satelliteManager.registerForModemStateChanged(any(), any()))
                 .thenAnswer { invocation ->
-                    val receiver = invocation
-                            .getArgument<
-                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+                    val callback = invocation
+                            .getArgument<SatelliteModemStateCallback>(
                                     1
                             )
-                    receiver.onResult(true)
+                    callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_ENABLING_SATELLITE)
                     null
                 }
 
         try {
             SatelliteDialogUtils.mayStartSatelliteWarningDialog(
                     context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                        assertTrue(it)
-                })
+                assertTrue(it)
+            })
         } catch (e: AndroidRuntimeException) {
             // Catch exception of starting activity .
         }
@@ -95,68 +91,49 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_satelliteIsOff_notShowWarningDialog() = runBlocking {
-        `when`(
-                satelliteManager.requestIsEnabled(
-                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
-                )
-        )
+        `when`(satelliteManager.registerForModemStateChanged(any(), any()))
                 .thenAnswer { invocation ->
-                    val receiver = invocation
-                            .getArgument<
-                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+                    val callback = invocation
+                            .getArgument<SatelliteModemStateCallback>(
                                     1
                             )
-                    receiver.onResult(false)
+                    callback.onSatelliteModemStateChanged(SATELLITE_MODEM_STATE_OFF)
                     null
                 }
 
 
         SatelliteDialogUtils.mayStartSatelliteWarningDialog(
-            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                assertFalse(it)
-            })
+                context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+            assertFalse(it)
+        })
 
-        verify(context, Times(0)).startActivity(any<Intent>())
+        verify(context, Times(0)).startActivity(any())
     }
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_noSatelliteManager_notShowWarningDialog() = runBlocking {
-        `when`(context.getSystemService(SatelliteManager::class.java))
-                .thenReturn(null)
+        `when`(context.getSystemService(SatelliteManager::class.java)).thenReturn(null)
 
         SatelliteDialogUtils.mayStartSatelliteWarningDialog(
-            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                assertFalse(it)
-            })
+                context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+            assertFalse(it)
+        })
 
-        verify(context, Times(0)).startActivity(any<Intent>())
+        verify(context, Times(0)).startActivity(any())
     }
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     fun mayStartSatelliteWarningDialog_satelliteErrorResult_notShowWarningDialog() = runBlocking {
-        `when`(
-                satelliteManager.requestIsEnabled(
-                        any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
-                )
-        )
-                .thenAnswer { invocation ->
-                    val receiver = invocation
-                            .getArgument<
-                                    OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
-                                    1
-                            )
-                    receiver.onError(SatelliteException(SatelliteManager.SATELLITE_RESULT_ERROR))
-                    null
-                }
-
+        `when`(satelliteManager.registerForModemStateChanged(any(), any()))
+                .thenReturn(SATELLITE_RESULT_MODEM_ERROR)
 
         SatelliteDialogUtils.mayStartSatelliteWarningDialog(
-            context, coroutineScope, TYPE_IS_WIFI, allowClick = {
-                assertFalse(it)
-            })
+                context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+            assertFalse(it)
+        })
 
-        verify(context, Times(0)).startActivity(any<Intent>())
+        verify(context, Times(0)).startActivity(any())
     }
 }