Merge "Return mapping of subscription id to unique display name."

This commit is contained in:
Jeremy Goldman
2021-01-13 12:04:54 +00:00
committed by Android (Google) Code Review
2 changed files with 224 additions and 0 deletions

View File

@@ -28,19 +28,25 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.telephony.UiccSlotInfo; import android.telephony.UiccSlotInfo;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity; import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity; import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
import com.android.settingslib.DeviceInfoUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SubscriptionUtil { public class SubscriptionUtil {
private static final String TAG = "SubscriptionUtil"; private static final String TAG = "SubscriptionUtil";
@@ -214,6 +220,92 @@ public class SubscriptionUtil {
return null; return null;
} }
/**
* Return a mapping of active subscription ids to diaplay names. Each display name is
* guaranteed to be unique in the following manner:
* 1) If the original display name is not unique, the last four digits of the phone number
* will be appended.
* 2) If the phone number is not visible or the last four digits are shared with another
* subscription, the subscription id will be appended to the original display name.
* More details can be found at go/unique-sub-display-names.
*
* @return map of active subscription ids to diaplay names.
*/
@VisibleForTesting
public static Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
class DisplayInfo {
public SubscriptionInfo subscriptionInfo;
public CharSequence originalName;
public CharSequence uniqueName;
}
final SubscriptionManager subscriptionManager =
context.getSystemService(SubscriptionManager.class);
// Map of SubscriptionId to DisplayName
final Supplier<Stream<DisplayInfo>> originalInfos =
() -> getActiveSubscriptions(subscriptionManager)
.stream()
.map(i -> {
DisplayInfo info = new DisplayInfo();
info.subscriptionInfo = i;
info.originalName = i.getDisplayName();
return info;
});
// TODO(goldmanj) consider using a map of DisplayName to SubscriptionInfos.
// A Unique set of display names
Set<CharSequence> uniqueNames = new HashSet<>();
// Return the set of duplicate names
final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
.filter(info -> !uniqueNames.add(info.originalName))
.map(info -> info.originalName)
.collect(Collectors.toSet());
// If a display name is duplicate, append the final 4 digits of the phone number.
// Creates a mapping of Subscription id to original display name + phone number display name
final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
if (duplicateOriginalNames.contains(info.originalName)) {
// This may return null, if the user cannot view the phone number itself.
final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
info.subscriptionInfo);
String lastFourDigits = "";
if (phoneNumber != null) {
lastFourDigits = (phoneNumber.length() > 4)
? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
}
if (TextUtils.isEmpty(lastFourDigits)) {
info.uniqueName = info.originalName;
} else {
info.uniqueName = info.originalName + " " + lastFourDigits;
}
} else {
info.uniqueName = info.originalName;
}
return info;
});
// Check uniqueness a second time.
// We might not have had permission to view the phone numbers.
// There might also be multiple phone numbers whose last 4 digits the same.
uniqueNames.clear();
final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
.filter(info -> !uniqueNames.add(info.uniqueName))
.map(info -> info.uniqueName)
.collect(Collectors.toSet());
return uniqueInfos.get().map(info -> {
if (duplicatePhoneNames.contains(info.uniqueName)) {
info.uniqueName = info.originalName + " "
+ info.subscriptionInfo.getSubscriptionId();
}
return info;
}).collect(Collectors.toMap(
info -> info.subscriptionInfo.getSubscriptionId(),
info -> info.uniqueName));
}
public static String getDisplayName(SubscriptionInfo info) { public static String getDisplayName(SubscriptionInfo info) {
final CharSequence name = info.getDisplayName(); final CharSequence name = info.getDisplayName();
if (name != null) { if (name != null) {

View File

@@ -18,6 +18,7 @@ package com.android.settings.network;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -38,9 +39,15 @@ import org.mockito.MockitoAnnotations;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class SubscriptionUtilTest { public class SubscriptionUtilTest {
private static final int SUBID_1 = 1;
private static final int SUBID_2 = 2;
private static final int SUBID_3 = 3;
private static final CharSequence CARRIER_1 = "carrier1";
private static final CharSequence CARRIER_2 = "carrier2";
private Context mContext; private Context mContext;
@Mock @Mock
@@ -125,6 +132,131 @@ public class SubscriptionUtilTest {
assertThat(subs).hasSize(2); assertThat(subs).hasSize(2);
} }
@Test
public void getUniqueDisplayNames_uniqueCarriers_originalUsed() {
// Each subscription's default display name is unique.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
when(info2.getSubscriptionId()).thenReturn(SUBID_2);
when(info1.getDisplayName()).thenReturn(CARRIER_1);
when(info2.getDisplayName()).thenReturn(CARRIER_2);
when(mSubMgr.getActiveSubscriptionInfoList()).thenReturn(
Arrays.asList(info1, info2));
// Each subscription has a unique last 4 digits of the phone number.
TelephonyManager sub1Telmgr = mock(TelephonyManager.class);
TelephonyManager sub2Telmgr = mock(TelephonyManager.class);
when(sub1Telmgr.getLine1Number()).thenReturn("1112223333");
when(sub2Telmgr.getLine1Number()).thenReturn("2223334444");
when(mTelMgr.createForSubscriptionId(SUBID_1)).thenReturn(sub1Telmgr);
when(mTelMgr.createForSubscriptionId(SUBID_2)).thenReturn(sub2Telmgr);
final Map<Integer, CharSequence> idNames =
SubscriptionUtil.getUniqueSubscriptionDisplayNames(mContext);
assertThat(idNames).isNotNull();
assertThat(idNames).hasSize(2);
assertEquals(CARRIER_1, idNames.get(SUBID_1));
assertEquals(CARRIER_2, idNames.get(SUBID_2));
}
@Test
public void getUniqueDisplayNames_identicalCarriers_fourDigitsUsed() {
// Both subscriptoins have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
when(info2.getSubscriptionId()).thenReturn(SUBID_2);
when(info1.getDisplayName()).thenReturn(CARRIER_1);
when(info2.getDisplayName()).thenReturn(CARRIER_1);
when(mSubMgr.getActiveSubscriptionInfoList()).thenReturn(
Arrays.asList(info1, info2));
// Each subscription has a unique last 4 digits of the phone number.
TelephonyManager sub1Telmgr = mock(TelephonyManager.class);
TelephonyManager sub2Telmgr = mock(TelephonyManager.class);
when(sub1Telmgr.getLine1Number()).thenReturn("1112223333");
when(sub2Telmgr.getLine1Number()).thenReturn("2223334444");
when(mTelMgr.createForSubscriptionId(SUBID_1)).thenReturn(sub1Telmgr);
when(mTelMgr.createForSubscriptionId(SUBID_2)).thenReturn(sub2Telmgr);
final Map<Integer, CharSequence> idNames =
SubscriptionUtil.getUniqueSubscriptionDisplayNames(mContext);
assertThat(idNames).isNotNull();
assertThat(idNames).hasSize(2);
assertEquals(CARRIER_1 + " 3333", idNames.get(SUBID_1));
assertEquals(CARRIER_1 + " 4444", idNames.get(SUBID_2));
}
@Test
public void getUniqueDisplayNames_phoneNumberBlocked_subscriptoinIdFallback() {
// Both subscriptoins have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
when(info2.getSubscriptionId()).thenReturn(SUBID_2);
when(info1.getDisplayName()).thenReturn(CARRIER_1);
when(info2.getDisplayName()).thenReturn(CARRIER_1);
when(mSubMgr.getActiveSubscriptionInfoList()).thenReturn(
Arrays.asList(info1, info2));
// The subscriptions' phone numbers cannot be revealed to the user.
TelephonyManager sub1Telmgr = mock(TelephonyManager.class);
TelephonyManager sub2Telmgr = mock(TelephonyManager.class);
when(sub1Telmgr.getLine1Number()).thenReturn("");
when(sub2Telmgr.getLine1Number()).thenReturn("");
when(mTelMgr.createForSubscriptionId(SUBID_1)).thenReturn(sub1Telmgr);
when(mTelMgr.createForSubscriptionId(SUBID_2)).thenReturn(sub2Telmgr);
final Map<Integer, CharSequence> idNames =
SubscriptionUtil.getUniqueSubscriptionDisplayNames(mContext);
assertThat(idNames).isNotNull();
assertThat(idNames).hasSize(2);
assertEquals(CARRIER_1 + " 1", idNames.get(SUBID_1));
assertEquals(CARRIER_1 + " 2", idNames.get(SUBID_2));
}
@Test
public void getUniqueDisplayNames_phoneNumberIdentical_subscriptoinIdFallback() {
// TODO have three here from the same carrier
// Both subscriptoins have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
final SubscriptionInfo info3 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
when(info2.getSubscriptionId()).thenReturn(SUBID_2);
when(info3.getSubscriptionId()).thenReturn(SUBID_3);
when(info1.getDisplayName()).thenReturn(CARRIER_1);
when(info2.getDisplayName()).thenReturn(CARRIER_1);
when(info3.getDisplayName()).thenReturn(CARRIER_1);
when(mSubMgr.getActiveSubscriptionInfoList()).thenReturn(
Arrays.asList(info1, info2, info3));
// Subscription 1 has a unique phone number, but subscriptions 2 and 3 share the same
// last four digits.
TelephonyManager sub1Telmgr = mock(TelephonyManager.class);
TelephonyManager sub2Telmgr = mock(TelephonyManager.class);
TelephonyManager sub3Telmgr = mock(TelephonyManager.class);
when(sub1Telmgr.getLine1Number()).thenReturn("1112223333");
when(sub2Telmgr.getLine1Number()).thenReturn("2223334444");
when(sub3Telmgr.getLine1Number()).thenReturn("5556664444");
when(mTelMgr.createForSubscriptionId(SUBID_1)).thenReturn(sub1Telmgr);
when(mTelMgr.createForSubscriptionId(SUBID_2)).thenReturn(sub2Telmgr);
when(mTelMgr.createForSubscriptionId(SUBID_3)).thenReturn(sub3Telmgr);
final Map<Integer, CharSequence> idNames =
SubscriptionUtil.getUniqueSubscriptionDisplayNames(mContext);
assertThat(idNames).isNotNull();
assertThat(idNames).hasSize(3);
assertEquals(CARRIER_1 + " 3333", idNames.get(SUBID_1));
assertEquals(CARRIER_1 + " 2", idNames.get(SUBID_2));
assertEquals(CARRIER_1 + " 3", idNames.get(SUBID_3));
}
@Test @Test
public void isInactiveInsertedPSim_nullSubInfo_doesNotCrash() { public void isInactiveInsertedPSim_nullSubInfo_doesNotCrash() {
assertThat(SubscriptionUtil.isInactiveInsertedPSim(null)).isFalse(); assertThat(SubscriptionUtil.isInactiveInsertedPSim(null)).isFalse();