[Settings] Hide subscriptions not existed within device
For non-active subscriptions, the one inserted in slot
but turned off need to be visible to the user. However,
the one un-plugged need to be invisble.
Since SubscriptionUtil#getSelectableSubscriptionInfoList() didn't cover all the cases required. Create this one to fit into the criteria required here.
Note: subscriptions with same group UUID will be displayed seperately.
Bug: 191228344
Test: local
Change-Id: Ia68c23b007164b7520456cb6c7427ca142558b59
(cherry picked from commit 75f1450bbf9e40f2c465a2db9d7391736f91973c)
(cherry picked from commit 0726f263a89e64a13a16679c313fccfc17bf91fd)
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 48ff591..41760de 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -486,7 +486,7 @@
* @param info the subscriptionInfo to check against.
* @return true if this subscription should be visible to the API caller.
*/
- private static boolean isSubscriptionVisible(
+ public static boolean isSubscriptionVisible(
SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) {
if (info == null) return false;
// If subscription is NOT grouped opportunistic subscription, it's visible.
diff --git a/src/com/android/settings/network/helper/QueryEsimCardId.java b/src/com/android/settings/network/helper/QueryEsimCardId.java
new file mode 100644
index 0000000..dc29c47
--- /dev/null
+++ b/src/com/android/settings/network/helper/QueryEsimCardId.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.helper;
+
+import android.telephony.TelephonyManager;
+import android.telephony.UiccCardInfo;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+/**
+ * This is a Callable class which queries valid card ID for eSIM
+ */
+public class QueryEsimCardId implements Callable<AtomicIntegerArray> {
+ private static final String TAG = "QueryEsimCardId";
+
+ private TelephonyManager mTelephonyManager;
+
+ /**
+ * Constructor of class
+ * @param TelephonyManager
+ */
+ public QueryEsimCardId(TelephonyManager telephonyManager) {
+ mTelephonyManager = telephonyManager;
+ }
+
+ /**
+ * Implementation of Callable
+ * @return card ID(s) in AtomicIntegerArray
+ */
+ public AtomicIntegerArray call() {
+ List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
+ if (cardInfos == null) {
+ return new AtomicIntegerArray(0);
+ }
+ return new AtomicIntegerArray(cardInfos.stream()
+ .filter(Objects::nonNull)
+ .filter(cardInfo -> (!cardInfo.isRemovable()
+ && (cardInfo.getCardId() != TelephonyManager.UNSUPPORTED_CARD_ID)))
+ .mapToInt(UiccCardInfo::getCardId)
+ .toArray());
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/helper/QuerySimSlotIndex.java b/src/com/android/settings/network/helper/QuerySimSlotIndex.java
new file mode 100644
index 0000000..b70a148
--- /dev/null
+++ b/src/com/android/settings/network/helper/QuerySimSlotIndex.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.helper;
+
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotInfo;
+
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+/**
+ * This is a Callable class which query slot index within device
+ */
+public class QuerySimSlotIndex implements Callable<AtomicIntegerArray> {
+ private static final String TAG = "QuerySimSlotIndex";
+
+ private TelephonyManager mTelephonyManager;
+ private boolean mDisabledSlotsIncluded;
+ private boolean mOnlySlotWithSim;
+
+ /**
+ * Constructor of class
+ * @param TelephonyManager
+ * @param disabledSlotsIncluded query both active and inactive slots when true,
+ * only query active slot when false.
+ * @param onlySlotWithSim query slot index with SIM available when true,
+ * include absent ones when false.
+ */
+ public QuerySimSlotIndex(TelephonyManager telephonyManager,
+ boolean disabledSlotsIncluded, boolean onlySlotWithSim) {
+ mTelephonyManager = telephonyManager;
+ mDisabledSlotsIncluded = disabledSlotsIncluded;
+ mOnlySlotWithSim = onlySlotWithSim;
+ }
+
+ /**
+ * Implementation of Callable
+ * @return slot index in AtomicIntegerArray
+ */
+ public AtomicIntegerArray call() {
+ UiccSlotInfo [] slotInfo = mTelephonyManager.getUiccSlotsInfo();
+ if (slotInfo == null) {
+ return new AtomicIntegerArray(0);
+ }
+ int slotIndexFilter = mOnlySlotWithSim ? 0 : SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ return new AtomicIntegerArray(Arrays.stream(slotInfo)
+ .filter(slot -> filterSlot(slot))
+ .mapToInt(slot -> mapToSlotIndex(slot))
+ .filter(slotIndex -> (slotIndex >= slotIndexFilter))
+ .toArray());
+ }
+
+ protected boolean filterSlot(UiccSlotInfo slotInfo) {
+ if (mDisabledSlotsIncluded) {
+ return true;
+ }
+ if (slotInfo == null) {
+ return false;
+ }
+ return slotInfo.getIsActive();
+ }
+
+ protected int mapToSlotIndex(UiccSlotInfo slotInfo) {
+ if (slotInfo == null) {
+ return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ }
+ if (slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_ABSENT) {
+ return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ }
+ return slotInfo.getLogicalSlotIdx();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/helper/SelectableSubscriptions.java b/src/com/android/settings/network/helper/SelectableSubscriptions.java
new file mode 100644
index 0000000..436e84c
--- /dev/null
+++ b/src/com/android/settings/network/helper/SelectableSubscriptions.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.helper;
+
+import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.annotation.Keep;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.network.helper.SubscriptionAnnotation;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * This is a Callable class to query user selectable subscription list.
+ *
+ * Here's example of creating a Callable for retrieving a list of SubscriptionAnnotation
+ * for active Subscriptions:
+ *
+ * List<SubscriptionAnnotation> result = (new SelectableSubscriptions(context, false)).call();
+ *
+ * Another example for retrieving a list of SubscriptionAnnotation for all subscriptions
+ * accessible in another thread.
+ *
+ * List<SubscriptionAnnotation> result = ExecutorService.submit(
+ * new SelectableSubscriptions(context, true)).get()
+ */
+public class SelectableSubscriptions implements Callable<List<SubscriptionAnnotation>> {
+ private static final String TAG = "SelectableSubscriptions";
+
+ private Context mContext;
+ private Supplier<List<SubscriptionInfo>> mSubscriptions;
+ private Predicate<SubscriptionAnnotation> mFilter;
+ private Function<List<SubscriptionAnnotation>, List<SubscriptionAnnotation>> mFinisher;
+
+ /**
+ * Constructor of class
+ * @param context
+ * @param disabledSlotsIncluded query both active and inactive slots when true,
+ * only query active slot when false.
+ */
+ public SelectableSubscriptions(Context context, boolean disabledSlotsIncluded) {
+ mContext = context;
+ mSubscriptions = disabledSlotsIncluded ? (() -> getAvailableSubInfoList(context)) :
+ (() -> getActiveSubInfoList(context));
+ mFilter = disabledSlotsIncluded ? (subAnno -> subAnno.isExisted()) :
+ (subAnno -> subAnno.isActive());
+ mFinisher = annoList -> annoList;
+ }
+
+ /**
+ * Add UnaryOperator to be applied to the final result.
+ * @param finisher a function to be applied to the final result.
+ */
+ public SelectableSubscriptions addFinisher(
+ UnaryOperator<List<SubscriptionAnnotation>> finisher) {
+ mFinisher = mFinisher.andThen(finisher);
+ return this;
+ }
+
+ /**
+ * Implementation of Callable
+ * @return a list of SubscriptionAnnotation which is user selectable
+ */
+ public List<SubscriptionAnnotation> call() {
+ TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class);
+
+ try {
+ // query in background thread
+ Future<AtomicIntegerArray> eSimCardId =
+ ThreadUtils.postOnBackgroundThread(new QueryEsimCardId(telMgr));
+
+ // query in background thread
+ Future<AtomicIntegerArray> simSlotIndex =
+ ThreadUtils.postOnBackgroundThread(
+ new QuerySimSlotIndex(telMgr, true, true));
+
+ // query in background thread
+ Future<AtomicIntegerArray> activeSimSlotIndex =
+ ThreadUtils.postOnBackgroundThread(
+ new QuerySimSlotIndex(telMgr, false, true));
+
+ List<SubscriptionInfo> subInfoList = mSubscriptions.get();
+
+ // wait for result from background thread
+ List<Integer> eSimCardIdList = atomicToList(eSimCardId.get());
+ List<Integer> simSlotIndexList = atomicToList(simSlotIndex.get());
+ List<Integer> activeSimSlotIndexList = atomicToList(activeSimSlotIndex.get());
+
+ // build a list of SubscriptionAnnotation
+ return IntStream.range(0, subInfoList.size())
+ .mapToObj(subInfoIndex ->
+ new SubscriptionAnnotation.Builder(subInfoList, subInfoIndex))
+ .map(annoBdr -> annoBdr.build(mContext,
+ eSimCardIdList, simSlotIndexList, activeSimSlotIndexList))
+ .filter(mFilter)
+ .collect(Collectors.collectingAndThen(Collectors.toList(), mFinisher));
+ } catch (Exception exception) {
+ Log.w(TAG, "Fail to request subIdList", exception);
+ }
+ return Collections.emptyList();
+ }
+
+ protected List<SubscriptionInfo> getSubInfoList(Context context,
+ Function<SubscriptionManager, List<SubscriptionInfo>> convertor) {
+ SubscriptionManager subManager = getSubscriptionManager(context);
+ return (subManager == null) ? Collections.emptyList() : convertor.apply(subManager);
+ }
+
+ protected SubscriptionManager getSubscriptionManager(Context context) {
+ return context.getSystemService(SubscriptionManager.class);
+ }
+
+ protected List<SubscriptionInfo> getAvailableSubInfoList(Context context) {
+ return getSubInfoList(context, SubscriptionManager::getAvailableSubscriptionInfoList);
+ }
+
+ protected List<SubscriptionInfo> getActiveSubInfoList(Context context) {
+ return getSubInfoList(context, SubscriptionManager::getActiveSubscriptionInfoList);
+ }
+
+ @Keep
+ @VisibleForTesting
+ protected static List<Integer> atomicToList(AtomicIntegerArray atomicIntArray) {
+ if (atomicIntArray == null) {
+ return Collections.emptyList();
+ }
+ return IntStream.range(0, atomicIntArray.length())
+ .map(idx -> atomicIntArray.get(idx)).boxed()
+ .collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/helper/SubscriptionAnnotation.java b/src/com/android/settings/network/helper/SubscriptionAnnotation.java
new file mode 100644
index 0000000..fae5b9b
--- /dev/null
+++ b/src/com/android/settings/network/helper/SubscriptionAnnotation.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.helper;
+
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import androidx.annotation.Keep;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.network.SubscriptionUtil;
+
+import java.util.List;
+
+/**
+ * This is a class helps providing additional info required by UI
+ * based on SubscriptionInfo.
+ */
+public class SubscriptionAnnotation {
+ private static final String TAG = "SubscriptionAnnotation";
+
+ private SubscriptionInfo mSubInfo;
+ private int mOrderWithinList;
+ private int mType = TYPE_UNKNOWN;
+ private boolean mIsExisted;
+ private boolean mIsActive;
+ private boolean mIsAllowToDisplay;
+
+ public static final ParcelUuid EMPTY_UUID = ParcelUuid.fromString("0-0-0-0-0");
+
+ public static final int TYPE_UNKNOWN = 0x0;
+ public static final int TYPE_PSIM = 0x1;
+ public static final int TYPE_ESIM = 0x2;
+
+ /**
+ * Builder class for SubscriptionAnnotation
+ */
+ public static class Builder {
+
+ private List<SubscriptionInfo> mSubInfoList;
+ private int mIndexWithinList;
+
+ /**
+ * Constructor of builder
+ * @param subInfoList list of subscription info
+ * @param indexWithinList target index within list provided
+ */
+ public Builder(List<SubscriptionInfo> subInfoList, int indexWithinList) {
+ mSubInfoList = subInfoList;
+ mIndexWithinList = indexWithinList;
+ }
+
+ public SubscriptionAnnotation build(Context context, List<Integer> eSimCardId,
+ List<Integer> simSlotIndex, List<Integer> activeSimSlotIndex) {
+ return new SubscriptionAnnotation(mSubInfoList, mIndexWithinList, context,
+ eSimCardId, simSlotIndex, activeSimSlotIndex);
+ }
+ }
+
+ /**
+ * Constructor of class
+ */
+ @Keep
+ @VisibleForTesting
+ protected SubscriptionAnnotation(List<SubscriptionInfo> subInfoList, int subInfoIndex,
+ Context context, List<Integer> eSimCardId,
+ List<Integer> simSlotIndex, List<Integer> activeSimSlotIndexList) {
+ if ((subInfoIndex < 0) || (subInfoIndex >= subInfoList.size())) {
+ return;
+ }
+ mSubInfo = subInfoList.get(subInfoIndex);
+ if (mSubInfo == null) {
+ return;
+ }
+
+ mOrderWithinList = subInfoIndex;
+ mType = mSubInfo.isEmbedded() ? TYPE_ESIM : TYPE_PSIM;
+ if (mType == TYPE_ESIM) {
+ int cardId = mSubInfo.getCardId();
+ mIsExisted = eSimCardId.contains(cardId);
+ if (mIsExisted) {
+ mIsActive = activeSimSlotIndexList.contains(mSubInfo.getSimSlotIndex());
+ mIsAllowToDisplay = isDisplayAllowed(context);
+ }
+ return;
+ }
+
+ mIsExisted = simSlotIndex.contains(mSubInfo.getSimSlotIndex());
+ mIsActive = activeSimSlotIndexList.contains(mSubInfo.getSimSlotIndex());
+ if (mIsExisted) {
+ mIsAllowToDisplay = isDisplayAllowed(context);
+ }
+ }
+
+ // the index provided during construction of Builder
+ @Keep
+ public int getOrderingInList() {
+ return mOrderWithinList;
+ }
+
+ // type of subscription
+ @Keep
+ public int getType() {
+ return mType;
+ }
+
+ // if a subscription is existed within device
+ @Keep
+ public boolean isExisted() {
+ return mIsExisted;
+ }
+
+ // if a subscription is currently ON
+ @Keep
+ public boolean isActive() {
+ return mIsActive;
+ }
+
+ // if display of subscription is allowed
+ @Keep
+ public boolean isDisplayAllowed() {
+ return mIsAllowToDisplay;
+ }
+
+ // the subscription ID
+ @Keep
+ public int getSubscriptionId() {
+ return (mSubInfo == null) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID :
+ mSubInfo.getSubscriptionId();
+ }
+
+ // the grouping UUID
+ @Keep
+ public ParcelUuid getGroupUuid() {
+ return (mSubInfo == null) ? null : mSubInfo.getGroupUuid();
+ }
+
+ // the SubscriptionInfo
+ @Keep
+ public SubscriptionInfo getSubInfo() {
+ return mSubInfo;
+ }
+
+ private boolean isDisplayAllowed(Context context) {
+ return SubscriptionUtil.isSubscriptionVisible(
+ context.getSystemService(SubscriptionManager.class), context, mSubInfo);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/helper/SubscriptionGrouping.java b/src/com/android/settings/network/helper/SubscriptionGrouping.java
new file mode 100644
index 0000000..cfb5ea9
--- /dev/null
+++ b/src/com/android/settings/network/helper/SubscriptionGrouping.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.helper;
+
+import android.os.ParcelUuid;
+
+import androidx.annotation.Keep;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.network.helper.SubscriptionAnnotation;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+
+/**
+ * A UnaryOperator for converting a list of SubscriptionAnnotation into
+ * another list of SubscriptionAnnotation based on group UUID.
+ * Only one SubscriptionAnnotation with entries with same (valid) group UUID would be kept.
+ *
+ * Here's an example when applying this operation as a finisher of SelectableSubscriptions:
+ *
+ * Callable<SubscriptionAnnotation> callable = (new SelectableSubscriptions(context, true))
+ * .addFinisher(new SubscriptionGrouping());
+ *
+ * List<SubscriptionAnnotation> result = ExecutorService.submit(callable).get()
+ */
+public class SubscriptionGrouping
+ implements UnaryOperator<List<SubscriptionAnnotation>> {
+
+ // implementation of UnaryOperator
+ public List<SubscriptionAnnotation> apply(List<SubscriptionAnnotation> listOfSubscriptions) {
+ // group by GUID
+ Map<ParcelUuid, List<SubscriptionAnnotation>> groupedSubInfoList =
+ listOfSubscriptions.stream()
+ .filter(Objects::nonNull)
+ .collect(Collectors.groupingBy(subAnno -> getGroupUuid(subAnno)));
+
+ // select best one from subscription(s) within the same group
+ groupedSubInfoList.replaceAll((uuid, annoList) -> {
+ if ((uuid == SubscriptionAnnotation.EMPTY_UUID) || (annoList.size() <= 1)) {
+ return annoList;
+ }
+ return Collections.singletonList(selectBestFromList(annoList));
+ });
+
+ // build a stream of subscriptions
+ return groupedSubInfoList.values()
+ .stream().flatMap(List::stream).collect(Collectors.toList());
+ }
+
+ @Keep
+ @VisibleForTesting
+ protected ParcelUuid getGroupUuid(SubscriptionAnnotation subAnno) {
+ ParcelUuid groupUuid = subAnno.getGroupUuid();
+ return (groupUuid == null) ? SubscriptionAnnotation.EMPTY_UUID : groupUuid;
+ }
+
+ protected SubscriptionAnnotation selectBestFromList(List<SubscriptionAnnotation> annoList) {
+ Comparator<SubscriptionAnnotation> annoSelector = (anno1, anno2) -> {
+ if (anno1.isDisplayAllowed() != anno2.isDisplayAllowed()) {
+ return anno1.isDisplayAllowed() ? -1 : 1;
+ }
+ if (anno1.isActive() != anno2.isActive()) {
+ return anno1.isActive() ? -1 : 1;
+ }
+ if (anno1.isExisted() != anno2.isExisted()) {
+ return anno1.isExisted() ? -1 : 1;
+ }
+ return 0;
+ };
+ annoSelector = annoSelector
+ // eSIM in front of pSIM
+ .thenComparingInt(anno -> -anno.getType())
+ // subscription ID in reverse order
+ .thenComparingInt(anno -> -anno.getSubscriptionId());
+ return annoList.stream().sorted(annoSelector).findFirst().orElse(null);
+ }
+}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkActivity.java b/src/com/android/settings/network/telephony/MobileNetworkActivity.java
index f2be37f..5016460 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkActivity.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkActivity.java
@@ -19,6 +19,7 @@
import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
import android.app.ActionBar;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserManager;
@@ -43,6 +44,8 @@
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.network.ProxySubscriptionManager;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.helper.SelectableSubscriptions;
+import com.android.settings.network.helper.SubscriptionAnnotation;
import java.util.List;
@@ -244,15 +247,21 @@
*/
@VisibleForTesting
SubscriptionInfo getSubscription() {
+ List<SubscriptionAnnotation> subList =
+ (new SelectableSubscriptions(this, true)).call();
+ SubscriptionAnnotation currentSubInfo = null;
if (mCurSubscriptionId != SUB_ID_NULL) {
- return getSubscriptionForSubId(mCurSubscriptionId);
+ currentSubInfo = subList.stream()
+ .filter(SubscriptionAnnotation::isDisplayAllowed)
+ .filter(subAnno -> (subAnno.getSubscriptionId() == mCurSubscriptionId))
+ .findFirst().orElse(null);
}
- final List<SubscriptionInfo> subInfos = getProxySubscriptionManager()
- .getActiveSubscriptionsInfo();
- if (CollectionUtils.isEmpty(subInfos)) {
- return null;
+ if (currentSubInfo == null) {
+ currentSubInfo = subList.stream()
+ .filter(SubscriptionAnnotation::isDisplayAllowed)
+ .findFirst().orElse(null);
}
- return subInfos.get(0);
+ return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo();
}
@VisibleForTesting
diff --git a/tests/unit/src/com/android/settings/network/helper/SelectableSubscriptionsTest.java b/tests/unit/src/com/android/settings/network/helper/SelectableSubscriptionsTest.java
new file mode 100644
index 0000000..04e9122
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/helper/SelectableSubscriptionsTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.helper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+@RunWith(AndroidJUnit4.class)
+public class SelectableSubscriptionsTest {
+
+ @Before
+ public void setUp() {
+ }
+
+ @Test
+ public void atomicToList_nullInput_getNoneNullEmptyList() {
+ List<Integer> result = SelectableSubscriptions.atomicToList(null);
+
+ assertThat(result.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void atomicToList_zeroLengthInput_getEmptyList() {
+ List<Integer> result = SelectableSubscriptions.atomicToList(new AtomicIntegerArray(0));
+
+ assertThat(result.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void atomicToList_subIdInArray_getList() {
+ AtomicIntegerArray array = new AtomicIntegerArray(3);
+ array.set(0, 3);
+ array.set(1, 7);
+ array.set(2, 4);
+
+ List<Integer> result = SelectableSubscriptions.atomicToList(array);
+
+ assertThat(result.size()).isEqualTo(3);
+ assertThat(result.get(0)).isEqualTo(3);
+ assertThat(result.get(1)).isEqualTo(7);
+ assertThat(result.get(2)).isEqualTo(4);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/helper/SubscriptionGroupingTest.java b/tests/unit/src/com/android/settings/network/helper/SubscriptionGroupingTest.java
new file mode 100644
index 0000000..97bdb74
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/helper/SubscriptionGroupingTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.network.helper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.ParcelUuid;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.network.helper.SubscriptionAnnotation;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SubscriptionGroupingTest {
+
+ private ParcelUuid mMockUuid;
+
+ private Context mContext;
+ private SubscriptionGrouping mTarget;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mMockUuid = ParcelUuid.fromString("1-1-1-1-1");
+ mTarget = spy(new SubscriptionGrouping());
+ }
+
+ @Test
+ public void apply_multipleEntriesWithSameGroupUuid_onlyOneLeft() {
+ SubscriptionAnnotation subAnno1 = new TestSubAnnotation(1,
+ SubscriptionAnnotation.TYPE_ESIM, true, true, mMockUuid);
+ SubscriptionAnnotation subAnno2 = new TestSubAnnotation(2,
+ SubscriptionAnnotation.TYPE_PSIM, true, true, mMockUuid);
+ SubscriptionAnnotation subAnno3 = new TestSubAnnotation(3,
+ SubscriptionAnnotation.TYPE_ESIM, true, true, mMockUuid);
+ doReturn(mMockUuid).when(mTarget).getGroupUuid(any());
+
+ List<SubscriptionAnnotation> result = mTarget
+ .apply(Arrays.asList(subAnno2, subAnno1, subAnno3));
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.get(0)).isEqualTo(subAnno3);
+ }
+
+ @Test
+ public void apply_multipleEntriesWithSameGroupUuid_disabledOneIsAvoided() {
+ SubscriptionAnnotation subAnno1 = new TestSubAnnotation(1,
+ SubscriptionAnnotation.TYPE_ESIM, true, true, mMockUuid);
+ SubscriptionAnnotation subAnno2 = new TestSubAnnotation(2,
+ SubscriptionAnnotation.TYPE_PSIM, true, true, mMockUuid);
+ SubscriptionAnnotation subAnno3 = new TestSubAnnotation(3,
+ SubscriptionAnnotation.TYPE_ESIM, false, false, mMockUuid);
+ doReturn(mMockUuid).when(mTarget).getGroupUuid(any());
+
+ List<SubscriptionAnnotation> result = mTarget
+ .apply(Arrays.asList(subAnno2, subAnno1, subAnno3));
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.get(0)).isEqualTo(subAnno1);
+ }
+
+ private class TestSubAnnotation extends SubscriptionAnnotation {
+ private int mSubId;
+ private int mSimType;
+ private boolean mIsActive;
+ private boolean mIsDisplayAllowed;
+ private ParcelUuid mUuid;
+
+ private TestSubAnnotation(int subId, int simType,
+ boolean isActive, boolean isDisplayAllowed, ParcelUuid guuid) {
+ super(null, -1, null, null, null, null);
+ mSubId = subId;
+ mSimType = simType;
+ mIsActive = isActive;
+ mIsDisplayAllowed = isDisplayAllowed;
+ mUuid = guuid;
+ }
+
+ @Override
+ public int getSubscriptionId() {
+ return mSubId;
+ }
+
+ @Override
+ public int getType() {
+ return mSimType;
+ }
+
+ @Override
+ public boolean isExisted() {
+ return true;
+ }
+
+ @Override
+ public boolean isActive() {
+ return mIsActive;
+ }
+
+ @Override
+ public boolean isDisplayAllowed() {
+ return mIsDisplayAllowed;
+ }
+
+ @Override
+ public ParcelUuid getGroupUuid() {
+ return mUuid;
+ }
+ }
+}