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:
@@ -20,7 +20,6 @@ import android.app.Application
|
|||||||
import android.telephony.SubscriptionManager
|
import android.telephony.SubscriptionManager
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.android.settings.network.telephony.getSelectableSubscriptionInfoList
|
|
||||||
import com.android.settings.network.telephony.subscriptionsChangedFlow
|
import com.android.settings.network.telephony.subscriptionsChangedFlow
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -46,6 +45,6 @@ class SubscriptionInfoListViewModel(application: Application) : AndroidViewModel
|
|||||||
* getAvailableSubscriptionInfoList
|
* getAvailableSubscriptionInfoList
|
||||||
*/
|
*/
|
||||||
val selectableSubscriptionInfoListFlow = application.subscriptionsChangedFlow().map {
|
val selectableSubscriptionInfoListFlow = application.subscriptionsChangedFlow().map {
|
||||||
application.getSelectableSubscriptionInfoList()
|
SubscriptionUtil.getSelectableSubscriptionInfoList(application)
|
||||||
}.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList())
|
}.stateIn(scope, SharingStarted.Eagerly, initialValue = emptyList())
|
||||||
}
|
}
|
||||||
|
@@ -50,12 +50,12 @@ import com.android.settings.network.helper.SelectableSubscriptions;
|
|||||||
import com.android.settings.network.helper.SubscriptionAnnotation;
|
import com.android.settings.network.helper.SubscriptionAnnotation;
|
||||||
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
||||||
import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity;
|
import com.android.settings.network.telephony.EuiccRacConnectivityDialogActivity;
|
||||||
import com.android.settings.network.telephony.SubscriptionRepositoryKt;
|
|
||||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -507,7 +507,40 @@ public class SubscriptionUtil {
|
|||||||
* @return list of user selectable subscriptions.
|
* @return list of user selectable subscriptions.
|
||||||
*/
|
*/
|
||||||
public static List<SubscriptionInfo> getSelectableSubscriptionInfoList(Context context) {
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -67,7 +67,7 @@ class MobileNetworkSwitchController @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Context.isVisible(): Boolean {
|
private fun Context.isVisible(): Boolean {
|
||||||
val subInfo = subscriptionRepository.getSelectableSubscriptionInfoList()
|
val subInfo = SubscriptionUtil.getAvailableSubscriptions(this)
|
||||||
.firstOrNull { it.subscriptionId == subId }
|
.firstOrNull { it.subscriptionId == subId }
|
||||||
?: return false
|
?: return false
|
||||||
// For eSIM, we always want the toggle. If telephony stack support disabling a pSIM
|
// For eSIM, we always want the toggle. If telephony stack support disabling a pSIM
|
||||||
|
@@ -20,9 +20,7 @@ import android.content.Context
|
|||||||
import android.telephony.SubscriptionInfo
|
import android.telephony.SubscriptionInfo
|
||||||
import android.telephony.SubscriptionManager
|
import android.telephony.SubscriptionManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import com.android.settings.network.SubscriptionUtil
|
import com.android.settings.network.SubscriptionUtil
|
||||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.asExecutor
|
import kotlinx.coroutines.asExecutor
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
@@ -36,14 +34,6 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
private const val TAG = "SubscriptionRepository"
|
private const val TAG = "SubscriptionRepository"
|
||||||
|
|
||||||
class SubscriptionRepository(private val context: Context) {
|
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)
|
fun isSubscriptionEnabledFlow(subId: Int) = context.isSubscriptionEnabledFlow(subId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,36 +67,3 @@ fun Context.subscriptionsChangedFlow() = callbackFlow {
|
|||||||
|
|
||||||
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
|
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
|
||||||
}.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default)
|
}.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") }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@@ -30,8 +30,11 @@ import androidx.compose.ui.test.onNodeWithText
|
|||||||
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.R
|
import com.android.settings.R
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
import com.android.settingslib.spa.testutils.waitUntilExists
|
import com.android.settingslib.spa.testutils.waitUntilExists
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -58,7 +61,6 @@ class MobileNetworkSwitchControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val mockSubscriptionRepository = mock<SubscriptionRepository> {
|
private val mockSubscriptionRepository = mock<SubscriptionRepository> {
|
||||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(SubInfo)
|
|
||||||
on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false)
|
on { isSubscriptionEnabledFlow(SUB_ID) } doReturn flowOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +70,16 @@ class MobileNetworkSwitchControllerTest {
|
|||||||
subscriptionRepository = mockSubscriptionRepository,
|
subscriptionRepository = mockSubscriptionRepository,
|
||||||
).apply { init(SUB_ID) }
|
).apply { init(SUB_ID) }
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(SubInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
SubscriptionUtil.setAvailableSubscriptionsForTesting(null)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isVisible_pSimAndCanDisablePhysicalSubscription_returnTrue() {
|
fun isVisible_pSimAndCanDisablePhysicalSubscription_returnTrue() {
|
||||||
val pSimSubInfo = SubscriptionInfo.Builder().apply {
|
val pSimSubInfo = SubscriptionInfo.Builder().apply {
|
||||||
@@ -77,9 +89,7 @@ class MobileNetworkSwitchControllerTest {
|
|||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
on { canDisablePhysicalSubscription() } doReturn true
|
on { canDisablePhysicalSubscription() } doReturn true
|
||||||
}
|
}
|
||||||
mockSubscriptionRepository.stub {
|
SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(pSimSubInfo))
|
||||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(pSimSubInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
setContent()
|
setContent()
|
||||||
|
|
||||||
@@ -96,9 +106,7 @@ class MobileNetworkSwitchControllerTest {
|
|||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
on { canDisablePhysicalSubscription() } doReturn false
|
on { canDisablePhysicalSubscription() } doReturn false
|
||||||
}
|
}
|
||||||
mockSubscriptionRepository.stub {
|
SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(pSimSubInfo))
|
||||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(pSimSubInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
setContent()
|
setContent()
|
||||||
|
|
||||||
@@ -112,9 +120,7 @@ class MobileNetworkSwitchControllerTest {
|
|||||||
setId(SUB_ID)
|
setId(SUB_ID)
|
||||||
setEmbedded(true)
|
setEmbedded(true)
|
||||||
}.build()
|
}.build()
|
||||||
mockSubscriptionRepository.stub {
|
SubscriptionUtil.setAvailableSubscriptionsForTesting(listOf(eSimSubInfo))
|
||||||
on { getSelectableSubscriptionInfoList() } doReturn listOf(eSimSubInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
setContent()
|
setContent()
|
||||||
|
|
||||||
|
@@ -24,7 +24,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||||
import com.android.settingslib.spa.testutils.toListWithTimeout
|
import com.android.settingslib.spa.testutils.toListWithTimeout
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import java.util.UUID
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@@ -82,65 +81,6 @@ class SubscriptionRepositoryTest {
|
|||||||
assertThat(listDeferred.await()).hasSize(2)
|
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
|
@Test
|
||||||
fun phoneNumberFlow() = runBlocking {
|
fun phoneNumberFlow() = runBlocking {
|
||||||
mockSubscriptionManager.stub {
|
mockSubscriptionManager.stub {
|
||||||
@@ -158,9 +98,6 @@ class SubscriptionRepositoryTest {
|
|||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
const val SUB_ID_1 = 1
|
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 NUMBER_1 = "000000001"
|
||||||
const val MCC = "310"
|
const val MCC = "310"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user