1 /*
2  * Copyright (C) 2014 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 android.telephony;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SdkConstant;
21 import android.annotation.SystemService;
22 import android.annotation.SdkConstant.SdkConstantType;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.res.Configuration;
26 import android.content.res.Resources;
27 import android.net.Uri;
28 import android.telephony.Rlog;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.os.ServiceManager;
32 import android.os.RemoteException;
33 import android.util.DisplayMetrics;
34 
35 import com.android.internal.telephony.ISub;
36 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
37 import com.android.internal.telephony.ITelephonyRegistry;
38 import com.android.internal.telephony.PhoneConstants;
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 /**
43  * SubscriptionManager is the application interface to SubscriptionController
44  * and provides information about the current Telephony Subscriptions.
45  * <p>
46  * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE.
47  */
48 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
49 public class SubscriptionManager {
50     private static final String LOG_TAG = "SubscriptionManager";
51     private static final boolean DBG = false;
52     private static final boolean VDBG = false;
53 
54     /** An invalid subscription identifier */
55     public static final int INVALID_SUBSCRIPTION_ID = -1;
56 
57     /** Base value for Dummy SUBSCRIPTION_ID's. */
58     /** FIXME: Remove DummySubId's, but for now have them map just below INVALID_SUBSCRIPTION_ID
59     /** @hide */
60     public static final int DUMMY_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1;
61 
62     /** An invalid phone identifier */
63     /** @hide */
64     public static final int INVALID_PHONE_INDEX = -1;
65 
66     /** An invalid slot identifier */
67     /** @hide */
68     public static final int INVALID_SIM_SLOT_INDEX = -1;
69 
70     /** Indicates the caller wants the default sub id. */
71     /** @hide */
72     public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE;
73 
74     /**
75      * Indicates the caller wants the default phone id.
76      * Used in SubscriptionController and Phone but do we really need it???
77      * @hide
78      */
79     public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE;
80 
81     /** Indicates the caller wants the default slot id. NOT used remove? */
82     /** @hide */
83     public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE;
84 
85     /** Minimum possible subid that represents a subscription */
86     /** @hide */
87     public static final int MIN_SUBSCRIPTION_ID_VALUE = 0;
88 
89     /** Maximum possible subid that represents a subscription */
90     /** @hide */
91     public static final int MAX_SUBSCRIPTION_ID_VALUE = DEFAULT_SUBSCRIPTION_ID - 1;
92 
93     /** @hide */
94     public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
95 
96     /**
97      * TelephonyProvider unique key column name is the subscription id.
98      * <P>Type: TEXT (String)</P>
99      */
100     /** @hide */
101     public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
102 
103     /**
104      * TelephonyProvider column name for SIM ICC Identifier
105      * <P>Type: TEXT (String)</P>
106      */
107     /** @hide */
108     public static final String ICC_ID = "icc_id";
109 
110     /**
111      * TelephonyProvider column name for user SIM_SlOT_INDEX
112      * <P>Type: INTEGER (int)</P>
113      */
114     /** @hide */
115     public static final String SIM_SLOT_INDEX = "sim_id";
116 
117     /** SIM is not inserted */
118     /** @hide */
119     public static final int SIM_NOT_INSERTED = -1;
120 
121     /**
122      * TelephonyProvider column name for user displayed name.
123      * <P>Type: TEXT (String)</P>
124      */
125     /** @hide */
126     public static final String DISPLAY_NAME = "display_name";
127 
128     /**
129      * TelephonyProvider column name for the service provider name for the SIM.
130      * <P>Type: TEXT (String)</P>
131      */
132     /** @hide */
133     public static final String CARRIER_NAME = "carrier_name";
134 
135     /**
136      * Default name resource
137      * @hide
138      */
139     public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName;
140 
141     /**
142      * TelephonyProvider column name for source of the user displayed name.
143      * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below
144      *
145      * @hide
146      */
147     public static final String NAME_SOURCE = "name_source";
148 
149     /**
150      * The name_source is undefined
151      * @hide
152      */
153     public static final int NAME_SOURCE_UNDEFINDED = -1;
154 
155     /**
156      * The name_source is the default
157      * @hide
158      */
159     public static final int NAME_SOURCE_DEFAULT_SOURCE = 0;
160 
161     /**
162      * The name_source is from the SIM
163      * @hide
164      */
165     public static final int NAME_SOURCE_SIM_SOURCE = 1;
166 
167     /**
168      * The name_source is from the user
169      * @hide
170      */
171     public static final int NAME_SOURCE_USER_INPUT = 2;
172 
173     /**
174      * TelephonyProvider column name for the color of a SIM.
175      * <P>Type: INTEGER (int)</P>
176      */
177     /** @hide */
178     public static final String COLOR = "color";
179 
180     /** @hide */
181     public static final int COLOR_1 = 0;
182 
183     /** @hide */
184     public static final int COLOR_2 = 1;
185 
186     /** @hide */
187     public static final int COLOR_3 = 2;
188 
189     /** @hide */
190     public static final int COLOR_4 = 3;
191 
192     /** @hide */
193     public static final int COLOR_DEFAULT = COLOR_1;
194 
195     /**
196      * TelephonyProvider column name for the phone number of a SIM.
197      * <P>Type: TEXT (String)</P>
198      */
199     /** @hide */
200     public static final String NUMBER = "number";
201 
202     /**
203      * TelephonyProvider column name for the number display format of a SIM.
204      * <P>Type: INTEGER (int)</P>
205      */
206     /** @hide */
207     public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
208 
209     /** @hide */
210     public static final int DISPLAY_NUMBER_NONE = 0;
211 
212     /** @hide */
213     public static final int DISPLAY_NUMBER_FIRST = 1;
214 
215     /** @hide */
216     public static final int DISPLAY_NUMBER_LAST = 2;
217 
218     /** @hide */
219     public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
220 
221     /**
222      * TelephonyProvider column name for permission for data roaming of a SIM.
223      * <P>Type: INTEGER (int)</P>
224      */
225     /** @hide */
226     public static final String DATA_ROAMING = "data_roaming";
227 
228     /** Indicates that data roaming is enabled for a subscription */
229     public static final int DATA_ROAMING_ENABLE = 1;
230 
231     /** Indicates that data roaming is disabled for a subscription */
232     public static final int DATA_ROAMING_DISABLE = 0;
233 
234     /** @hide */
235     public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
236 
237     /** @hide */
238     public static final int SIM_PROVISIONED = 0;
239 
240     /**
241      * TelephonyProvider column name for the MCC associated with a SIM.
242      * <P>Type: INTEGER (int)</P>
243      * @hide
244      */
245     public static final String MCC = "mcc";
246 
247     /**
248      * TelephonyProvider column name for the MNC associated with a SIM.
249      * <P>Type: INTEGER (int)</P>
250      * @hide
251      */
252     public static final String MNC = "mnc";
253 
254     /**
255      * TelephonyProvider column name for the sim provisioning status associated with a SIM.
256      * <P>Type: INTEGER (int)</P>
257      * @hide
258      */
259     public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
260 
261     /**
262      *  TelephonyProvider column name for extreme threat in CB settings
263      * @hide
264      */
265     public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
266 
267     /**
268      * TelephonyProvider column name for severe threat in CB settings
269      *@hide
270      */
271     public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
272 
273     /**
274      * TelephonyProvider column name for amber alert in CB settings
275      *@hide
276      */
277     public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
278 
279     /**
280      * TelephonyProvider column name for emergency alert in CB settings
281      *@hide
282      */
283     public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
284 
285     /**
286      * TelephonyProvider column name for alert sound duration in CB settings
287      *@hide
288      */
289     public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
290 
291     /**
292      * TelephonyProvider column name for alert reminder interval in CB settings
293      *@hide
294      */
295     public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
296 
297     /**
298      * TelephonyProvider column name for enabling vibrate in CB settings
299      *@hide
300      */
301     public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
302 
303     /**
304      * TelephonyProvider column name for enabling alert speech in CB settings
305      *@hide
306      */
307     public static final String CB_ALERT_SPEECH = "enable_alert_speech";
308 
309     /**
310      * TelephonyProvider column name for ETWS test alert in CB settings
311      *@hide
312      */
313     public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
314 
315     /**
316      * TelephonyProvider column name for enable channel50 alert in CB settings
317      *@hide
318      */
319     public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
320 
321     /**
322      * TelephonyProvider column name for CMAS test alert in CB settings
323      *@hide
324      */
325     public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts";
326 
327     /**
328      * TelephonyProvider column name for Opt out dialog in CB settings
329      *@hide
330      */
331     public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
332 
333     /**
334      * Broadcast Action: The user has changed one of the default subs related to
335      * data, phone calls, or sms</p>
336      *
337      * TODO: Change to a listener
338      * @hide
339      */
340     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
341     public static final String SUB_DEFAULT_CHANGED_ACTION =
342         "android.intent.action.SUB_DEFAULT_CHANGED";
343 
344     /**
345      * Broadcast Action: The default subscription has changed.  This has the following
346      * extra values:</p>
347      * The {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default subscription index
348      */
349     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
350     public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED
351             = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
352 
353     /**
354      * Broadcast Action: The default sms subscription has changed.  This has the following
355      * extra values:</p>
356      * {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default sms
357      * subscription index
358      */
359     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
360     public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED
361             = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
362 
363     /**
364      * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and
365      * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription
366      * which has changed.
367      */
368     public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
369 
370     private final Context mContext;
371 
372     /**
373      * A listener class for monitoring changes to {@link SubscriptionInfo} records.
374      * <p>
375      * Override the onSubscriptionsChanged method in the object that extends this
376      * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
377      * to register your listener and to unregister invoke
378      * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
379      * <p>
380      * Permissions android.Manifest.permission.READ_PHONE_STATE is required
381      * for #onSubscriptionsChanged to be invoked.
382      */
383     public static class OnSubscriptionsChangedListener {
384         private final Handler mHandler  = new Handler() {
385             @Override
386             public void handleMessage(Message msg) {
387                 if (DBG) {
388                     log("handleMessage: invoke the overriden onSubscriptionsChanged()");
389                 }
390                 OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
391             }
392         };
393 
394         /**
395          * Callback invoked when there is any change to any SubscriptionInfo. Typically
396          * this method would invoke {@link #getActiveSubscriptionInfoList}
397          */
onSubscriptionsChanged()398         public void onSubscriptionsChanged() {
399             if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
400         }
401 
402         /**
403          * The callback methods need to be called on the handler thread where
404          * this object was created.  If the binder did that for us it'd be nice.
405          */
406         IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
407             @Override
408             public void onSubscriptionsChanged() {
409                 if (DBG) log("callback: received, sendEmptyMessage(0) to handler");
410                 mHandler.sendEmptyMessage(0);
411             }
412         };
413 
log(String s)414         private void log(String s) {
415             Rlog.d(LOG_TAG, s);
416         }
417     }
418 
419     /** @hide */
SubscriptionManager(Context context)420     public SubscriptionManager(Context context) {
421         if (DBG) logd("SubscriptionManager created");
422         mContext = context;
423     }
424 
425     /**
426      * Get an instance of the SubscriptionManager from the Context.
427      * This invokes {@link android.content.Context#getSystemService
428      * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}.
429      *
430      * @param context to use.
431      * @return SubscriptionManager instance
432      */
from(Context context)433     public static SubscriptionManager from(Context context) {
434         return (SubscriptionManager) context.getSystemService(
435                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
436     }
437 
438     /**
439      * Register for changes to the list of active {@link SubscriptionInfo} records or to the
440      * individual records themselves. When a change occurs the onSubscriptionsChanged method of
441      * the listener will be invoked immediately if there has been a notification.
442      *
443      * @param listener an instance of {@link OnSubscriptionsChangedListener} with
444      *                 onSubscriptionsChanged overridden.
445      */
addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener)446     public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
447         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
448         if (DBG) {
449             logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
450                     + " listener=" + listener);
451         }
452         try {
453             // We use the TelephonyRegistry as it runs in the system and thus is always
454             // available. Where as SubscriptionController could crash and not be available
455             ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
456                     "telephony.registry"));
457             if (tr != null) {
458                 tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
459             }
460         } catch (RemoteException ex) {
461             // Should not happen
462         }
463     }
464 
465     /**
466      * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary
467      * as the listener will automatically be unregistered if an attempt to invoke the listener
468      * fails.
469      *
470      * @param listener that is to be unregistered.
471      */
removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener)472     public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
473         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
474         if (DBG) {
475             logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
476                     + " listener=" + listener);
477         }
478         try {
479             // We use the TelephonyRegistry as its runs in the system and thus is always
480             // available where as SubscriptionController could crash and not be available
481             ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
482                     "telephony.registry"));
483             if (tr != null) {
484                 tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
485             }
486         } catch (RemoteException ex) {
487             // Should not happen
488         }
489     }
490 
491     /**
492      * Get the active SubscriptionInfo with the input subId.
493      *
494      * @param subId The unique SubscriptionInfo key in database.
495      * @return SubscriptionInfo, maybe null if its not active.
496      */
getActiveSubscriptionInfo(int subId)497     public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
498         if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
499         if (!isValidSubscriptionId(subId)) {
500             if (DBG) {
501                 logd("[getActiveSubscriptionInfo]- invalid subId");
502             }
503             return null;
504         }
505 
506         SubscriptionInfo subInfo = null;
507 
508         try {
509             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
510             if (iSub != null) {
511                 subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName());
512             }
513         } catch (RemoteException ex) {
514             // ignore it
515         }
516 
517         return subInfo;
518 
519     }
520 
521     /**
522      * Get the active SubscriptionInfo associated with the iccId
523      * @param iccId the IccId of SIM card
524      * @return SubscriptionInfo, maybe null if its not active
525      * @hide
526      */
getActiveSubscriptionInfoForIccIndex(String iccId)527     public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) {
528         if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId);
529         if (iccId == null) {
530             logd("[getActiveSubscriptionInfoForIccIndex]- null iccid");
531             return null;
532         }
533 
534         SubscriptionInfo result = null;
535 
536         try {
537             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
538             if (iSub != null) {
539                 result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName());
540             }
541         } catch (RemoteException ex) {
542             // ignore it
543         }
544 
545         return result;
546     }
547 
548     /**
549      * Get the active SubscriptionInfo associated with the slotIndex
550      * @param slotIndex the slot which the subscription is inserted
551      * @return SubscriptionInfo, maybe null if its not active
552      */
getActiveSubscriptionInfoForSimSlotIndex(int slotIndex)553     public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
554         if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
555         if (!isValidSlotIndex(slotIndex)) {
556             logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIndex");
557             return null;
558         }
559 
560         SubscriptionInfo result = null;
561 
562         try {
563             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
564             if (iSub != null) {
565                 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
566                         mContext.getOpPackageName());
567             }
568         } catch (RemoteException ex) {
569             // ignore it
570         }
571 
572         return result;
573     }
574 
575     /**
576      * @return List of all SubscriptionInfo records in database,
577      * include those that were inserted before, maybe empty but not null.
578      * @hide
579      */
getAllSubscriptionInfoList()580     public List<SubscriptionInfo> getAllSubscriptionInfoList() {
581         if (VDBG) logd("[getAllSubscriptionInfoList]+");
582 
583         List<SubscriptionInfo> result = null;
584 
585         try {
586             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
587             if (iSub != null) {
588                 result = iSub.getAllSubInfoList(mContext.getOpPackageName());
589             }
590         } catch (RemoteException ex) {
591             // ignore it
592         }
593 
594         if (result == null) {
595             result = new ArrayList<SubscriptionInfo>();
596         }
597         return result;
598     }
599 
600     /**
601      * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
602      * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
603      *
604      * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
605      * <ul>
606      * <li>
607      * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener}
608      * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be
609      * invoked in the future.
610      * </li>
611      * <li>
612      * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
613      * </li>
614      * <li>
615      * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
616      * then by {@link SubscriptionInfo#getSubscriptionId}.
617      * </li>
618      * </ul>
619      */
getActiveSubscriptionInfoList()620     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
621         List<SubscriptionInfo> result = null;
622 
623         try {
624             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
625             if (iSub != null) {
626                 result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
627             }
628         } catch (RemoteException ex) {
629             // ignore it
630         }
631         return result;
632     }
633 
634     /**
635      * @return the count of all subscriptions in the database, this includes
636      * all subscriptions that have been seen.
637      * @hide
638      */
getAllSubscriptionInfoCount()639     public int getAllSubscriptionInfoCount() {
640         if (VDBG) logd("[getAllSubscriptionInfoCount]+");
641 
642         int result = 0;
643 
644         try {
645             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
646             if (iSub != null) {
647                 result = iSub.getAllSubInfoCount(mContext.getOpPackageName());
648             }
649         } catch (RemoteException ex) {
650             // ignore it
651         }
652 
653         return result;
654     }
655 
656     /**
657      * @return the current number of active subscriptions. There is no guarantee the value
658      * returned by this method will be the same as the length of the list returned by
659      * {@link #getActiveSubscriptionInfoList}.
660      */
getActiveSubscriptionInfoCount()661     public int getActiveSubscriptionInfoCount() {
662         int result = 0;
663 
664         try {
665             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
666             if (iSub != null) {
667                 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName());
668             }
669         } catch (RemoteException ex) {
670             // ignore it
671         }
672 
673         return result;
674     }
675 
676     /**
677      * @return the maximum number of active subscriptions that will be returned by
678      * {@link #getActiveSubscriptionInfoList} and the value returned by
679      * {@link #getActiveSubscriptionInfoCount}.
680      */
getActiveSubscriptionInfoCountMax()681     public int getActiveSubscriptionInfoCountMax() {
682         int result = 0;
683 
684         try {
685             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
686             if (iSub != null) {
687                 result = iSub.getActiveSubInfoCountMax();
688             }
689         } catch (RemoteException ex) {
690             // ignore it
691         }
692 
693         return result;
694     }
695 
696     /**
697      * Add a new SubscriptionInfo to SubscriptionInfo database if needed
698      * @param iccId the IccId of the SIM card
699      * @param slotIndex the slot which the SIM is inserted
700      * @return the URL of the newly created row or the updated row
701      * @hide
702      */
addSubscriptionInfoRecord(String iccId, int slotIndex)703     public Uri addSubscriptionInfoRecord(String iccId, int slotIndex) {
704         if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotIndex:" + slotIndex);
705         if (iccId == null) {
706             logd("[addSubscriptionInfoRecord]- null iccId");
707         }
708         if (!isValidSlotIndex(slotIndex)) {
709             logd("[addSubscriptionInfoRecord]- invalid slotIndex");
710         }
711 
712         try {
713             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
714             if (iSub != null) {
715                 // FIXME: This returns 1 on success, 0 on error should should we return it?
716                 iSub.addSubInfoRecord(iccId, slotIndex);
717             }
718         } catch (RemoteException ex) {
719             // ignore it
720         }
721 
722         // FIXME: Always returns null?
723         return null;
724 
725     }
726 
727     /**
728      * Set SIM icon tint color by simInfo index
729      * @param tint the RGB value of icon tint color of the SIM
730      * @param subId the unique SubInfoRecord index in database
731      * @return the number of records updated
732      * @hide
733      */
setIconTint(int tint, int subId)734     public int setIconTint(int tint, int subId) {
735         if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
736         if (!isValidSubscriptionId(subId)) {
737             logd("[setIconTint]- fail");
738             return -1;
739         }
740 
741         int result = 0;
742 
743         try {
744             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
745             if (iSub != null) {
746                 result = iSub.setIconTint(tint, subId);
747             }
748         } catch (RemoteException ex) {
749             // ignore it
750         }
751 
752         return result;
753 
754     }
755 
756     /**
757      * Set display name by simInfo index
758      * @param displayName the display name of SIM card
759      * @param subId the unique SubscriptionInfo index in database
760      * @return the number of records updated
761      * @hide
762      */
setDisplayName(String displayName, int subId)763     public int setDisplayName(String displayName, int subId) {
764         return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED);
765     }
766 
767     /**
768      * Set display name by simInfo index with name source
769      * @param displayName the display name of SIM card
770      * @param subId the unique SubscriptionInfo index in database
771      * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
772      *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
773      * @return the number of records updated or < 0 if invalid subId
774      * @hide
775      */
setDisplayName(String displayName, int subId, long nameSource)776     public int setDisplayName(String displayName, int subId, long nameSource) {
777         if (VDBG) {
778             logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
779                     + " nameSource:" + nameSource);
780         }
781         if (!isValidSubscriptionId(subId)) {
782             logd("[setDisplayName]- fail");
783             return -1;
784         }
785 
786         int result = 0;
787 
788         try {
789             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
790             if (iSub != null) {
791                 result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource);
792             }
793         } catch (RemoteException ex) {
794             // ignore it
795         }
796 
797         return result;
798 
799     }
800 
801     /**
802      * Set phone number by subId
803      * @param number the phone number of the SIM
804      * @param subId the unique SubscriptionInfo index in database
805      * @return the number of records updated
806      * @hide
807      */
setDisplayNumber(String number, int subId)808     public int setDisplayNumber(String number, int subId) {
809         if (number == null || !isValidSubscriptionId(subId)) {
810             logd("[setDisplayNumber]- fail");
811             return -1;
812         }
813 
814         int result = 0;
815 
816         try {
817             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
818             if (iSub != null) {
819                 result = iSub.setDisplayNumber(number, subId);
820             }
821         } catch (RemoteException ex) {
822             // ignore it
823         }
824 
825         return result;
826 
827     }
828 
829     /**
830      * Set data roaming by simInfo index
831      * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
832      * @param subId the unique SubscriptionInfo index in database
833      * @return the number of records updated
834      * @hide
835      */
setDataRoaming(int roaming, int subId)836     public int setDataRoaming(int roaming, int subId) {
837         if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
838         if (roaming < 0 || !isValidSubscriptionId(subId)) {
839             logd("[setDataRoaming]- fail");
840             return -1;
841         }
842 
843         int result = 0;
844 
845         try {
846             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
847             if (iSub != null) {
848                 result = iSub.setDataRoaming(roaming, subId);
849             }
850         } catch (RemoteException ex) {
851             // ignore it
852         }
853 
854         return result;
855     }
856 
857     /**
858      * Get slotIndex associated with the subscription.
859      * @return slotIndex as a positive integer or a negative value if an error either
860      * SIM_NOT_INSERTED or < 0 if an invalid slot index
861      * @hide
862      */
getSlotIndex(int subId)863     public static int getSlotIndex(int subId) {
864         if (!isValidSubscriptionId(subId)) {
865             if (DBG) {
866                 logd("[getSlotIndex]- fail");
867             }
868         }
869 
870         int result = INVALID_SIM_SLOT_INDEX;
871 
872         try {
873             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
874             if (iSub != null) {
875                 result = iSub.getSlotIndex(subId);
876             }
877         } catch (RemoteException ex) {
878             // ignore it
879         }
880 
881         return result;
882 
883     }
884 
885     /** @hide */
getSubId(int slotIndex)886     public static int[] getSubId(int slotIndex) {
887         if (!isValidSlotIndex(slotIndex)) {
888             logd("[getSubId]- fail");
889             return null;
890         }
891 
892         int[] subId = null;
893 
894         try {
895             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
896             if (iSub != null) {
897                 subId = iSub.getSubId(slotIndex);
898             }
899         } catch (RemoteException ex) {
900             // ignore it
901         }
902 
903         return subId;
904     }
905 
906     /** @hide */
getPhoneId(int subId)907     public static int getPhoneId(int subId) {
908         if (!isValidSubscriptionId(subId)) {
909             if (DBG) {
910                 logd("[getPhoneId]- fail");
911             }
912             return INVALID_PHONE_INDEX;
913         }
914 
915         int result = INVALID_PHONE_INDEX;
916 
917         try {
918             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
919             if (iSub != null) {
920                 result = iSub.getPhoneId(subId);
921             }
922         } catch (RemoteException ex) {
923             // ignore it
924         }
925 
926         if (VDBG) logd("[getPhoneId]- phoneId=" + result);
927         return result;
928 
929     }
930 
logd(String msg)931     private static void logd(String msg) {
932         Rlog.d(LOG_TAG, msg);
933     }
934 
935     /**
936      * Returns the system's default subscription id.
937      *
938      * For a voice capable device, it will return getDefaultVoiceSubscriptionId.
939      * For a data only device, it will return the getDefaultDataSubscriptionId.
940      * May return an INVALID_SUBSCRIPTION_ID on error.
941      *
942      * @return the "system" default subscription id.
943      */
getDefaultSubscriptionId()944     public static int getDefaultSubscriptionId() {
945         int subId = INVALID_SUBSCRIPTION_ID;
946 
947         try {
948             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
949             if (iSub != null) {
950                 subId = iSub.getDefaultSubId();
951             }
952         } catch (RemoteException ex) {
953             // ignore it
954         }
955 
956         if (VDBG) logd("getDefaultSubId=" + subId);
957         return subId;
958     }
959 
960     /**
961      * Returns the system's default voice subscription id.
962      *
963      * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
964      *
965      * @return the default voice subscription Id.
966      */
getDefaultVoiceSubscriptionId()967     public static int getDefaultVoiceSubscriptionId() {
968         int subId = INVALID_SUBSCRIPTION_ID;
969 
970         try {
971             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
972             if (iSub != null) {
973                 subId = iSub.getDefaultVoiceSubId();
974             }
975         } catch (RemoteException ex) {
976             // ignore it
977         }
978 
979         if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
980         return subId;
981     }
982 
983     /** @hide */
setDefaultVoiceSubId(int subId)984     public void setDefaultVoiceSubId(int subId) {
985         if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId);
986         try {
987             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
988             if (iSub != null) {
989                 iSub.setDefaultVoiceSubId(subId);
990             }
991         } catch (RemoteException ex) {
992             // ignore it
993         }
994     }
995 
996     /**
997      * Return the SubscriptionInfo for default voice subscription.
998      *
999      * Will return null on data only devices, or on error.
1000      *
1001      * @return the SubscriptionInfo for the default voice subscription.
1002      * @hide
1003      */
getDefaultVoiceSubscriptionInfo()1004     public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
1005         return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId());
1006     }
1007 
1008     /** @hide */
getDefaultVoicePhoneId()1009     public static int getDefaultVoicePhoneId() {
1010         return getPhoneId(getDefaultVoiceSubscriptionId());
1011     }
1012 
1013     /**
1014      * Returns the system's default SMS subscription id.
1015      *
1016      * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
1017      *
1018      * @return the default SMS subscription Id.
1019      */
getDefaultSmsSubscriptionId()1020     public static int getDefaultSmsSubscriptionId() {
1021         int subId = INVALID_SUBSCRIPTION_ID;
1022 
1023         try {
1024             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1025             if (iSub != null) {
1026                 subId = iSub.getDefaultSmsSubId();
1027             }
1028         } catch (RemoteException ex) {
1029             // ignore it
1030         }
1031 
1032         if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId);
1033         return subId;
1034     }
1035 
1036     /** @hide */
setDefaultSmsSubId(int subId)1037     public void setDefaultSmsSubId(int subId) {
1038         if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
1039         try {
1040             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1041             if (iSub != null) {
1042                 iSub.setDefaultSmsSubId(subId);
1043             }
1044         } catch (RemoteException ex) {
1045             // ignore it
1046         }
1047     }
1048 
1049     /**
1050      * Return the SubscriptionInfo for default voice subscription.
1051      *
1052      * Will return null on data only devices, or on error.
1053      *
1054      * @return the SubscriptionInfo for the default SMS subscription.
1055      * @hide
1056      */
getDefaultSmsSubscriptionInfo()1057     public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
1058         return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId());
1059     }
1060 
1061     /** @hide */
getDefaultSmsPhoneId()1062     public int getDefaultSmsPhoneId() {
1063         return getPhoneId(getDefaultSmsSubscriptionId());
1064     }
1065 
1066     /**
1067      * Returns the system's default data subscription id.
1068      *
1069      * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID.
1070      *
1071      * @return the default data subscription Id.
1072      */
getDefaultDataSubscriptionId()1073     public static int getDefaultDataSubscriptionId() {
1074         int subId = INVALID_SUBSCRIPTION_ID;
1075 
1076         try {
1077             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1078             if (iSub != null) {
1079                 subId = iSub.getDefaultDataSubId();
1080             }
1081         } catch (RemoteException ex) {
1082             // ignore it
1083         }
1084 
1085         if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId);
1086         return subId;
1087     }
1088 
1089     /** @hide */
setDefaultDataSubId(int subId)1090     public void setDefaultDataSubId(int subId) {
1091         if (VDBG) logd("setDataSubscription sub id = " + subId);
1092         try {
1093             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1094             if (iSub != null) {
1095                 iSub.setDefaultDataSubId(subId);
1096             }
1097         } catch (RemoteException ex) {
1098             // ignore it
1099         }
1100     }
1101 
1102     /**
1103      * Return the SubscriptionInfo for default data subscription.
1104      *
1105      * Will return null on voice only devices, or on error.
1106      *
1107      * @return the SubscriptionInfo for the default data subscription.
1108      * @hide
1109      */
getDefaultDataSubscriptionInfo()1110     public SubscriptionInfo getDefaultDataSubscriptionInfo() {
1111         return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
1112     }
1113 
1114     /** @hide */
getDefaultDataPhoneId()1115     public int getDefaultDataPhoneId() {
1116         return getPhoneId(getDefaultDataSubscriptionId());
1117     }
1118 
1119     /** @hide */
clearSubscriptionInfo()1120     public void clearSubscriptionInfo() {
1121         try {
1122             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1123             if (iSub != null) {
1124                 iSub.clearSubInfo();
1125             }
1126         } catch (RemoteException ex) {
1127             // ignore it
1128         }
1129 
1130         return;
1131     }
1132 
1133     //FIXME this is vulnerable to race conditions
1134     /** @hide */
allDefaultsSelected()1135     public boolean allDefaultsSelected() {
1136         if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) {
1137             return false;
1138         }
1139         if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) {
1140             return false;
1141         }
1142         if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) {
1143             return false;
1144         }
1145         return true;
1146     }
1147 
1148     /**
1149      * If a default is set to subscription which is not active, this will reset that default back to
1150      * an invalid subscription id, i.e. < 0.
1151      * @hide
1152      */
clearDefaultsForInactiveSubIds()1153     public void clearDefaultsForInactiveSubIds() {
1154         if (VDBG) logd("clearDefaultsForInactiveSubIds");
1155         try {
1156             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1157             if (iSub != null) {
1158                 iSub.clearDefaultsForInactiveSubIds();
1159             }
1160         } catch (RemoteException ex) {
1161             // ignore it
1162         }
1163     }
1164 
1165     /**
1166      * @return true if a valid subId else false
1167      * @hide
1168      */
isValidSubscriptionId(int subId)1169     public static boolean isValidSubscriptionId(int subId) {
1170         return subId > INVALID_SUBSCRIPTION_ID ;
1171     }
1172 
1173     /**
1174      * @return true if subId is an usable subId value else false. A
1175      * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
1176      * @hide
1177      */
isUsableSubIdValue(int subId)1178     public static boolean isUsableSubIdValue(int subId) {
1179         return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
1180     }
1181 
1182     /** @hide */
isValidSlotIndex(int slotIndex)1183     public static boolean isValidSlotIndex(int slotIndex) {
1184         return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSimCount();
1185     }
1186 
1187     /** @hide */
isValidPhoneId(int phoneId)1188     public static boolean isValidPhoneId(int phoneId) {
1189         return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
1190     }
1191 
1192     /** @hide */
putPhoneIdAndSubIdExtra(Intent intent, int phoneId)1193     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
1194         int[] subIds = SubscriptionManager.getSubId(phoneId);
1195         if (subIds != null && subIds.length > 0) {
1196             putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]);
1197         } else {
1198             logd("putPhoneIdAndSubIdExtra: no valid subs");
1199         }
1200     }
1201 
1202     /** @hide */
putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId)1203     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) {
1204         if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
1205         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1206         intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
1207         intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
1208         //FIXME this is using phoneId and slotIndex interchangeably
1209         //Eventually, this should be removed as it is not the slot id
1210         intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
1211     }
1212 
1213     /**
1214      * @return the list of subId's that are active,
1215      *         is never null but the length maybe 0.
1216      * @hide
1217      */
getActiveSubscriptionIdList()1218     public @NonNull int[] getActiveSubscriptionIdList() {
1219         int[] subId = null;
1220 
1221         try {
1222             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1223             if (iSub != null) {
1224                 subId = iSub.getActiveSubIdList();
1225             }
1226         } catch (RemoteException ex) {
1227             // ignore it
1228         }
1229 
1230         if (subId == null) {
1231             subId = new int[0];
1232         }
1233 
1234         return subId;
1235 
1236     }
1237 
1238     /**
1239      * Returns true if the device is considered roaming on the current
1240      * network for a subscription.
1241      * <p>
1242      * Availability: Only when user registered to a network.
1243      *
1244      * @param subId The subscription ID
1245      * @return true if the network for the subscription is roaming, false otherwise
1246      */
isNetworkRoaming(int subId)1247     public boolean isNetworkRoaming(int subId) {
1248         final int phoneId = getPhoneId(subId);
1249         if (phoneId < 0) {
1250             // What else can we do?
1251             return false;
1252         }
1253         return TelephonyManager.getDefault().isNetworkRoaming(subId);
1254     }
1255 
1256     /**
1257      * Returns a constant indicating the state of sim for the slot index.
1258      *
1259      * @param slotIndex
1260      *
1261      * {@See TelephonyManager#SIM_STATE_UNKNOWN}
1262      * {@See TelephonyManager#SIM_STATE_ABSENT}
1263      * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED}
1264      * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED}
1265      * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED}
1266      * {@See TelephonyManager#SIM_STATE_READY}
1267      * {@See TelephonyManager#SIM_STATE_NOT_READY}
1268      * {@See TelephonyManager#SIM_STATE_PERM_DISABLED}
1269      * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR}
1270      *
1271      * {@hide}
1272      */
getSimStateForSlotIndex(int slotIndex)1273     public static int getSimStateForSlotIndex(int slotIndex) {
1274         int simState = TelephonyManager.SIM_STATE_UNKNOWN;
1275 
1276         try {
1277             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1278             if (iSub != null) {
1279                 simState = iSub.getSimStateForSlotIndex(slotIndex);
1280             }
1281         } catch (RemoteException ex) {
1282         }
1283 
1284         return simState;
1285     }
1286 
1287     /**
1288      * Store properties associated with SubscriptionInfo in database
1289      * @param subId Subscription Id of Subscription
1290      * @param propKey Column name in database associated with SubscriptionInfo
1291      * @param propValue Value to store in DB for particular subId & column name
1292      * @hide
1293      */
setSubscriptionProperty(int subId, String propKey, String propValue)1294     public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
1295         try {
1296             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1297             if (iSub != null) {
1298                 iSub.setSubscriptionProperty(subId, propKey, propValue);
1299             }
1300         } catch (RemoteException ex) {
1301             // ignore it
1302         }
1303     }
1304 
1305     /**
1306      * Store properties associated with SubscriptionInfo in database
1307      * @param subId Subscription Id of Subscription
1308      * @param propKey Column name in SubscriptionInfo database
1309      * @return Value associated with subId and propKey column in database
1310      * @hide
1311      */
getSubscriptionProperty(int subId, String propKey, Context context)1312     private static String getSubscriptionProperty(int subId, String propKey,
1313             Context context) {
1314         String resultValue = null;
1315         try {
1316             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1317             if (iSub != null) {
1318                 resultValue = iSub.getSubscriptionProperty(subId, propKey,
1319                     context.getOpPackageName());
1320             }
1321         } catch (RemoteException ex) {
1322             // ignore it
1323         }
1324         return resultValue;
1325     }
1326 
1327     /**
1328      * Returns boolean value corresponding to query result.
1329      * @param subId Subscription Id of Subscription
1330      * @param propKey Column name in SubscriptionInfo database
1331      * @param defValue Default boolean value to be returned
1332      * @return boolean result value to be returned
1333      * @hide
1334      */
getBooleanSubscriptionProperty(int subId, String propKey, boolean defValue, Context context)1335     public static boolean getBooleanSubscriptionProperty(int subId, String propKey,
1336             boolean defValue, Context context) {
1337         String result = getSubscriptionProperty(subId, propKey, context);
1338         if (result != null) {
1339             try {
1340                 return Integer.parseInt(result) == 1;
1341             } catch (NumberFormatException err) {
1342                 logd("getBooleanSubscriptionProperty NumberFormat exception");
1343             }
1344         }
1345         return defValue;
1346     }
1347 
1348     /**
1349      * Returns integer value corresponding to query result.
1350      * @param subId Subscription Id of Subscription
1351      * @param propKey Column name in SubscriptionInfo database
1352      * @param defValue Default integer value to be returned
1353      * @return integer result value to be returned
1354      * @hide
1355      */
getIntegerSubscriptionProperty(int subId, String propKey, int defValue, Context context)1356     public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue,
1357             Context context) {
1358         String result = getSubscriptionProperty(subId, propKey, context);
1359         if (result != null) {
1360             try {
1361                 return Integer.parseInt(result);
1362             } catch (NumberFormatException err) {
1363                 logd("getBooleanSubscriptionProperty NumberFormat exception");
1364             }
1365         }
1366         return defValue;
1367     }
1368 
1369     /**
1370      * Returns the resources associated with Subscription.
1371      * @param context Context object
1372      * @param subId Subscription Id of Subscription who's resources are required
1373      * @return Resources associated with Subscription.
1374      * @hide
1375      */
getResourcesForSubId(Context context, int subId)1376     public static Resources getResourcesForSubId(Context context, int subId) {
1377         final SubscriptionInfo subInfo =
1378                 SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
1379 
1380         Configuration config = context.getResources().getConfiguration();
1381         Configuration newConfig = new Configuration();
1382         newConfig.setTo(config);
1383         if (subInfo != null) {
1384             newConfig.mcc = subInfo.getMcc();
1385             newConfig.mnc = subInfo.getMnc();
1386             if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
1387         }
1388         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
1389         DisplayMetrics newMetrics = new DisplayMetrics();
1390         newMetrics.setTo(metrics);
1391         return new Resources(context.getResources().getAssets(), newMetrics, newConfig);
1392     }
1393 
1394     /**
1395      * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
1396      * and the SIM providing the subscription is present in a slot and in "LOADED" state.
1397      * @hide
1398      */
isActiveSubId(int subId)1399     public boolean isActiveSubId(int subId) {
1400         try {
1401             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1402             if (iSub != null) {
1403                 return iSub.isActiveSubId(subId);
1404             }
1405         } catch (RemoteException ex) {
1406         }
1407         return false;
1408     }
1409 }
1410