SubscriptionManager.java revision 85973dcf8cd7d7a964ba746875a0c572685d1807
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 com.android.internal.telephony.TelephonyProperties;
36
37import java.util.ArrayList;
38import 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 */
49public 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         */
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
311        private void log(String s) {
312            Rlog.d(LOG_TAG, s);
313        }
314    }
315
316    /** @hide */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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 */
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 */
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
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     */
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 */
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 */
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 */
877    public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
878        return getActiveSubscriptionInfo(getDefaultVoiceSubId());
879    }
880
881    /** @hide */
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     */
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 */
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 */
922    public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
923        return getActiveSubscriptionInfo(getDefaultSmsSubId());
924    }
925
926    /** @hide */
927    public int getDefaultSmsPhoneId() {
928        return getPhoneId(getDefaultSmsSubId());
929    }
930
931    /** @hide */
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 */
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 */
962    public SubscriptionInfo getDefaultDataSubscriptionInfo() {
963        return getActiveSubscriptionInfo(getDefaultDataSubId());
964    }
965
966    /** @hide */
967    public int getDefaultDataPhoneId() {
968        return getPhoneId(getDefaultDataSubId());
969    }
970
971    /** @hide */
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 */
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     */
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     */
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     */
1030    public static boolean isUsableSubIdValue(int subId) {
1031        return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
1032    }
1033
1034    /** @hide */
1035    public static boolean isValidSlotId(int slotId) {
1036        return slotId >= 0 && slotId < TelephonyManager.getDefault().getSimCount();
1037    }
1038
1039    /** @hide */
1040    public static boolean isValidPhoneId(int phoneId) {
1041        return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
1042    }
1043
1044    /** @hide */
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 */
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     */
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     */
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     */
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