diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java index 35950f70957..e61cc36d37e 100644 --- a/src/com/android/settings/network/SubscriptionUtil.java +++ b/src/com/android/settings/network/SubscriptionUtil.java @@ -31,7 +31,9 @@ import android.telephony.UiccSlotInfo; import androidx.annotation.VisibleForTesting; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class SubscriptionUtil { private static final String TAG = "SubscriptionUtil"; @@ -78,12 +80,7 @@ public class SubscriptionUtil { if (sAvailableResultsForTesting != null) { return sAvailableResultsForTesting; } - final SubscriptionManager subMgr = context.getSystemService(SubscriptionManager.class); - - final List subscriptions = - new ArrayList<>(emptyIfNull(subMgr.getSelectableSubscriptionInfoList())); - - return subscriptions; + return new ArrayList<>(emptyIfNull(getSelectableSubscriptionInfoList(context))); } /** @@ -239,4 +236,70 @@ public class SubscriptionUtil { } return info.getSimSlotIndex(); } + + /** + * Return a list of subscriptions that are available and visible to the user. + * + * @return list of user selectable subscriptions. + */ + public static List getSelectableSubscriptionInfoList(Context 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); + } + + } + return selectableList; + } + } + + + /** + * Whether a subscription is visible to API caller. If it's a bundled opportunistic + * subscription, it should be hidden anywhere in Settings, dialer, status bar etc. + * Exception is if caller owns carrier privilege, in which case they will + * want to see their own hidden subscriptions. + * + * @param info the subscriptionInfo to check against. + * @return true if this subscription should be visible to the API caller. + */ + private static boolean isSubscriptionVisible( + SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) { + if (info == null) return false; + // If subscription is NOT grouped opportunistic subscription, it's visible. + if (info.getGroupUuid() == null || !info.isOpportunistic()) return true; + + // If the caller is the carrier app and owns the subscription, it should be visible + // to the caller. + TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) + .createForSubscriptionId(info.getSubscriptionId()); + boolean hasCarrierPrivilegePermission = telephonyManager.hasCarrierPrivileges() + || subscriptionManager.canManageSubscription(info); + return hasCarrierPrivilegePermission; + } } diff --git a/src/com/android/settings/network/telephony/MobileDataSlice.java b/src/com/android/settings/network/telephony/MobileDataSlice.java index 40c747c1112..b76eb3814cf 100644 --- a/src/com/android/settings/network/telephony/MobileDataSlice.java +++ b/src/com/android/settings/network/telephony/MobileDataSlice.java @@ -39,6 +39,7 @@ import androidx.slice.builders.SliceAction; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.network.MobileDataContentObserver; +import com.android.settings.network.SubscriptionUtil; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SliceBackgroundWorker; @@ -177,7 +178,7 @@ public class MobileDataSlice implements CustomSliceable { */ private boolean isMobileDataAvailable() { final List subInfoList = - mSubscriptionManager.getSelectableSubscriptionInfoList(); + SubscriptionUtil.getSelectableSubscriptionInfoList(mContext); return !(subInfoList == null || subInfoList.isEmpty()); } diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java index 1aa7cd4cd2b..8b16a7be518 100644 --- a/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java +++ b/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java @@ -61,7 +61,7 @@ public class SubscriptionUtilTest { @Test public void getAvailableSubscriptions_nullInfoFromSubscriptionManager_nonNullResult() { - when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(null); + when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(null); final List subs = SubscriptionUtil.getAvailableSubscriptions(mContext); assertThat(subs).isNotNull(); assertThat(subs).isEmpty(); @@ -70,7 +70,7 @@ public class SubscriptionUtilTest { @Test public void getAvailableSubscriptions_oneSubscription_oneResult() { final SubscriptionInfo info = mock(SubscriptionInfo.class); - when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info)); + when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info)); final List subs = SubscriptionUtil.getAvailableSubscriptions(mContext); assertThat(subs).isNotNull(); assertThat(subs).hasSize(1); @@ -80,7 +80,7 @@ public class SubscriptionUtilTest { public void getAvailableSubscriptions_twoSubscriptions_twoResults() { final SubscriptionInfo info1 = mock(SubscriptionInfo.class); final SubscriptionInfo info2 = mock(SubscriptionInfo.class); - when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2)); + when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2)); final List subs = SubscriptionUtil.getAvailableSubscriptions(mContext); assertThat(subs).isNotNull(); assertThat(subs).hasSize(2); @@ -101,7 +101,7 @@ public class SubscriptionUtilTest { when(info3.getSubscriptionId()).thenReturn(333); when(info3.getSimSlotIndex()).thenReturn(0); - when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info1)); + when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info1)); when(mSubMgr.getAllSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2, info3)); final UiccSlotInfo info2slot = mock(UiccSlotInfo.class); diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java index 4cc8f36bb02..ba37c30389a 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java @@ -24,8 +24,8 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.provider.Settings; @@ -79,9 +79,8 @@ public class MobileDataSliceTest { doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID); doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt()); doReturn(SUB_ID).when(mSubscriptionInfo).getSubscriptionId(); - doReturn(new ArrayList<>(Arrays.asList(mSubscriptionInfo))) - .when(mSubscriptionManager).getSelectableSubscriptionInfoList(); - + when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn( + Arrays.asList(mSubscriptionInfo)); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); @@ -172,7 +171,7 @@ public class MobileDataSliceTest { @Test public void isMobileDataAvailable_noSubscriptions_slicePrimaryActionIsEmpty() { - doReturn(new ArrayList<>()).when(mSubscriptionManager).getSelectableSubscriptionInfoList(); + when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(new ArrayList<>()); final Slice mobileData = mMobileDataSlice.getSlice(); assertThat(mobileData).isNull(); @@ -180,7 +179,7 @@ public class MobileDataSliceTest { @Test public void isMobileDataAvailable_nullSubscriptions_slicePrimaryActionIsEmpty() { - doReturn(null).when(mSubscriptionManager).getSelectableSubscriptionInfoList(); + when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(null); final Slice mobileData = mMobileDataSlice.getSlice(); assertThat(mobileData).isNull();