Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1bfea5d4 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

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
parent ffa3d11e
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -21,14 +21,15 @@ import android.telephony.SubscriptionManager
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 @@ class MobileNetworkSwitchController @JvmOverloads constructor(
        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)
                }
            }
        })
    }
+41 −0
Original line number Diff line number Diff line
@@ -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 @@ class SubscriptionActivationRepository(
    ) { 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"
    }
}
+9 −7
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable
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.model.enterprise.Restrictions
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 @@ private fun SimPreference(subInfo: SubscriptionInfo) {
            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 @@ private fun SimPreference(subInfo: SubscriptionInfo) {
            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)),
+54 −1
Original line number Diff line number Diff line
@@ -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.flow.flowOf
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 @@ class SubscriptionActivationRepositoryTest {

        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
    }
}