From c6005fb22a738cacba75d3a155d788dad6a2ef17 Mon Sep 17 00:00:00 2001 From: Thomas Nguyen Date: Tue, 23 Apr 2024 11:26:58 -0700 Subject: [PATCH] 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 --- .../settings/network/SatelliteRepository.kt | 52 +++++++++++++++++- .../sim/receivers/SimSlotChangeReceiver.java | 21 ++++---- .../network/SatelliteRepositoryTest.kt | 54 +++++++++++++++++-- 3 files changed, 113 insertions(+), 14 deletions(-) diff --git a/src/com/android/settings/network/SatelliteRepository.kt b/src/com/android/settings/network/SatelliteRepository.kt index 4145e018f47..09b7781d107 100644 --- a/src/com/android/settings/network/SatelliteRepository.kt +++ b/src/com/android/settings/network/SatelliteRepository.kt @@ -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 { + 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 + } + } } diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java index 4920bb80e05..406e083c0f7 100644 --- a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java +++ b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java @@ -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 satelliteEnabledFuture = new SatelliteRepository(context) - .requestIsEnabled(executor); - satelliteEnabledFuture.addListener(() -> { - boolean isSatelliteEnabled = false; + ListenableFuture 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 432048cbc33..5c6e7eb2941 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 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(1) + callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED) + SatelliteManager.SATELLITE_RESULT_SUCCESS + } + + val result: ListenableFuture = 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(1) + callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_OFF) + SatelliteManager.SATELLITE_RESULT_SUCCESS + } + + val result: ListenableFuture = 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 = 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 = repository.requestIsSessionStarted(mockExecutor) + assertFalse(result.get()) + verifyNoInteractions(mockSatelliteManager) + } + @Test fun requestIsEnabled_resultIsFalse() = runBlocking { `when`(