Use old getSelectableSubscriptionInfoList

24D1-dev was using the
SubscriptionUtil.getSelectableSubscriptionInfoList().

Change I215b5a4615a3b3da6fc160f76c85c814210cc3ef cherry-pick the new
getSelectableSubscriptionInfoList from main, but not the latest version,
could produce an unexpected subscription order.

Main branch already fixed by I06b6c61304f9e6c9515b5f989294417ac3f82a60
and If7150bd2fa5c1b7d42fe34ef2e814ba540454ed8.

Fix: 341037896
Test: manual - on Mobile Settings
Merged-In: I06b6c61304f9e6c9515b5f989294417ac3f82a60
Change-Id: I5cb16b60a7bf5df5b048e539f89bfedc43aa1582
This commit is contained in:
Chaohui Wang
2024-06-20 16:19:59 +08:00
parent f9a571ddef
commit f1b3c2b9c9
6 changed files with 53 additions and 121 deletions

View File

@@ -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())
}

View File

@@ -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<SubscriptionInfo> getSelectableSubscriptionInfoList(Context context) {
return SubscriptionRepositoryKt.getSelectableSubscriptionInfoList(context);
SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
List<SubscriptionInfo> 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<SubscriptionInfo> selectableList = new ArrayList<>();
Map<ParcelUuid, SubscriptionInfo> 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;
}
}
/**

View File

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

View File

@@ -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<SubscriptionInfo> =
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<SubscriptionInfo> {
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") }
}

View File

@@ -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<SubscriptionRepository> {
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()

View File

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