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