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