1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.network; 18 19 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; 20 import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT; 21 22 import static com.android.internal.util.CollectionUtils.emptyIfNull; 23 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.os.ParcelUuid; 27 import android.telephony.PhoneNumberUtils; 28 import android.telephony.SubscriptionInfo; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.TelephonyManager; 31 import android.telephony.UiccCardInfo; 32 import android.telephony.UiccSlotInfo; 33 import android.text.TextUtils; 34 import android.util.Log; 35 36 import androidx.annotation.VisibleForTesting; 37 38 import com.android.internal.telephony.MccTable; 39 import com.android.settings.R; 40 import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity; 41 import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity; 42 import com.android.settingslib.DeviceInfoUtils; 43 44 import java.util.ArrayList; 45 import java.util.Collections; 46 import java.util.HashMap; 47 import java.util.HashSet; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Set; 51 import java.util.function.Supplier; 52 import java.util.stream.Collectors; 53 import java.util.stream.Stream; 54 55 public class SubscriptionUtil { 56 private static final String TAG = "SubscriptionUtil"; 57 private static final String PROFILE_GENERIC_DISPLAY_NAME = "CARD"; 58 private static List<SubscriptionInfo> sAvailableResultsForTesting; 59 private static List<SubscriptionInfo> sActiveResultsForTesting; 60 61 @VisibleForTesting setAvailableSubscriptionsForTesting(List<SubscriptionInfo> results)62 public static void setAvailableSubscriptionsForTesting(List<SubscriptionInfo> results) { 63 sAvailableResultsForTesting = results; 64 } 65 66 @VisibleForTesting setActiveSubscriptionsForTesting(List<SubscriptionInfo> results)67 public static void setActiveSubscriptionsForTesting(List<SubscriptionInfo> results) { 68 sActiveResultsForTesting = results; 69 } 70 getActiveSubscriptions(SubscriptionManager manager)71 public static List<SubscriptionInfo> getActiveSubscriptions(SubscriptionManager manager) { 72 if (sActiveResultsForTesting != null) { 73 return sActiveResultsForTesting; 74 } 75 if (manager == null) { 76 return Collections.emptyList(); 77 } 78 final List<SubscriptionInfo> subscriptions = manager.getActiveSubscriptionInfoList(); 79 if (subscriptions == null) { 80 return new ArrayList<>(); 81 } 82 return subscriptions; 83 } 84 85 @VisibleForTesting isInactiveInsertedPSim(UiccSlotInfo slotInfo)86 static boolean isInactiveInsertedPSim(UiccSlotInfo slotInfo) { 87 if (slotInfo == null) { 88 return false; 89 } 90 return !slotInfo.getIsEuicc() && !slotInfo.getIsActive() && 91 slotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT; 92 } 93 94 /** 95 * Get all of the subscriptions which is available to display to the user. 96 * 97 * @param context {@code Context} 98 * @return list of {@code SubscriptionInfo} 99 */ getAvailableSubscriptions(Context context)100 public static List<SubscriptionInfo> getAvailableSubscriptions(Context context) { 101 if (sAvailableResultsForTesting != null) { 102 return sAvailableResultsForTesting; 103 } 104 return new ArrayList<>(emptyIfNull(getSelectableSubscriptionInfoList(context))); 105 } 106 107 /** 108 * Get subscription which is available to be displayed to the user 109 * per subscription id. 110 * 111 * @param context {@code Context} 112 * @param subscriptionManager The ProxySubscriptionManager for accessing subcription 113 * information 114 * @param subId The id of subscription to be retrieved 115 * @return {@code SubscriptionInfo} based on the given subscription id. Null of subscription 116 * is invalid or not allowed to be displayed to the user. 117 */ getAvailableSubscription(Context context, ProxySubscriptionManager subscriptionManager, int subId)118 public static SubscriptionInfo getAvailableSubscription(Context context, 119 ProxySubscriptionManager subscriptionManager, int subId) { 120 final SubscriptionInfo subInfo = subscriptionManager.getAccessibleSubscriptionInfo(subId); 121 if (subInfo == null) { 122 return null; 123 } 124 125 final ParcelUuid groupUuid = subInfo.getGroupUuid(); 126 127 if (groupUuid != null) { 128 if (isPrimarySubscriptionWithinSameUuid(getUiccSlotsInfo(context), groupUuid, 129 subscriptionManager.getAccessibleSubscriptionsInfo(), subId)) { 130 return subInfo; 131 } 132 return null; 133 } 134 135 return subInfo; 136 } 137 getUiccSlotsInfo(Context context)138 private static UiccSlotInfo [] getUiccSlotsInfo(Context context) { 139 final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class); 140 return telMgr.getUiccSlotsInfo(); 141 } 142 isPrimarySubscriptionWithinSameUuid(UiccSlotInfo[] slotsInfo, ParcelUuid groupUuid, List<SubscriptionInfo> subscriptions, int subId)143 private static boolean isPrimarySubscriptionWithinSameUuid(UiccSlotInfo[] slotsInfo, 144 ParcelUuid groupUuid, List<SubscriptionInfo> subscriptions, int subId) { 145 // only interested in subscriptions with this group UUID 146 final ArrayList<SubscriptionInfo> physicalSubInfoList = 147 new ArrayList<SubscriptionInfo>(); 148 final ArrayList<SubscriptionInfo> nonOpportunisticSubInfoList = 149 new ArrayList<SubscriptionInfo>(); 150 final ArrayList<SubscriptionInfo> activeSlotSubInfoList = 151 new ArrayList<SubscriptionInfo>(); 152 final ArrayList<SubscriptionInfo> inactiveSlotSubInfoList = 153 new ArrayList<SubscriptionInfo>(); 154 for (SubscriptionInfo subInfo : subscriptions) { 155 if (groupUuid.equals(subInfo.getGroupUuid())) { 156 if (!subInfo.isEmbedded()) { 157 physicalSubInfoList.add(subInfo); 158 } else { 159 if (!subInfo.isOpportunistic()) { 160 nonOpportunisticSubInfoList.add(subInfo); 161 } 162 if (subInfo.getSimSlotIndex() 163 != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 164 activeSlotSubInfoList.add(subInfo); 165 } else { 166 inactiveSlotSubInfoList.add(subInfo); 167 } 168 } 169 } 170 } 171 172 // find any physical SIM which is currently inserted within logical slot 173 // and which is our target subscription 174 if ((slotsInfo != null) && (physicalSubInfoList.size() > 0)) { 175 final SubscriptionInfo subInfo = searchForSubscriptionId(physicalSubInfoList, subId); 176 if (subInfo == null) { 177 return false; 178 } 179 // verify if subscription is inserted within slot 180 for (UiccSlotInfo slotInfo : slotsInfo) { 181 if ((slotInfo != null) && (!slotInfo.getIsEuicc()) 182 && (slotInfo.getLogicalSlotIdx() == subInfo.getSimSlotIndex())) { 183 return true; 184 } 185 } 186 return false; 187 } 188 189 // When all of the eSIM profiles are opprtunistic and no physical SIM, 190 // first opportunistic subscriptions with same group UUID can be primary. 191 if (nonOpportunisticSubInfoList.size() <= 0) { 192 if (physicalSubInfoList.size() > 0) { 193 return false; 194 } 195 if (activeSlotSubInfoList.size() > 0) { 196 return (activeSlotSubInfoList.get(0).getSubscriptionId() == subId); 197 } 198 return (inactiveSlotSubInfoList.get(0).getSubscriptionId() == subId); 199 } 200 201 // Allow non-opportunistic + active eSIM subscription as primary 202 int numberOfActiveNonOpportunisticSubs = 0; 203 boolean isTargetNonOpportunistic = false; 204 for (SubscriptionInfo subInfo : nonOpportunisticSubInfoList) { 205 final boolean isTargetSubInfo = (subInfo.getSubscriptionId() == subId); 206 if (subInfo.getSimSlotIndex() != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 207 if (isTargetSubInfo) { 208 return true; 209 } 210 numberOfActiveNonOpportunisticSubs++; 211 } else { 212 isTargetNonOpportunistic |= isTargetSubInfo; 213 } 214 } 215 if (numberOfActiveNonOpportunisticSubs > 0) { 216 return false; 217 } 218 return isTargetNonOpportunistic; 219 } 220 searchForSubscriptionId(List<SubscriptionInfo> subInfoList, int subscriptionId)221 private static SubscriptionInfo searchForSubscriptionId(List<SubscriptionInfo> subInfoList, 222 int subscriptionId) { 223 for (SubscriptionInfo subInfo : subInfoList) { 224 if (subInfo.getSubscriptionId() == subscriptionId) { 225 return subInfo; 226 } 227 } 228 return null; 229 } 230 231 /** 232 * Return a mapping of active subscription ids to display names. Each display name is 233 * guaranteed to be unique in the following manner: 234 * 1) If the original display name is not unique, the last four digits of the phone number 235 * will be appended. 236 * 2) If the phone number is not visible or the last four digits are shared with another 237 * subscription, the subscription id will be appended to the original display name. 238 * More details can be found at go/unique-sub-display-names. 239 * 240 * @return map of active subscription ids to display names. 241 */ 242 @VisibleForTesting getUniqueSubscriptionDisplayNames(Context context)243 public static Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) { 244 class DisplayInfo { 245 public SubscriptionInfo subscriptionInfo; 246 public CharSequence originalName; 247 public CharSequence uniqueName; 248 } 249 250 // Map of SubscriptionId to DisplayName 251 final Supplier<Stream<DisplayInfo>> originalInfos = 252 () -> getAvailableSubscriptions(context) 253 .stream() 254 .filter(i -> { 255 // Filter out null values. 256 return (i != null && i.getDisplayName() != null); 257 }) 258 .map(i -> { 259 DisplayInfo info = new DisplayInfo(); 260 info.subscriptionInfo = i; 261 String displayName = i.getDisplayName().toString(); 262 info.originalName = TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME) 263 ? context.getResources().getString(R.string.sim_card) 264 : displayName.trim(); 265 return info; 266 }); 267 268 // TODO(goldmanj) consider using a map of DisplayName to SubscriptionInfos. 269 // A Unique set of display names 270 Set<CharSequence> uniqueNames = new HashSet<>(); 271 // Return the set of duplicate names 272 final Set<CharSequence> duplicateOriginalNames = originalInfos.get() 273 .filter(info -> !uniqueNames.add(info.originalName)) 274 .map(info -> info.originalName) 275 .collect(Collectors.toSet()); 276 277 // If a display name is duplicate, append the final 4 digits of the phone number. 278 // Creates a mapping of Subscription id to original display name + phone number display name 279 final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> { 280 if (duplicateOriginalNames.contains(info.originalName)) { 281 // This may return null, if the user cannot view the phone number itself. 282 final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context, 283 info.subscriptionInfo); 284 String lastFourDigits = ""; 285 if (phoneNumber != null) { 286 lastFourDigits = (phoneNumber.length() > 4) 287 ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber; 288 } 289 290 if (TextUtils.isEmpty(lastFourDigits)) { 291 info.uniqueName = info.originalName; 292 } else { 293 info.uniqueName = info.originalName + " " + lastFourDigits; 294 } 295 296 } else { 297 info.uniqueName = info.originalName; 298 } 299 return info; 300 }); 301 302 // Check uniqueness a second time. 303 // We might not have had permission to view the phone numbers. 304 // There might also be multiple phone numbers whose last 4 digits the same. 305 uniqueNames.clear(); 306 final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get() 307 .filter(info -> !uniqueNames.add(info.uniqueName)) 308 .map(info -> info.uniqueName) 309 .collect(Collectors.toSet()); 310 311 return uniqueInfos.get().map(info -> { 312 if (duplicatePhoneNames.contains(info.uniqueName)) { 313 info.uniqueName = info.originalName + " " 314 + info.subscriptionInfo.getSubscriptionId(); 315 } 316 return info; 317 }).collect(Collectors.toMap( 318 info -> info.subscriptionInfo.getSubscriptionId(), 319 info -> info.uniqueName)); 320 } 321 322 /** 323 * Return the display name for a subscription id, which is guaranteed to be unique. 324 * The logic to create this name has the following order of operations: 325 * 1) If the original display name is not unique, the last four digits of the phone number 326 * will be appended. 327 * 2) If the phone number is not visible or the last four digits are shared with another 328 * subscription, the subscription id will be appended to the original display name. 329 * More details can be found at go/unique-sub-display-names. 330 * 331 * @return map of active subscription ids to display names. 332 */ 333 @VisibleForTesting 334 public static CharSequence getUniqueSubscriptionDisplayName( 335 Integer subscriptionId, Context context) { 336 final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context); 337 return displayNames.getOrDefault(subscriptionId, ""); 338 } 339 340 /** 341 * Return the display name for a subscription, which is guaranteed to be unique. 342 * The logic to create this name has the following order of operations: 343 * 1) If the original display name is not unique, the last four digits of the phone number 344 * will be appended. 345 * 2) If the phone number is not visible or the last four digits are shared with another 346 * subscription, the subscription id will be appended to the original display name. 347 * More details can be found at go/unique-sub-display-names. 348 * 349 * @return map of active subscription ids to display names. 350 */ 351 public static CharSequence getUniqueSubscriptionDisplayName( 352 SubscriptionInfo info, Context context) { 353 if (info == null) { 354 return ""; 355 } 356 return getUniqueSubscriptionDisplayName(info.getSubscriptionId(), context); 357 } 358 359 public static String getDisplayName(SubscriptionInfo info) { 360 final CharSequence name = info.getDisplayName(); 361 if (name != null) { 362 return name.toString(); 363 } 364 return ""; 365 } 366 367 /** 368 * Whether Settings should show a "Use SIM" toggle in pSIM detailed page. 369 */ 370 public static boolean showToggleForPhysicalSim(SubscriptionManager subMgr) { 371 return subMgr.canDisablePhysicalSubscription(); 372 } 373 374 /** 375 * Get phoneId or logical slot index for a subId if active, or INVALID_PHONE_INDEX if inactive. 376 */ 377 public static int getPhoneId(Context context, int subId) { 378 final SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class); 379 if (subManager == null) { 380 return INVALID_SIM_SLOT_INDEX; 381 } 382 final SubscriptionInfo info = subManager.getActiveSubscriptionInfo(subId); 383 if (info == null) { 384 return INVALID_SIM_SLOT_INDEX; 385 } 386 return info.getSimSlotIndex(); 387 } 388 389 /** 390 * Return a list of subscriptions that are available and visible to the user. 391 * 392 * @return list of user selectable subscriptions. 393 */ 394 public static List<SubscriptionInfo> getSelectableSubscriptionInfoList(Context context) { 395 SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class); 396 List<SubscriptionInfo> availableList = subManager.getAvailableSubscriptionInfoList(); 397 if (availableList == null) { 398 return null; 399 } else { 400 // Multiple subscriptions in a group should only have one representative. 401 // It should be the current active primary subscription if any, or any 402 // primary subscription. 403 List<SubscriptionInfo> selectableList = new ArrayList<>(); 404 Map<ParcelUuid, SubscriptionInfo> groupMap = new HashMap<>(); 405 406 for (SubscriptionInfo info : availableList) { 407 // Opportunistic subscriptions are considered invisible 408 // to users so they should never be returned. 409 if (!isSubscriptionVisible(subManager, context, info)) continue; 410 411 ParcelUuid groupUuid = info.getGroupUuid(); 412 if (groupUuid == null) { 413 // Doesn't belong to any group. Add in the list. 414 selectableList.add(info); 415 } else if (!groupMap.containsKey(groupUuid) 416 || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX 417 && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) { 418 // If it belongs to a group that has never been recorded or it's the current 419 // active subscription, add it in the list. 420 selectableList.remove(groupMap.get(groupUuid)); 421 selectableList.add(info); 422 groupMap.put(groupUuid, info); 423 } 424 425 } 426 return selectableList; 427 } 428 } 429 430 /** 431 * Starts a dialog activity to handle SIM enabling/disabling. 432 * @param context {@code Context} 433 * @param subId The id of subscription need to be enabled or disabled. 434 * @param enable Whether the subscription with {@code subId} should be enabled or disabled. 435 */ 436 public static void startToggleSubscriptionDialogActivity( 437 Context context, int subId, boolean enable) { 438 if (!SubscriptionManager.isUsableSubscriptionId(subId)) { 439 Log.i(TAG, "Unable to toggle subscription due to invalid subscription ID."); 440 return; 441 } 442 context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable)); 443 } 444 445 /** 446 * Starts a dialog activity to handle eSIM deletion. 447 * @param context {@code Context} 448 * @param subId The id of subscription need to be deleted. 449 */ 450 public static void startDeleteEuiccSubscriptionDialogActivity(Context context, int subId) { 451 if (!SubscriptionManager.isUsableSubscriptionId(subId)) { 452 Log.i(TAG, "Unable to delete subscription due to invalid subscription ID."); 453 return; 454 } 455 context.startActivity(DeleteEuiccSubscriptionDialogActivity.getIntent(context, subId)); 456 } 457 458 /** 459 * Finds and returns a subscription with a specific subscription ID. 460 * @param subscriptionManager The ProxySubscriptionManager for accessing subscription 461 * information 462 * @param subId The id of subscription to be returned 463 * @return the {@code SubscriptionInfo} whose ID is {@code subId}. It returns null if the 464 * {@code subId} is {@code SubscriptionManager.INVALID_SUBSCRIPTION_ID} or no such 465 * {@code SubscriptionInfo} is found. 466 */ 467 @Nullable 468 public static SubscriptionInfo getSubById(SubscriptionManager subscriptionManager, int subId) { 469 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 470 return null; 471 } 472 return subscriptionManager 473 .getAllSubscriptionInfoList() 474 .stream() 475 .filter(subInfo -> subInfo.getSubscriptionId() == subId) 476 .findFirst() 477 .get(); 478 } 479 480 /** 481 * Whether a subscription is visible to API caller. If it's a bundled opportunistic 482 * subscription, it should be hidden anywhere in Settings, dialer, status bar etc. 483 * Exception is if caller owns carrier privilege, in which case they will 484 * want to see their own hidden subscriptions. 485 * 486 * @param info the subscriptionInfo to check against. 487 * @return true if this subscription should be visible to the API caller. 488 */ 489 public static boolean isSubscriptionVisible( 490 SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) { 491 if (info == null) return false; 492 // If subscription is NOT grouped opportunistic subscription, it's visible. 493 if (info.getGroupUuid() == null || !info.isOpportunistic()) return true; 494 495 // If the caller is the carrier app and owns the subscription, it should be visible 496 // to the caller. 497 TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 498 .createForSubscriptionId(info.getSubscriptionId()); 499 boolean hasCarrierPrivilegePermission = telephonyManager.hasCarrierPrivileges() 500 || subscriptionManager.canManageSubscription(info); 501 return hasCarrierPrivilegePermission; 502 } 503 504 /** 505 * Finds all the available subscriptions having the same group uuid as {@code subscriptionInfo}. 506 * @param subscriptionManager The SubscriptionManager for accessing subscription information 507 * @param subId The id of subscription 508 * @return a list of {@code SubscriptionInfo} which have the same group UUID. 509 */ 510 public static List<SubscriptionInfo> findAllSubscriptionsInGroup( 511 SubscriptionManager subscriptionManager, int subId) { 512 513 SubscriptionInfo subscription = getSubById(subscriptionManager, subId); 514 if (subscription == null) { 515 return Collections.emptyList(); 516 } 517 ParcelUuid groupUuid = subscription.getGroupUuid(); 518 List<SubscriptionInfo> availableSubscriptions = 519 subscriptionManager.getAvailableSubscriptionInfoList(); 520 521 if (availableSubscriptions == null 522 || availableSubscriptions.isEmpty() 523 || groupUuid == null) { 524 return Collections.singletonList(subscription); 525 } 526 527 return availableSubscriptions 528 .stream() 529 .filter(sub -> sub.isEmbedded() && groupUuid.equals(sub.getGroupUuid())) 530 .collect(Collectors.toList()); 531 } 532 533 /** Returns the formatted phone number of a subscription. */ 534 @Nullable 535 public static String getFormattedPhoneNumber( 536 Context context, SubscriptionInfo subscriptionInfo) { 537 if (subscriptionInfo == null) { 538 Log.e(TAG, "Invalid subscription."); 539 return null; 540 } 541 542 TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); 543 String rawPhoneNumber = 544 telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId()); 545 String countryIso = MccTable.countryCodeForMcc(subscriptionInfo.getMccString()); 546 if (TextUtils.isEmpty(rawPhoneNumber)) { 547 return null; 548 } 549 return PhoneNumberUtils.formatNumber(rawPhoneNumber, countryIso); 550 } 551 552 /** 553 * Returns the subscription on a removable sim card. The device does not need to be on removable 554 * slot. 555 */ 556 @Nullable 557 public static SubscriptionInfo getFirstRemovableSubscription(Context context) { 558 TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); 559 SubscriptionManager subscriptionManager = 560 context.getSystemService(SubscriptionManager.class); 561 List<UiccCardInfo> cardInfos = telephonyManager.getUiccCardsInfo(); 562 if (cardInfos == null) { 563 Log.w(TAG, "UICC cards info list is empty."); 564 return null; 565 } 566 List<SubscriptionInfo> allSubscriptions = subscriptionManager.getAllSubscriptionInfoList(); 567 if (allSubscriptions == null) { 568 Log.w(TAG, "All subscription info list is empty."); 569 return null; 570 } 571 for (UiccCardInfo cardInfo : cardInfos) { 572 if (cardInfo == null) { 573 Log.w(TAG, "Got null card."); 574 continue; 575 } 576 if (!cardInfo.isRemovable() 577 || cardInfo.getCardId() == TelephonyManager.UNSUPPORTED_CARD_ID) { 578 Log.i(TAG, "Skip embedded card or invalid cardId on slot: " 579 + cardInfo.getSlotIndex()); 580 continue; 581 } 582 Log.i(TAG, "Target removable cardId :" + cardInfo.getCardId()); 583 for (SubscriptionInfo subInfo : allSubscriptions) { 584 // Match the removable card id with subscription card id. 585 if (cardInfo.getCardId() == subInfo.getCardId()) { 586 return subInfo; 587 } 588 } 589 } 590 return null; 591 } 592 593 public static CharSequence getDefaultSimConfig(Context context, int subId) { 594 boolean isDefaultCall = subId == getDefaultVoiceSubscriptionId(); 595 boolean isDefaultSms = subId == getDefaultSmsSubscriptionId(); 596 boolean isDefaultData = subId == getDefaultDataSubscriptionId(); 597 598 if (!isDefaultData && !isDefaultCall && !isDefaultSms) { 599 return null; 600 } 601 602 final StringBuilder defaultConfig = new StringBuilder(); 603 if (isDefaultData) { 604 defaultConfig.append( 605 getResForDefaultConfig(context, R.string.default_active_sim_mobile_data)) 606 .append(", "); 607 } 608 609 if (isDefaultCall) { 610 defaultConfig.append(getResForDefaultConfig(context, R.string.default_active_sim_calls)) 611 .append(", "); 612 } 613 614 if (isDefaultSms) { 615 defaultConfig.append(getResForDefaultConfig(context, R.string.default_active_sim_sms)) 616 .append(", "); 617 } 618 619 // Do not add ", " for the last config. 620 defaultConfig.setLength(defaultConfig.length() - 2); 621 622 final String summary = context.getResources().getString( 623 R.string.sim_category_default_active_sim, 624 defaultConfig); 625 626 return summary; 627 } 628 629 private static String getResForDefaultConfig(Context context, int resId) { 630 return context.getResources().getString(resId); 631 } 632 633 private static int getDefaultVoiceSubscriptionId() { 634 return SubscriptionManager.getDefaultVoiceSubscriptionId(); 635 } 636 637 private static int getDefaultSmsSubscriptionId() { 638 return SubscriptionManager.getDefaultSmsSubscriptionId(); 639 } 640 641 private static int getDefaultDataSubscriptionId() { 642 return SubscriptionManager.getDefaultDataSubscriptionId(); 643 } 644 } 645