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