• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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