Check if ECBMode when deactivate SIM card
If in ECBMode, start ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS to show a
dialog instead.
This align with the current airplane mode switch.
Fix: 191943857
Test: adb shell cmd phone emergency-callback-mode
Test: unit test
Change-Id: Icf646cd76990d621121b4367ec0fd02a3880b85c
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt
index 6c5127f..63364f9 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt
+++ b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt
@@ -21,14 +21,15 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
-import com.android.settings.network.SubscriptionUtil
import com.android.settings.spa.preference.ComposePreferenceController
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import kotlinx.coroutines.launch
class MobileNetworkSwitchController @JvmOverloads constructor(
context: Context,
@@ -56,12 +57,15 @@
val changeable by remember {
subscriptionActivationRepository.isActivationChangeableFlow()
}.collectAsStateWithLifecycle(initialValue = true)
+ val coroutineScope = rememberCoroutineScope()
MainSwitchPreference(model = object : SwitchPreferenceModel {
override val title = stringResource(R.string.mobile_network_use_sim_on)
override val changeable = { changeable }
override val checked = { checked }
- override val onCheckedChange = { newChecked: Boolean ->
- SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, newChecked)
+ override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
+ coroutineScope.launch {
+ subscriptionActivationRepository.setActive(subId, newChecked)
+ }
}
})
}
diff --git a/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt b/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt
index 416dda1..185af0c 100644
--- a/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt
+++ b/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt
@@ -17,9 +17,18 @@
package com.android.settings.network.telephony
import android.content.Context
+import android.content.Intent
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
+import android.util.Log
+import com.android.settings.Utils
+import com.android.settings.flags.Flags
import com.android.settings.network.SatelliteRepository
+import com.android.settings.network.SimOnboardingActivity.Companion.startSimOnboardingActivity
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.withContext
class SubscriptionActivationRepository(
private val context: Context,
@@ -32,4 +41,36 @@
) { isInCall, isSatelliteModemEnabled ->
!isInCall && !isSatelliteModemEnabled
}
+
+ /**
+ * Starts a dialog activity to handle SIM enabling / disabling.
+ * @param subId The id of subscription need to be enabled or disabled.
+ * @param active Whether the subscription with [subId] should be enabled or disabled.
+ */
+ suspend fun setActive(subId: Int, active: Boolean) {
+ if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
+ Log.i(TAG, "Unable to toggle subscription due to unusable subscription ID.")
+ return
+ }
+ if (!active && isEmergencyCallbackMode(subId)) {
+ val intent = Intent(ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS).apply {
+ setPackage(Utils.PHONE_PACKAGE_NAME)
+ }
+ context.startActivity(intent)
+ return
+ }
+ if (active && Flags.isDualSimOnboardingEnabled()) {
+ startSimOnboardingActivity(context, subId)
+ return
+ }
+ context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, active))
+ }
+
+ private suspend fun isEmergencyCallbackMode(subId: Int) = withContext(Dispatchers.Default) {
+ context.telephonyManager(subId).emergencyCallbackMode
+ }
+
+ private companion object {
+ private const val TAG = "SubscriptionActivationR"
+ }
}
diff --git a/src/com/android/settings/spa/network/SimsSection.kt b/src/com/android/settings/spa/network/SimsSection.kt
index 07da034..842656e 100644
--- a/src/com/android/settings/spa/network/SimsSection.kt
+++ b/src/com/android/settings/spa/network/SimsSection.kt
@@ -30,6 +30,7 @@
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -47,6 +48,7 @@
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
import com.android.settingslib.spaprivileged.template.preference.RestrictedTwoTargetSwitchPreference
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.launch
@Composable
fun SimsSection(subscriptionInfoList: List<SubscriptionInfo>) {
@@ -71,9 +73,11 @@
emit(SubscriptionUtil.isConvertedPsimSubscription(subInfo))
}
}.collectAsStateWithLifecycle(initialValue = false)
+ val subscriptionActivationRepository = remember { SubscriptionActivationRepository(context) }
val isActivationChangeable by remember {
- SubscriptionActivationRepository(context).isActivationChangeableFlow()
+ subscriptionActivationRepository.isActivationChangeableFlow()
}.collectAsStateWithLifecycle(initialValue = false)
+ val coroutineScope = rememberCoroutineScope()
RestrictedTwoTargetSwitchPreference(
model = object : SwitchPreferenceModel {
override val title = subInfo.displayName.toString()
@@ -87,12 +91,10 @@
override val icon = @Composable { SimIcon(subInfo.isEmbedded) }
override val changeable = { isActivationChangeable && !isConvertedPsim }
override val checked = { checked.value }
- override val onCheckedChange = { newChecked: Boolean ->
- SubscriptionUtil.startToggleSubscriptionDialogActivity(
- context,
- subInfo.subscriptionId,
- newChecked,
- )
+ override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
+ coroutineScope.launch {
+ subscriptionActivationRepository.setActive(subInfo.subscriptionId, newChecked)
+ }
}
},
restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)),
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt
index dd9c505..427ab7b 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt
@@ -17,6 +17,9 @@
package com.android.settings.network.telephony
import android.content.Context
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.network.SatelliteRepository
@@ -26,14 +29,29 @@
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
+import org.mockito.kotlin.doNothing
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class SubscriptionActivationRepositoryTest {
- private val context: Context = ApplicationProvider.getApplicationContext()
+ private val mockTelephonyManager = mock<TelephonyManager> {
+ on { createForSubscriptionId(SUB_ID) } doReturn mock
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ doNothing().whenever(mock).startActivity(any())
+ on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
+ }
+
private val mockCallStateRepository = mock<CallStateRepository>()
private val mockSatelliteRepository = mock<SatelliteRepository>()
@@ -81,4 +99,39 @@
assertThat(changeable).isFalse()
}
+
+ @Test
+ fun setActive_defaultSubId_doNothing() = runBlocking {
+ repository.setActive(subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, active = true)
+
+ verify(context, never()).startActivity(any())
+ }
+
+ @Test
+ fun setActive_turnOffAndIsEmergencyCallbackMode() = runBlocking {
+ mockTelephonyManager.stub {
+ on { emergencyCallbackMode } doReturn true
+ }
+
+ repository.setActive(subId = SUB_ID, active = false)
+
+ verify(context).startActivity(argThat { action == ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS })
+ }
+
+ @Test
+ fun setActive_turnOffAndNotEmergencyCallbackMode() = runBlocking {
+ mockTelephonyManager.stub {
+ on { emergencyCallbackMode } doReturn false
+ }
+
+ repository.setActive(subId = SUB_ID, active = false)
+
+ verify(context).startActivity(argThat {
+ component?.className == ToggleSubscriptionDialogActivity::class.qualifiedName
+ })
+ }
+
+ private companion object {
+ const val SUB_ID = 1
+ }
}