diff --git a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt index df3b8bad278..1a838741981 100644 --- a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt +++ b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt @@ -20,7 +20,6 @@ import android.app.Application import android.telephony.SubscriptionManager import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import com.android.settings.network.telephony.getSelectableSubscriptionInfoList import com.android.settings.network.telephony.subscriptionsChangedFlow import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.SharingStarted @@ -46,6 +45,6 @@ class SubscriptionInfoListViewModel(application: Application) : AndroidViewModel * getAvailableSubscriptionInfoList */ val selectableSubscriptionInfoListFlow = application.subscriptionsChangedFlow().map { - application.getSelectableSubscriptionInfoList() + SubscriptionUtil.getSelectableSubscriptionInfoList(application) }.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList()) } diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java index a3798565727..a9c4ac517bc 100644 --- a/src/com/android/settings/network/SubscriptionUtil.java +++ b/src/com/android/settings/network/SubscriptionUtil.java @@ -50,12 +50,12 @@ import com.android.settings.network.helper.SelectableSubscriptions; import com.android.settings.network.helper.SubscriptionAnnotation; import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity; import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity; -import com.android.settings.network.telephony.SubscriptionRepositoryKt; import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -507,7 +507,40 @@ public class SubscriptionUtil { * @return list of user selectable subscriptions. */ public static List getSelectableSubscriptionInfoList(Context context) { - return SubscriptionRepositoryKt.getSelectableSubscriptionInfoList(context); + SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class); + List availableList = subManager.getAvailableSubscriptionInfoList(); + if (availableList == null) { + return null; + } else { + // Multiple subscriptions in a group should only have one representative. + // It should be the current active primary subscription if any, or any + // primary subscription. + List selectableList = new ArrayList<>(); + Map groupMap = new HashMap<>(); + + for (SubscriptionInfo info : availableList) { + // Opportunistic subscriptions are considered invisible + // to users so they should never be returned. + if (!isSubscriptionVisible(subManager, context, info)) continue; + + ParcelUuid groupUuid = info.getGroupUuid(); + if (groupUuid == null) { + // Doesn't belong to any group. Add in the list. + selectableList.add(info); + } else if (!groupMap.containsKey(groupUuid) + || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX + && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) { + // If it belongs to a group that has never been recorded or it's the current + // active subscription, add it in the list. + selectableList.remove(groupMap.get(groupUuid)); + selectableList.add(info); + groupMap.put(groupUuid, info); + } + + } + Log.d(TAG, "getSelectableSubscriptionInfoList: " + selectableList); + return selectableList; + } } /** diff --git a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt index f89a0dba16a..4c13d98769c 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt @@ -67,7 +67,7 @@ class MobileNetworkSwitchController @JvmOverloads constructor( } private fun Context.isVisible(): Boolean { - val subInfo = subscriptionRepository.getSelectableSubscriptionInfoList() + val subInfo = SubscriptionUtil.getAvailableSubscriptions(this) .firstOrNull { it.subscriptionId == subId } ?: return false // For eSIM, we always want the toggle. If telephony stack support disabling a pSIM diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt index 8aee2975c09..db5216a9fd6 100644 --- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt +++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt @@ -20,9 +20,7 @@ import android.content.Context import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager import android.util.Log -import androidx.lifecycle.LifecycleOwner import com.android.settings.network.SubscriptionUtil -import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose @@ -36,14 +34,6 @@ import kotlinx.coroutines.flow.onEach private const val TAG = "SubscriptionRepository" class SubscriptionRepository(private val context: Context) { - /** - * Return a list of subscriptions that are available and visible to the user. - * - * @return list of user selectable subscriptions. - */ - fun getSelectableSubscriptionInfoList(): List = - context.getSelectableSubscriptionInfoList() - fun isSubscriptionEnabledFlow(subId: Int) = context.isSubscriptionEnabledFlow(subId) } @@ -77,36 +67,3 @@ fun Context.subscriptionsChangedFlow() = callbackFlow { awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) } }.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default) - -/** - * Return a list of subscriptions that are available and visible to the user. - * - * @return list of user selectable subscriptions. - */ -fun Context.getSelectableSubscriptionInfoList(): List { - val subscriptionManager = requireSubscriptionManager() - val availableList = subscriptionManager.getAvailableSubscriptionInfoList() ?: return emptyList() - val visibleList = availableList.filter { subInfo -> - // Opportunistic subscriptions are considered invisible - // to users so they should never be returned. - SubscriptionUtil.isSubscriptionVisible(subscriptionManager, this, subInfo) - } - // Multiple subscriptions in a group should only have one representative. - // It should be the current active primary subscription if any, or any primary subscription. - val groupUuidToSelectedIdMap = visibleList - .groupBy { it.groupUuid } - .mapValues { (_, subInfos) -> - subInfos.filter { it.simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX } - .ifEmpty { subInfos } - .minOf { it.subscriptionId } - } - - return visibleList - .filter { subInfo -> - val groupUuid = subInfo.groupUuid ?: return@filter true - groupUuidToSelectedIdMap[groupUuid] == subInfo.subscriptionId - } - .sortedBy { it.subscriptionId } - .also { Log.d(TAG, "getSelectableSubscriptionInfoList: $it") } -} - diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt index e7313334a6f..77724e87d7b 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.kt @@ -30,8 +30,11 @@ import androidx.compose.ui.test.onNodeWithText import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R +import com.android.settings.network.SubscriptionUtil import com.android.settingslib.spa.testutils.waitUntilExists import kotlinx.coroutines.flow.flowOf +import org.junit.After +import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -58,7 +61,6 @@ class MobileNetworkSwitchControllerTest { } private val mockSubscriptionRepository = mock { - on { getSelectableSubscriptionInfoList() } doReturn listOf(SubInfo) on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false) } @@ -68,6 +70,16 @@ class MobileNetworkSwitchControllerTest { subscriptionRepository = mockSubscriptionRepository, ).apply { init(SUB_ID) } + @Before + fun setUp() { + SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(SubInfo)) + } + + @After + fun tearDown() { + SubscriptionUtil.setAvailableSubscriptionsForTesting(null) + } + @Test fun isVisible_pSimAndCanDisablePhysicalSubscription_returnTrue() { val pSimSubInfo = SubscriptionInfo.Builder().apply { @@ -77,9 +89,7 @@ class MobileNetworkSwitchControllerTest { mockSubscriptionManager.stub { on { canDisablePhysicalSubscription() } doReturn true } - mockSubscriptionRepository.stub { - on { getSelectableSubscriptionInfoList() } doReturn listOf(pSimSubInfo) - } + SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(pSimSubInfo)) setContent() @@ -96,9 +106,7 @@ class MobileNetworkSwitchControllerTest { mockSubscriptionManager.stub { on { canDisablePhysicalSubscription() } doReturn false } - mockSubscriptionRepository.stub { - on { getSelectableSubscriptionInfoList() } doReturn listOf(pSimSubInfo) - } + SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(pSimSubInfo)) setContent() @@ -112,9 +120,7 @@ class MobileNetworkSwitchControllerTest { setId(SUB_ID) setEmbedded(true) }.build() - mockSubscriptionRepository.stub { - on { getSelectableSubscriptionInfoList() } doReturn listOf(eSimSubInfo) - } + SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(eSimSubInfo)) setContent() diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt index 3f69155a204..15ca2f065ac 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt @@ -24,7 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.android.settingslib.spa.testutils.toListWithTimeout import com.google.common.truth.Truth.assertThat -import java.util.UUID import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking @@ -82,65 +81,6 @@ class SubscriptionRepositoryTest { assertThat(listDeferred.await()).hasSize(2) } - @Test - fun getSelectableSubscriptionInfoList_sortedBySubId() { - mockSubscriptionManager.stub { - on { getAvailableSubscriptionInfoList() } doReturn listOf( - SubscriptionInfo.Builder().apply { - setId(SUB_ID_2) - }.build(), - SubscriptionInfo.Builder().apply { - setId(SUB_ID_1) - }.build(), - ) - } - - val subInfos = context.getSelectableSubscriptionInfoList() - - assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_1, SUB_ID_2).inOrder() - } - - @Test - fun getSelectableSubscriptionInfoList_sameGroupAndOneHasSlot_returnTheOneWithSimSlotIndex() { - mockSubscriptionManager.stub { - on { getAvailableSubscriptionInfoList() } doReturn listOf( - SubscriptionInfo.Builder().apply { - setId(SUB_ID_1) - setGroupUuid(GROUP_UUID) - }.build(), - SubscriptionInfo.Builder().apply { - setId(SUB_ID_2) - setGroupUuid(GROUP_UUID) - setSimSlotIndex(SIM_SLOT_INDEX) - }.build(), - ) - } - - val subInfos = context.getSelectableSubscriptionInfoList() - - assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_2) - } - - @Test - fun getSelectableSubscriptionInfoList_sameGroupAndNonHasSlot_returnTheOneWithMinimumSubId() { - mockSubscriptionManager.stub { - on { getAvailableSubscriptionInfoList() } doReturn listOf( - SubscriptionInfo.Builder().apply { - setId(SUB_ID_2) - setGroupUuid(GROUP_UUID) - }.build(), - SubscriptionInfo.Builder().apply { - setId(SUB_ID_1) - setGroupUuid(GROUP_UUID) - }.build(), - ) - } - - val subInfos = context.getSelectableSubscriptionInfoList() - - assertThat(subInfos.map { it.subscriptionId }).containsExactly(SUB_ID_1) - } - @Test fun phoneNumberFlow() = runBlocking { mockSubscriptionManager.stub { @@ -158,9 +98,6 @@ class SubscriptionRepositoryTest { private companion object { const val SUB_ID_1 = 1 - const val SUB_ID_2 = 2 - val GROUP_UUID = UUID.randomUUID().toString() - const val SIM_SLOT_INDEX = 1 const val NUMBER_1 = "000000001" const val MCC = "310" }