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
This commit is contained in:
@@ -21,14 +21,15 @@ import android.telephony.SubscriptionManager
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.network.SubscriptionUtil
|
|
||||||
import com.android.settings.spa.preference.ComposePreferenceController
|
import com.android.settings.spa.preference.ComposePreferenceController
|
||||||
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
|
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
|
||||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MobileNetworkSwitchController @JvmOverloads constructor(
|
class MobileNetworkSwitchController @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -56,12 +57,15 @@ class MobileNetworkSwitchController @JvmOverloads constructor(
|
|||||||
val changeable by remember {
|
val changeable by remember {
|
||||||
subscriptionActivationRepository.isActivationChangeableFlow()
|
subscriptionActivationRepository.isActivationChangeableFlow()
|
||||||
}.collectAsStateWithLifecycle(initialValue = true)
|
}.collectAsStateWithLifecycle(initialValue = true)
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
MainSwitchPreference(model = object : SwitchPreferenceModel {
|
MainSwitchPreference(model = object : SwitchPreferenceModel {
|
||||||
override val title = stringResource(R.string.mobile_network_use_sim_on)
|
override val title = stringResource(R.string.mobile_network_use_sim_on)
|
||||||
override val changeable = { changeable }
|
override val changeable = { changeable }
|
||||||
override val checked = { checked }
|
override val checked = { checked }
|
||||||
override val onCheckedChange = { newChecked: Boolean ->
|
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
|
||||||
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, newChecked)
|
coroutineScope.launch {
|
||||||
|
subscriptionActivationRepository.setActive(subId, newChecked)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,18 @@
|
|||||||
package com.android.settings.network.telephony
|
package com.android.settings.network.telephony
|
||||||
|
|
||||||
import android.content.Context
|
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.SatelliteRepository
|
||||||
|
import com.android.settings.network.SimOnboardingActivity.Companion.startSimOnboardingActivity
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class SubscriptionActivationRepository(
|
class SubscriptionActivationRepository(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
@@ -32,4 +41,36 @@ class SubscriptionActivationRepository(
|
|||||||
) { isInCall, isSatelliteModemEnabled ->
|
) { isInCall, isSatelliteModemEnabled ->
|
||||||
!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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
@@ -47,6 +48,7 @@ import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
|
|||||||
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
|
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
|
||||||
import com.android.settingslib.spaprivileged.template.preference.RestrictedTwoTargetSwitchPreference
|
import com.android.settingslib.spaprivileged.template.preference.RestrictedTwoTargetSwitchPreference
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SimsSection(subscriptionInfoList: List<SubscriptionInfo>) {
|
fun SimsSection(subscriptionInfoList: List<SubscriptionInfo>) {
|
||||||
@@ -71,9 +73,11 @@ private fun SimPreference(subInfo: SubscriptionInfo) {
|
|||||||
emit(SubscriptionUtil.isConvertedPsimSubscription(subInfo))
|
emit(SubscriptionUtil.isConvertedPsimSubscription(subInfo))
|
||||||
}
|
}
|
||||||
}.collectAsStateWithLifecycle(initialValue = false)
|
}.collectAsStateWithLifecycle(initialValue = false)
|
||||||
|
val subscriptionActivationRepository = remember { SubscriptionActivationRepository(context) }
|
||||||
val isActivationChangeable by remember {
|
val isActivationChangeable by remember {
|
||||||
SubscriptionActivationRepository(context).isActivationChangeableFlow()
|
subscriptionActivationRepository.isActivationChangeableFlow()
|
||||||
}.collectAsStateWithLifecycle(initialValue = false)
|
}.collectAsStateWithLifecycle(initialValue = false)
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
RestrictedTwoTargetSwitchPreference(
|
RestrictedTwoTargetSwitchPreference(
|
||||||
model = object : SwitchPreferenceModel {
|
model = object : SwitchPreferenceModel {
|
||||||
override val title = subInfo.displayName.toString()
|
override val title = subInfo.displayName.toString()
|
||||||
@@ -87,12 +91,10 @@ private fun SimPreference(subInfo: SubscriptionInfo) {
|
|||||||
override val icon = @Composable { SimIcon(subInfo.isEmbedded) }
|
override val icon = @Composable { SimIcon(subInfo.isEmbedded) }
|
||||||
override val changeable = { isActivationChangeable && !isConvertedPsim }
|
override val changeable = { isActivationChangeable && !isConvertedPsim }
|
||||||
override val checked = { checked.value }
|
override val checked = { checked.value }
|
||||||
override val onCheckedChange = { newChecked: Boolean ->
|
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
|
||||||
SubscriptionUtil.startToggleSubscriptionDialogActivity(
|
coroutineScope.launch {
|
||||||
context,
|
subscriptionActivationRepository.setActive(subInfo.subscriptionId, newChecked)
|
||||||
subInfo.subscriptionId,
|
}
|
||||||
newChecked,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)),
|
restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)),
|
||||||
|
@@ -17,6 +17,9 @@
|
|||||||
package com.android.settings.network.telephony
|
package com.android.settings.network.telephony
|
||||||
|
|
||||||
import android.content.Context
|
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.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settings.network.SatelliteRepository
|
import com.android.settings.network.SatelliteRepository
|
||||||
@@ -26,14 +29,29 @@ import kotlinx.coroutines.flow.flowOf
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
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.doReturn
|
||||||
import org.mockito.kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.never
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
import org.mockito.kotlin.stub
|
import org.mockito.kotlin.stub
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class SubscriptionActivationRepositoryTest {
|
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 mockCallStateRepository = mock<CallStateRepository>()
|
||||||
private val mockSatelliteRepository = mock<SatelliteRepository>()
|
private val mockSatelliteRepository = mock<SatelliteRepository>()
|
||||||
|
|
||||||
@@ -81,4 +99,39 @@ class SubscriptionActivationRepositoryTest {
|
|||||||
|
|
||||||
assertThat(changeable).isFalse()
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user