1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; 4 import static android.os.Build.VERSION_CODES.M; 5 import static android.os.Build.VERSION_CODES.N; 6 import static android.os.Build.VERSION_CODES.O_MR1; 7 import static android.os.Build.VERSION_CODES.P; 8 import static android.os.Build.VERSION_CODES.Q; 9 import static android.os.Build.VERSION_CODES.R; 10 import static android.os.Build.VERSION_CODES.TIRAMISU; 11 12 import android.os.Build.VERSION; 13 import android.telephony.SubscriptionInfo; 14 import android.telephony.SubscriptionManager; 15 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 16 import com.google.common.collect.ImmutableList; 17 import java.util.ArrayList; 18 import java.util.Arrays; 19 import java.util.HashMap; 20 import java.util.HashSet; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.Set; 24 import java.util.concurrent.Executor; 25 import org.robolectric.annotation.HiddenApi; 26 import org.robolectric.annotation.Implementation; 27 import org.robolectric.annotation.Implements; 28 import org.robolectric.annotation.Resetter; 29 import org.robolectric.util.ReflectionHelpers; 30 31 @Implements(value = SubscriptionManager.class, minSdk = LOLLIPOP_MR1) 32 public class ShadowSubscriptionManager { 33 34 private boolean readPhoneStatePermission = true; 35 private boolean readPhoneNumbersPermission = true; 36 public static final int INVALID_PHONE_INDEX = 37 ReflectionHelpers.getStaticField(SubscriptionManager.class, "INVALID_PHONE_INDEX"); 38 39 private static int activeDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 40 private static int defaultSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 41 private static int defaultDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 42 private static int defaultSmsSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 43 private static int defaultVoiceSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 44 45 private final Map<Integer, String> phoneNumberMap = new HashMap<>(); 46 47 /** Returns value set with {@link #setActiveDataSubscriptionId(int)}. */ 48 @Implementation(minSdk = R) getActiveDataSubscriptionId()49 protected static int getActiveDataSubscriptionId() { 50 return activeDataSubscriptionId; 51 } 52 53 /** Returns value set with {@link #setDefaultSubscriptionId(int)}. */ 54 @Implementation(minSdk = N) getDefaultSubscriptionId()55 protected static int getDefaultSubscriptionId() { 56 return defaultSubscriptionId; 57 } 58 59 /** Returns value set with {@link #setDefaultDataSubscriptionId(int)}. */ 60 @Implementation(minSdk = N) getDefaultDataSubscriptionId()61 protected static int getDefaultDataSubscriptionId() { 62 return defaultDataSubscriptionId; 63 } 64 65 /** Returns value set with {@link #setDefaultSmsSubscriptionId(int)}. */ 66 @Implementation(minSdk = N) getDefaultSmsSubscriptionId()67 protected static int getDefaultSmsSubscriptionId() { 68 return defaultSmsSubscriptionId; 69 } 70 71 /** Returns value set with {@link #setDefaultVoiceSubscriptionId(int)}. */ 72 @Implementation(minSdk = N) getDefaultVoiceSubscriptionId()73 protected static int getDefaultVoiceSubscriptionId() { 74 return defaultVoiceSubscriptionId; 75 } 76 77 @Implementation(maxSdk = M) 78 @HiddenApi getDefaultSubId()79 protected static int getDefaultSubId() { 80 return defaultSubscriptionId; 81 } 82 83 @Implementation(maxSdk = M) 84 @HiddenApi getDefaultVoiceSubId()85 protected static int getDefaultVoiceSubId() { 86 return defaultVoiceSubscriptionId; 87 } 88 89 @Implementation(maxSdk = M) 90 @HiddenApi getDefaultSmsSubId()91 protected static int getDefaultSmsSubId() { 92 return defaultSmsSubscriptionId; 93 } 94 95 @Implementation(maxSdk = M) 96 @HiddenApi getDefaultDataSubId()97 protected static int getDefaultDataSubId() { 98 return defaultDataSubscriptionId; 99 } 100 101 /** Sets the value that will be returned by {@link #getActiveDataSubscriptionId()}. */ setActiveDataSubscriptionId(int activeDataSubscriptionId)102 public static void setActiveDataSubscriptionId(int activeDataSubscriptionId) { 103 ShadowSubscriptionManager.activeDataSubscriptionId = activeDataSubscriptionId; 104 } 105 106 /** Sets the value that will be returned by {@link #getDefaultSubscriptionId()}. */ setDefaultSubscriptionId(int defaultSubscriptionId)107 public static void setDefaultSubscriptionId(int defaultSubscriptionId) { 108 ShadowSubscriptionManager.defaultSubscriptionId = defaultSubscriptionId; 109 } 110 setDefaultDataSubscriptionId(int defaultDataSubscriptionId)111 public static void setDefaultDataSubscriptionId(int defaultDataSubscriptionId) { 112 ShadowSubscriptionManager.defaultDataSubscriptionId = defaultDataSubscriptionId; 113 } 114 setDefaultSmsSubscriptionId(int defaultSmsSubscriptionId)115 public static void setDefaultSmsSubscriptionId(int defaultSmsSubscriptionId) { 116 ShadowSubscriptionManager.defaultSmsSubscriptionId = defaultSmsSubscriptionId; 117 } 118 setDefaultVoiceSubscriptionId(int defaultVoiceSubscriptionId)119 public static void setDefaultVoiceSubscriptionId(int defaultVoiceSubscriptionId) { 120 ShadowSubscriptionManager.defaultVoiceSubscriptionId = defaultVoiceSubscriptionId; 121 } 122 123 /** 124 * Cache of phone IDs used by {@link getPhoneId}. Managed by {@link putPhoneId} and {@link 125 * removePhoneId}. 126 */ 127 private static Map<Integer, Integer> phoneIds = new HashMap<>(); 128 129 /** 130 * Cache of {@link SubscriptionInfo} used by {@link #getActiveSubscriptionInfoList}. Managed by 131 * {@link #setActiveSubscriptionInfoList}. 132 */ 133 private List<SubscriptionInfo> subscriptionList = new ArrayList<>(); 134 135 /** 136 * Cache of {@link SubscriptionInfo} used by {@link #getAccessibleSubscriptionInfoList}. Managed 137 * by {@link #setAccessibleSubscriptionInfos}. 138 */ 139 private List<SubscriptionInfo> accessibleSubscriptionList = new ArrayList<>(); 140 141 /** 142 * Cache of {@link SubscriptionInfo} used by {@link #getAvailableSubscriptionInfoList}. Managed by 143 * {@link #setAvailableSubscriptionInfos}. 144 */ 145 private List<SubscriptionInfo> availableSubscriptionList = new ArrayList<>(); 146 147 /** 148 * List of listeners to be notified if the list of {@link SubscriptionInfo} changes. Managed by 149 * {@link #addOnSubscriptionsChangedListener} and {@link removeOnSubscriptionsChangedListener}. 150 */ 151 private List<OnSubscriptionsChangedListener> listeners = new ArrayList<>(); 152 /** 153 * Cache of subscription ids used by {@link #isNetworkRoaming}. Managed by {@link 154 * #setNetworkRoamingStatus} and {@link #clearNetworkRoamingStatus}. 155 */ 156 private Set<Integer> roamingSimSubscriptionIds = new HashSet<>(); 157 158 /** 159 * Returns the active list of {@link SubscriptionInfo} that were set via {@link 160 * #setActiveSubscriptionInfoList}. 161 */ 162 @Implementation(minSdk = LOLLIPOP_MR1) getActiveSubscriptionInfoList()163 protected List<SubscriptionInfo> getActiveSubscriptionInfoList() { 164 checkReadPhoneStatePermission(); 165 return subscriptionList; 166 } 167 168 /** 169 * Returns the accessible list of {@link SubscriptionInfo} that were set via {@link 170 * #setAccessibleSubscriptionInfoList}. 171 */ 172 @Implementation(minSdk = O_MR1) getAccessibleSubscriptionInfoList()173 protected List<SubscriptionInfo> getAccessibleSubscriptionInfoList() { 174 return accessibleSubscriptionList; 175 } 176 177 /** 178 * Returns the available list of {@link SubscriptionInfo} that were set via {@link 179 * #setAvailableSubscriptionInfoList}. 180 */ 181 @Implementation(minSdk = O_MR1) getAvailableSubscriptionInfoList()182 protected List<SubscriptionInfo> getAvailableSubscriptionInfoList() { 183 return availableSubscriptionList; 184 } 185 186 /** 187 * Returns the size of the list of {@link SubscriptionInfo} that were set via {@link 188 * #setActiveSubscriptionInfoList}. If no list was set, returns 0. 189 */ 190 @Implementation(minSdk = LOLLIPOP_MR1) getActiveSubscriptionInfoCount()191 protected int getActiveSubscriptionInfoCount() { 192 checkReadPhoneStatePermission(); 193 return subscriptionList == null ? 0 : subscriptionList.size(); 194 } 195 196 /** 197 * Returns subscription that were set via {@link #setActiveSubscriptionInfoList} if it can find 198 * one with the specified id or null if none found. 199 * 200 * <p>An exception will be thrown if the READ_PHONE_STATE permission has not been granted. 201 */ 202 @Implementation(minSdk = LOLLIPOP_MR1) getActiveSubscriptionInfo(int subId)203 protected SubscriptionInfo getActiveSubscriptionInfo(int subId) { 204 checkReadPhoneStatePermission(); 205 if (subscriptionList == null) { 206 return null; 207 } 208 for (SubscriptionInfo info : subscriptionList) { 209 if (info.getSubscriptionId() == subId) { 210 return info; 211 } 212 } 213 return null; 214 } 215 216 /** 217 * @return the maximum number of active subscriptions that will be returned by {@link 218 * #getActiveSubscriptionInfoList} and the value returned by {@link 219 * #getActiveSubscriptionInfoCount}. 220 */ 221 @Implementation(minSdk = LOLLIPOP_MR1) getActiveSubscriptionInfoCountMax()222 protected int getActiveSubscriptionInfoCountMax() { 223 List<SubscriptionInfo> infoList = getActiveSubscriptionInfoList(); 224 225 if (infoList == null) { 226 return getActiveSubscriptionInfoCount(); 227 } 228 229 return Math.max(getActiveSubscriptionInfoList().size(), getActiveSubscriptionInfoCount()); 230 } 231 232 /** 233 * Returns subscription that were set via {@link #setActiveSubscriptionInfoList} if it can find 234 * one with the specified slot index or null if none found. 235 */ 236 @Implementation(minSdk = N) getActiveSubscriptionInfoForSimSlotIndex(int slotIndex)237 protected SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) { 238 checkReadPhoneStatePermission(); 239 if (subscriptionList == null) { 240 return null; 241 } 242 for (SubscriptionInfo info : subscriptionList) { 243 if (info.getSimSlotIndex() == slotIndex) { 244 return info; 245 } 246 } 247 return null; 248 } 249 250 /** 251 * Sets the active list of {@link SubscriptionInfo}. This call internally triggers {@link 252 * OnSubscriptionsChangedListener#onSubscriptionsChanged()} to all the listeners. 253 * 254 * <p>"Active" here means subscriptions which are currently mapped to a live modem stack in the 255 * device (i.e. the modem will attempt to use them to connect to nearby towers), and they are 256 * expected to have {@link SubscriptionInfo#getSimSlotIndex()} >= 0. A subscription being "active" 257 * in the device does NOT have any relation to a carrier's "activation" process for subscribers' 258 * SIMs. 259 * 260 * @param list - The subscription info list, can be null. 261 */ setActiveSubscriptionInfoList(List<SubscriptionInfo> list)262 public void setActiveSubscriptionInfoList(List<SubscriptionInfo> list) { 263 subscriptionList = list; 264 dispatchOnSubscriptionsChanged(); 265 } 266 267 /** 268 * Sets the accessible list of {@link SubscriptionInfo}. This call internally triggers {@link 269 * OnSubscriptionsChangedListener#onSubscriptionsChanged()} to all the listeners. 270 * 271 * <p>"Accessible" here means subscriptions which are eSIM ({@link SubscriptionInfo#isEmbedded}) 272 * and "owned" by the calling app, i.e. by {@link 273 * SubscriptionManager#canManageSubscription(SubscriptionInfo)}. They may be active, or 274 * installed-but-inactive. This is generally intended to be called by carrier apps that directly 275 * manage their own eSIM profiles on the device in concert with {@link 276 * android.telephony.EuiccManager}. 277 * 278 * @param list - The subscription info list, can be null. 279 */ setAccessibleSubscriptionInfoList(List<SubscriptionInfo> list)280 public void setAccessibleSubscriptionInfoList(List<SubscriptionInfo> list) { 281 accessibleSubscriptionList = list; 282 dispatchOnSubscriptionsChanged(); 283 } 284 285 /** 286 * Sets the available list of {@link SubscriptionInfo}. This call internally triggers {@link 287 * OnSubscriptionsChangedListener#onSubscriptionsChanged()} to all the listeners. 288 * 289 * <p>"Available" here means all active subscriptions (see {@link #setActiveSubscriptionInfoList}) 290 * combined with all installed-but-inactive eSIM subscriptions (similar to {@link 291 * #setAccessibleSubscriptionInfoList}, but not filtered to one particular app's "ownership" 292 * rights for subscriptions). This is generally intended to be called by system components such as 293 * the eSIM LPA or Settings that allow the user to manage all subscriptions on the device through 294 * some system-provided user interface. 295 * 296 * @param list - The subscription info list, can be null. 297 */ setAvailableSubscriptionInfoList(List<SubscriptionInfo> list)298 public void setAvailableSubscriptionInfoList(List<SubscriptionInfo> list) { 299 availableSubscriptionList = list; 300 dispatchOnSubscriptionsChanged(); 301 } 302 303 /** 304 * Sets the active list of {@link SubscriptionInfo}. This call internally triggers {@link 305 * OnSubscriptionsChangedListener#onSubscriptionsChanged()} to all the listeners. 306 */ setActiveSubscriptionInfos(SubscriptionInfo... infos)307 public void setActiveSubscriptionInfos(SubscriptionInfo... infos) { 308 if (infos == null) { 309 setActiveSubscriptionInfoList(ImmutableList.of()); 310 } else { 311 setActiveSubscriptionInfoList(Arrays.asList(infos)); 312 } 313 } 314 315 /** 316 * Sets the accessible list of {@link SubscriptionInfo}. This call internally triggers {@link 317 * OnSubscriptionsChangedListener#onSubscriptionsChanged()} to all the listeners. 318 */ setAccessibleSubscriptionInfos(SubscriptionInfo... infos)319 public void setAccessibleSubscriptionInfos(SubscriptionInfo... infos) { 320 if (infos == null) { 321 setAccessibleSubscriptionInfoList(ImmutableList.of()); 322 } else { 323 setAccessibleSubscriptionInfoList(Arrays.asList(infos)); 324 } 325 } 326 327 /** 328 * Sets the available list of {@link SubscriptionInfo}. This call internally triggers {@link 329 * OnSubscriptionsChangedListener#onSubscriptionsChanged()} to all the listeners. 330 */ setAvailableSubscriptionInfos(SubscriptionInfo... infos)331 public void setAvailableSubscriptionInfos(SubscriptionInfo... infos) { 332 if (infos == null) { 333 setAvailableSubscriptionInfoList(ImmutableList.of()); 334 } else { 335 setAvailableSubscriptionInfoList(Arrays.asList(infos)); 336 } 337 } 338 339 /** 340 * Adds a listener to a local list of listeners. Will be triggered by {@link 341 * #setActiveSubscriptionInfoList} when the local list of {@link SubscriptionInfo} is updated. 342 */ 343 @Implementation(minSdk = LOLLIPOP_MR1) addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener)344 protected void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { 345 listeners.add(listener); 346 listener.onSubscriptionsChanged(); 347 } 348 349 /** 350 * Adds a listener to a local list of listeners. Will be triggered by {@link 351 * #setActiveSubscriptionInfoList} when the local list of {@link SubscriptionInfo} is updated. 352 */ 353 @Implementation(minSdk = R) addOnSubscriptionsChangedListener( Executor executor, OnSubscriptionsChangedListener listener)354 protected void addOnSubscriptionsChangedListener( 355 Executor executor, OnSubscriptionsChangedListener listener) { 356 listeners.add(listener); 357 listener.onSubscriptionsChanged(); 358 } 359 360 /** 361 * Removes a listener from a local list of listeners. Will be triggered by {@link 362 * #setActiveSubscriptionInfoList} when the local list of {@link SubscriptionInfo} is updated. 363 */ 364 @Implementation(minSdk = LOLLIPOP_MR1) removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener)365 protected void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { 366 listeners.remove(listener); 367 } 368 369 /** 370 * Check if a listener exists in the {@link ShadowSubscriptionManager.listeners}. 371 * 372 * @param listener The listener to check. 373 * @return boolean True if the listener already added, otherwise false. 374 */ hasOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener)375 public boolean hasOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { 376 return listeners.contains(listener); 377 } 378 379 /** Returns subscription Ids that were set via {@link #setActiveSubscriptionInfoList}. */ 380 @Implementation(minSdk = LOLLIPOP_MR1) 381 @HiddenApi getActiveSubscriptionIdList()382 protected int[] getActiveSubscriptionIdList() { 383 final List<SubscriptionInfo> infos = getActiveSubscriptionInfoList(); 384 if (infos == null) { 385 return new int[0]; 386 } 387 int[] ids = new int[infos.size()]; 388 for (int i = 0; i < infos.size(); i++) { 389 ids[i] = infos.get(i).getSubscriptionId(); 390 } 391 return ids; 392 } 393 394 /** 395 * Notifies {@link OnSubscriptionsChangedListener} listeners that the list of {@link 396 * SubscriptionInfo} has been updated. 397 */ dispatchOnSubscriptionsChanged()398 private void dispatchOnSubscriptionsChanged() { 399 for (OnSubscriptionsChangedListener listener : listeners) { 400 listener.onSubscriptionsChanged(); 401 } 402 } 403 404 /** Clears the local cache of roaming subscription Ids used by {@link #isNetworkRoaming}. */ clearNetworkRoamingStatus()405 public void clearNetworkRoamingStatus() { 406 roamingSimSubscriptionIds.clear(); 407 } 408 409 /** 410 * If isNetworkRoaming is set, it will mark the provided sim subscriptionId as roaming in a local 411 * cache. If isNetworkRoaming is unset it will remove the subscriptionId from the local cache. The 412 * local cache is used to provide roaming status returned by {@link #isNetworkRoaming}. 413 */ setNetworkRoamingStatus(int simSubscriptionId, boolean isNetworkRoaming)414 public void setNetworkRoamingStatus(int simSubscriptionId, boolean isNetworkRoaming) { 415 if (isNetworkRoaming) { 416 roamingSimSubscriptionIds.add(simSubscriptionId); 417 } else { 418 roamingSimSubscriptionIds.remove(simSubscriptionId); 419 } 420 } 421 422 /** 423 * Uses the local cache of roaming sim subscription Ids managed by {@link 424 * #setNetworkRoamingStatus} to return subscription Ids marked as roaming. Otherwise subscription 425 * Ids will be considered as non-roaming if they are not in the cache. 426 */ 427 @Implementation(minSdk = LOLLIPOP_MR1) isNetworkRoaming(int simSubscriptionId)428 protected boolean isNetworkRoaming(int simSubscriptionId) { 429 return roamingSimSubscriptionIds.contains(simSubscriptionId); 430 } 431 432 /** Adds a subscription ID-phone ID mapping to the map used by {@link getPhoneId}. */ putPhoneId(int subId, int phoneId)433 public static void putPhoneId(int subId, int phoneId) { 434 phoneIds.put(subId, phoneId); 435 } 436 437 /** 438 * Removes a subscription ID-phone ID mapping from the map used by {@link getPhoneId}. 439 * 440 * @return the previous phone ID associated with the subscription ID, or null if there was no 441 * mapping for the subscription ID 442 */ removePhoneId(int subId)443 public static Integer removePhoneId(int subId) { 444 return phoneIds.remove(subId); 445 } 446 447 /** 448 * Removes all mappings between subscription IDs and phone IDs from the map used by {@link 449 * getPhoneId}. 450 */ clearPhoneIds()451 public static void clearPhoneIds() { 452 phoneIds.clear(); 453 } 454 455 /** 456 * Uses the map of subscription IDs to phone IDs managed by {@link putPhoneId} and {@link 457 * removePhoneId} to return the phone ID for a given subscription ID. 458 */ 459 @Implementation(minSdk = LOLLIPOP_MR1, maxSdk = P) 460 @HiddenApi getPhoneId(int subId)461 protected static int getPhoneId(int subId) { 462 if (phoneIds.containsKey(subId)) { 463 return phoneIds.get(subId); 464 } 465 return INVALID_PHONE_INDEX; 466 } 467 468 /** 469 * When set to false methods requiring {@link android.Manifest.permission.READ_PHONE_STATE} 470 * permission will throw a {@link SecurityException}. By default it's set to true for backwards 471 * compatibility. 472 */ setReadPhoneStatePermission(boolean readPhoneStatePermission)473 public void setReadPhoneStatePermission(boolean readPhoneStatePermission) { 474 this.readPhoneStatePermission = readPhoneStatePermission; 475 } 476 checkReadPhoneStatePermission()477 private void checkReadPhoneStatePermission() { 478 if (!readPhoneStatePermission) { 479 throw new SecurityException(); 480 } 481 } 482 483 /** 484 * When set to false methods requiring {@link android.Manifest.permission.READ_PHONE_NUMBERS} 485 * permission will throw a {@link SecurityException}. By default it's set to true for backwards 486 * compatibility. 487 */ setReadPhoneNumbersPermission(boolean readPhoneNumbersPermission)488 public void setReadPhoneNumbersPermission(boolean readPhoneNumbersPermission) { 489 this.readPhoneNumbersPermission = readPhoneNumbersPermission; 490 } 491 checkReadPhoneNumbersPermission()492 private void checkReadPhoneNumbersPermission() { 493 if (!readPhoneNumbersPermission) { 494 throw new SecurityException(); 495 } 496 } 497 498 /** 499 * Returns the phone number for the given {@code subscriptionId}, or an empty string if not 500 * available. 501 * 502 * <p>The phone number can be set by {@link #setPhoneNumber(int, String)} 503 * 504 * <p>An exception will be thrown if the READ_PHONE_NUMBERS permission has not been granted. 505 */ 506 @Implementation(minSdk = TIRAMISU) getPhoneNumber(int subscriptionId)507 protected String getPhoneNumber(int subscriptionId) { 508 checkReadPhoneNumbersPermission(); 509 return phoneNumberMap.getOrDefault(subscriptionId, ""); 510 } 511 512 /** 513 * Returns the phone number for the given {@code subscriptionId}, or an empty string if not 514 * available. {@code source} is ignored and will return the same as {@link #getPhoneNumber(int)}. 515 * 516 * <p>The phone number can be set by {@link #setPhoneNumber(int, String)} 517 */ 518 @Implementation(minSdk = TIRAMISU) getPhoneNumber(int subscriptionId, int source)519 protected String getPhoneNumber(int subscriptionId, int source) { 520 return getPhoneNumber(subscriptionId); 521 } 522 523 /** Sets the phone number returned by {@link #getPhoneNumber(int)}. */ setPhoneNumber(int subscriptionId, String phoneNumber)524 public void setPhoneNumber(int subscriptionId, String phoneNumber) { 525 phoneNumberMap.put(subscriptionId, phoneNumber); 526 } 527 528 @Resetter reset()529 public static void reset() { 530 activeDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 531 defaultDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 532 defaultSmsSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 533 defaultVoiceSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 534 defaultSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 535 phoneIds.clear(); 536 } 537 538 /** Builder class to create instance of {@link SubscriptionInfo}. */ 539 public static class SubscriptionInfoBuilder { 540 private final SubscriptionInfo subscriptionInfo = 541 ReflectionHelpers.callConstructor(SubscriptionInfo.class); 542 newBuilder()543 public static SubscriptionInfoBuilder newBuilder() { 544 return new SubscriptionInfoBuilder(); 545 } 546 buildSubscriptionInfo()547 public SubscriptionInfo buildSubscriptionInfo() { 548 return subscriptionInfo; 549 } 550 setId(int id)551 public SubscriptionInfoBuilder setId(int id) { 552 ReflectionHelpers.setField(subscriptionInfo, "mId", id); 553 return this; 554 } 555 setIccId(String iccId)556 public SubscriptionInfoBuilder setIccId(String iccId) { 557 ReflectionHelpers.setField(subscriptionInfo, "mIccId", iccId); 558 return this; 559 } 560 setSimSlotIndex(int index)561 public SubscriptionInfoBuilder setSimSlotIndex(int index) { 562 ReflectionHelpers.setField(subscriptionInfo, "mSimSlotIndex", index); 563 return this; 564 } 565 setDisplayName(String name)566 public SubscriptionInfoBuilder setDisplayName(String name) { 567 ReflectionHelpers.setField(subscriptionInfo, "mDisplayName", name); 568 return this; 569 } 570 setCarrierName(String carrierName)571 public SubscriptionInfoBuilder setCarrierName(String carrierName) { 572 ReflectionHelpers.setField(subscriptionInfo, "mCarrierName", carrierName); 573 return this; 574 } 575 setIconTint(int iconTint)576 public SubscriptionInfoBuilder setIconTint(int iconTint) { 577 ReflectionHelpers.setField(subscriptionInfo, "mIconTint", iconTint); 578 return this; 579 } 580 setNumber(String number)581 public SubscriptionInfoBuilder setNumber(String number) { 582 ReflectionHelpers.setField(subscriptionInfo, "mNumber", number); 583 return this; 584 } 585 setDataRoaming(int dataRoaming)586 public SubscriptionInfoBuilder setDataRoaming(int dataRoaming) { 587 ReflectionHelpers.setField(subscriptionInfo, "mDataRoaming", dataRoaming); 588 return this; 589 } 590 setCountryIso(String countryIso)591 public SubscriptionInfoBuilder setCountryIso(String countryIso) { 592 ReflectionHelpers.setField(subscriptionInfo, "mCountryIso", countryIso); 593 return this; 594 } 595 setProfileClass(int profileClass)596 public SubscriptionInfoBuilder setProfileClass(int profileClass) { 597 ReflectionHelpers.setField(subscriptionInfo, "mProfileClass", profileClass); 598 return this; 599 } 600 setIsEmbedded(boolean isEmbedded)601 public SubscriptionInfoBuilder setIsEmbedded(boolean isEmbedded) { 602 ReflectionHelpers.setField(subscriptionInfo, "mIsEmbedded", isEmbedded); 603 return this; 604 } 605 setIsOpportunistic(boolean isOpportunistic)606 public SubscriptionInfoBuilder setIsOpportunistic(boolean isOpportunistic) { 607 ReflectionHelpers.setField(subscriptionInfo, "mIsOpportunistic", isOpportunistic); 608 return this; 609 } 610 setMnc(String mnc)611 public SubscriptionInfoBuilder setMnc(String mnc) { 612 if (VERSION.SDK_INT < Q) { 613 ReflectionHelpers.setField(subscriptionInfo, "mMnc", Integer.valueOf(mnc)); 614 } else { 615 ReflectionHelpers.setField(subscriptionInfo, "mMnc", mnc); 616 } 617 return this; 618 } 619 setMcc(String mcc)620 public SubscriptionInfoBuilder setMcc(String mcc) { 621 if (VERSION.SDK_INT < Q) { 622 ReflectionHelpers.setField(subscriptionInfo, "mMcc", Integer.valueOf(mcc)); 623 } else { 624 ReflectionHelpers.setField(subscriptionInfo, "mMcc", mcc); 625 } 626 return this; 627 } 628 629 // Use {@link #newBuilder} to construct builders. SubscriptionInfoBuilder()630 private SubscriptionInfoBuilder() {} 631 } 632 } 633