SubscriptionManager.java revision 21301f6016ce50594f6cd9c66581ee9a08b57be9
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, mContext.getOpPackageName());
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, mContext.getOpPackageName());
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                        mContext.getOpPackageName());
455            }
456        } catch (RemoteException ex) {
457            // ignore it
458        }
459
460        return result;
461    }
462
463    /**
464     * @return List of all SubscriptionInfo records in database,
465     * include those that were inserted before, maybe empty but not null.
466     * @hide
467     */
468    public List<SubscriptionInfo> getAllSubscriptionInfoList() {
469        if (VDBG) logd("[getAllSubscriptionInfoList]+");
470
471        List<SubscriptionInfo> result = null;
472
473        try {
474            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
475            if (iSub != null) {
476                result = iSub.getAllSubInfoList(mContext.getOpPackageName());
477            }
478        } catch (RemoteException ex) {
479            // ignore it
480        }
481
482        if (result == null) {
483            result = new ArrayList<SubscriptionInfo>();
484        }
485        return result;
486    }
487
488    /**
489     * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
490     * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
491     *
492     * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
493     * <ul>
494     * <li>
495     * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener}
496     * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be
497     * invoked in the future.
498     * </li>
499     * <li>
500     * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
501     * </li>
502     * <li>
503     * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
504     * then by {@link SubscriptionInfo#getSubscriptionId}.
505     * </li>
506     * </ul>
507     */
508    public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
509        List<SubscriptionInfo> result = null;
510
511        try {
512            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
513            if (iSub != null) {
514                result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
515            }
516        } catch (RemoteException ex) {
517            // ignore it
518        }
519        return result;
520    }
521
522    /**
523     * @return the count of all subscriptions in the database, this includes
524     * all subscriptions that have been seen.
525     * @hide
526     */
527    public int getAllSubscriptionInfoCount() {
528        if (VDBG) logd("[getAllSubscriptionInfoCount]+");
529
530        int result = 0;
531
532        try {
533            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
534            if (iSub != null) {
535                result = iSub.getAllSubInfoCount(mContext.getOpPackageName());
536            }
537        } catch (RemoteException ex) {
538            // ignore it
539        }
540
541        return result;
542    }
543
544    /**
545     * @return the current number of active subscriptions. There is no guarantee the value
546     * returned by this method will be the same as the length of the list returned by
547     * {@link #getActiveSubscriptionInfoList}.
548     */
549    public int getActiveSubscriptionInfoCount() {
550        int result = 0;
551
552        try {
553            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
554            if (iSub != null) {
555                result = iSub.getActiveSubInfoCount(mContext.getOpPackageName());
556            }
557        } catch (RemoteException ex) {
558            // ignore it
559        }
560
561        return result;
562    }
563
564    /**
565     * @return the maximum number of active subscriptions that will be returned by
566     * {@link #getActiveSubscriptionInfoList} and the value returned by
567     * {@link #getActiveSubscriptionInfoCount}.
568     */
569    public int getActiveSubscriptionInfoCountMax() {
570        int result = 0;
571
572        try {
573            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
574            if (iSub != null) {
575                result = iSub.getActiveSubInfoCountMax();
576            }
577        } catch (RemoteException ex) {
578            // ignore it
579        }
580
581        return result;
582    }
583
584    /**
585     * Add a new SubscriptionInfo to SubscriptionInfo database if needed
586     * @param iccId the IccId of the SIM card
587     * @param slotId the slot which the SIM is inserted
588     * @return the URL of the newly created row or the updated row
589     * @hide
590     */
591    public Uri addSubscriptionInfoRecord(String iccId, int slotId) {
592        if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
593        if (iccId == null) {
594            logd("[addSubscriptionInfoRecord]- null iccId");
595        }
596        if (!isValidSlotId(slotId)) {
597            logd("[addSubscriptionInfoRecord]- invalid slotId");
598        }
599
600        try {
601            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
602            if (iSub != null) {
603                // FIXME: This returns 1 on success, 0 on error should should we return it?
604                iSub.addSubInfoRecord(iccId, slotId, mContext.getOpPackageName());
605            }
606        } catch (RemoteException ex) {
607            // ignore it
608        }
609
610        // FIXME: Always returns null?
611        return null;
612
613    }
614
615    /**
616     * Set SIM icon tint color by simInfo index
617     * @param tint the RGB value of icon tint color of the SIM
618     * @param subId the unique SubInfoRecord index in database
619     * @return the number of records updated
620     * @hide
621     */
622    public int setIconTint(int tint, int subId) {
623        if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
624        if (!isValidSubscriptionId(subId)) {
625            logd("[setIconTint]- fail");
626            return -1;
627        }
628
629        int result = 0;
630
631        try {
632            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
633            if (iSub != null) {
634                result = iSub.setIconTint(tint, subId, mContext.getOpPackageName());
635            }
636        } catch (RemoteException ex) {
637            // ignore it
638        }
639
640        return result;
641
642    }
643
644    /**
645     * Set display name by simInfo index
646     * @param displayName the display name of SIM card
647     * @param subId the unique SubscriptionInfo index in database
648     * @return the number of records updated
649     * @hide
650     */
651    public int setDisplayName(String displayName, int subId) {
652        return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED);
653    }
654
655    /**
656     * Set display name by simInfo index with name source
657     * @param displayName the display name of SIM card
658     * @param subId the unique SubscriptionInfo index in database
659     * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
660     *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
661     * @return the number of records updated or < 0 if invalid subId
662     * @hide
663     */
664    public int setDisplayName(String displayName, int subId, long nameSource) {
665        if (VDBG) {
666            logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
667                    + " nameSource:" + nameSource);
668        }
669        if (!isValidSubscriptionId(subId)) {
670            logd("[setDisplayName]- fail");
671            return -1;
672        }
673
674        int result = 0;
675
676        try {
677            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
678            if (iSub != null) {
679                result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource,
680                        mContext.getOpPackageName());
681            }
682        } catch (RemoteException ex) {
683            // ignore it
684        }
685
686        return result;
687
688    }
689
690    /**
691     * Set phone number by subId
692     * @param number the phone number of the SIM
693     * @param subId the unique SubscriptionInfo index in database
694     * @return the number of records updated
695     * @hide
696     */
697    public int setDisplayNumber(String number, int subId) {
698        if (number == null || !isValidSubscriptionId(subId)) {
699            logd("[setDisplayNumber]- fail");
700            return -1;
701        }
702
703        int result = 0;
704
705        try {
706            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
707            if (iSub != null) {
708                result = iSub.setDisplayNumber(number, subId, mContext.getOpPackageName());
709            }
710        } catch (RemoteException ex) {
711            // ignore it
712        }
713
714        return result;
715
716    }
717
718    /**
719     * Set data roaming by simInfo index
720     * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
721     * @param subId the unique SubscriptionInfo index in database
722     * @return the number of records updated
723     * @hide
724     */
725    public int setDataRoaming(int roaming, int subId) {
726        if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
727        if (roaming < 0 || !isValidSubscriptionId(subId)) {
728            logd("[setDataRoaming]- fail");
729            return -1;
730        }
731
732        int result = 0;
733
734        try {
735            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
736            if (iSub != null) {
737                result = iSub.setDataRoaming(roaming, subId, mContext.getOpPackageName());
738            }
739        } catch (RemoteException ex) {
740            // ignore it
741        }
742
743        return result;
744    }
745
746    /**
747     * Get slotId associated with the subscription.
748     * @return slotId as a positive integer or a negative value if an error either
749     * SIM_NOT_INSERTED or < 0 if an invalid slot index
750     * @hide
751     */
752    public static int getSlotId(int subId) {
753        if (!isValidSubscriptionId(subId)) {
754            logd("[getSlotId]- fail");
755        }
756
757        int result = INVALID_SIM_SLOT_INDEX;
758
759        try {
760            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
761            if (iSub != null) {
762                result = iSub.getSlotId(subId);
763            }
764        } catch (RemoteException ex) {
765            // ignore it
766        }
767
768        return result;
769
770    }
771
772    /** @hide */
773    public static int[] getSubId(int slotId) {
774        if (!isValidSlotId(slotId)) {
775            logd("[getSubId]- fail");
776            return null;
777        }
778
779        int[] subId = null;
780
781        try {
782            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
783            if (iSub != null) {
784                subId = iSub.getSubId(slotId);
785            }
786        } catch (RemoteException ex) {
787            // ignore it
788        }
789
790        return subId;
791    }
792
793    /** @hide */
794    public static int getPhoneId(int subId) {
795        if (!isValidSubscriptionId(subId)) {
796            logd("[getPhoneId]- fail");
797            return INVALID_PHONE_INDEX;
798        }
799
800        int result = INVALID_PHONE_INDEX;
801
802        try {
803            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
804            if (iSub != null) {
805                result = iSub.getPhoneId(subId);
806            }
807        } catch (RemoteException ex) {
808            // ignore it
809        }
810
811        if (VDBG) logd("[getPhoneId]- phoneId=" + result);
812        return result;
813
814    }
815
816    private static void logd(String msg) {
817        Rlog.d(LOG_TAG, msg);
818    }
819
820    /**
821     * @return the "system" defaultSubId on a voice capable device this
822     * will be getDefaultVoiceSubId() and on a data only device it will be
823     * getDefaultDataSubId().
824     * @hide
825     */
826    public static int getDefaultSubId() {
827        int subId = INVALID_SUBSCRIPTION_ID;
828
829        try {
830            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
831            if (iSub != null) {
832                subId = iSub.getDefaultSubId();
833            }
834        } catch (RemoteException ex) {
835            // ignore it
836        }
837
838        if (VDBG) logd("getDefaultSubId=" + subId);
839        return subId;
840    }
841
842    /** @hide */
843    public static int getDefaultVoiceSubId() {
844        int subId = INVALID_SUBSCRIPTION_ID;
845
846        try {
847            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
848            if (iSub != null) {
849                subId = iSub.getDefaultVoiceSubId();
850            }
851        } catch (RemoteException ex) {
852            // ignore it
853        }
854
855        if (VDBG) logd("getDefaultVoiceSubId, sub id = " + subId);
856        return subId;
857    }
858
859    /** @hide */
860    public void setDefaultVoiceSubId(int subId) {
861        if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId);
862        try {
863            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
864            if (iSub != null) {
865                iSub.setDefaultVoiceSubId(subId);
866            }
867        } catch (RemoteException ex) {
868            // ignore it
869        }
870    }
871
872    /** @hide */
873    public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
874        return getActiveSubscriptionInfo(getDefaultVoiceSubId());
875    }
876
877    /** @hide */
878    public static int getDefaultVoicePhoneId() {
879        return getPhoneId(getDefaultVoiceSubId());
880    }
881
882    /**
883     * @return subId of the DefaultSms subscription or
884     * a value < 0 if an error.
885     *
886     * @hide
887     */
888    public static int getDefaultSmsSubId() {
889        int subId = INVALID_SUBSCRIPTION_ID;
890
891        try {
892            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
893            if (iSub != null) {
894                subId = iSub.getDefaultSmsSubId();
895            }
896        } catch (RemoteException ex) {
897            // ignore it
898        }
899
900        if (VDBG) logd("getDefaultSmsSubId, sub id = " + subId);
901        return subId;
902    }
903
904    /** @hide */
905    public void setDefaultSmsSubId(int subId) {
906        if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
907        try {
908            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
909            if (iSub != null) {
910                iSub.setDefaultSmsSubId(subId);
911            }
912        } catch (RemoteException ex) {
913            // ignore it
914        }
915    }
916
917    /** @hide */
918    public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
919        return getActiveSubscriptionInfo(getDefaultSmsSubId());
920    }
921
922    /** @hide */
923    public int getDefaultSmsPhoneId() {
924        return getPhoneId(getDefaultSmsSubId());
925    }
926
927    /** @hide */
928    public static int getDefaultDataSubId() {
929        int subId = INVALID_SUBSCRIPTION_ID;
930
931        try {
932            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
933            if (iSub != null) {
934                subId = iSub.getDefaultDataSubId();
935            }
936        } catch (RemoteException ex) {
937            // ignore it
938        }
939
940        if (VDBG) logd("getDefaultDataSubId, sub id = " + subId);
941        return subId;
942    }
943
944    /** @hide */
945    public void setDefaultDataSubId(int subId) {
946        if (VDBG) logd("setDataSubscription sub id = " + subId);
947        try {
948            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
949            if (iSub != null) {
950                iSub.setDefaultDataSubId(subId);
951            }
952        } catch (RemoteException ex) {
953            // ignore it
954        }
955    }
956
957    /** @hide */
958    public SubscriptionInfo getDefaultDataSubscriptionInfo() {
959        return getActiveSubscriptionInfo(getDefaultDataSubId());
960    }
961
962    /** @hide */
963    public int getDefaultDataPhoneId() {
964        return getPhoneId(getDefaultDataSubId());
965    }
966
967    /** @hide */
968    public void clearSubscriptionInfo() {
969        try {
970            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
971            if (iSub != null) {
972                iSub.clearSubInfo(mContext.getOpPackageName());
973            }
974        } catch (RemoteException ex) {
975            // ignore it
976        }
977
978        return;
979    }
980
981    //FIXME this is vulnerable to race conditions
982    /** @hide */
983    public boolean allDefaultsSelected() {
984        if (!isValidSubscriptionId(getDefaultDataSubId())) {
985            return false;
986        }
987        if (!isValidSubscriptionId(getDefaultSmsSubId())) {
988            return false;
989        }
990        if (!isValidSubscriptionId(getDefaultVoiceSubId())) {
991            return false;
992        }
993        return true;
994    }
995
996    /**
997     * If a default is set to subscription which is not active, this will reset that default back to
998     * an invalid subscription id, i.e. < 0.
999     * @hide
1000     */
1001    public void clearDefaultsForInactiveSubIds() {
1002        if (VDBG) logd("clearDefaultsForInactiveSubIds");
1003        try {
1004            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1005            if (iSub != null) {
1006                iSub.clearDefaultsForInactiveSubIds(mContext.getOpPackageName());
1007            }
1008        } catch (RemoteException ex) {
1009            // ignore it
1010        }
1011    }
1012
1013    /**
1014     * @return true if a valid subId else false
1015     * @hide
1016     */
1017    public static boolean isValidSubscriptionId(int subId) {
1018        return subId > INVALID_SUBSCRIPTION_ID ;
1019    }
1020
1021    /**
1022     * @return true if subId is an usable subId value else false. A
1023     * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
1024     * @hide
1025     */
1026    public static boolean isUsableSubIdValue(int subId) {
1027        return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
1028    }
1029
1030    /** @hide */
1031    public static boolean isValidSlotId(int slotId) {
1032        return slotId >= 0 && slotId < TelephonyManager.getDefault().getSimCount();
1033    }
1034
1035    /** @hide */
1036    public static boolean isValidPhoneId(int phoneId) {
1037        return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
1038    }
1039
1040    /** @hide */
1041    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
1042        int[] subIds = SubscriptionManager.getSubId(phoneId);
1043        if (subIds != null && subIds.length > 0) {
1044            putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]);
1045        } else {
1046            logd("putPhoneIdAndSubIdExtra: no valid subs");
1047        }
1048    }
1049
1050    /** @hide */
1051    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) {
1052        if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
1053        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1054        intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
1055        //FIXME this is using phoneId and slotId interchangeably
1056        //Eventually, this should be removed as it is not the slot id
1057        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
1058    }
1059
1060    /**
1061     * @return the list of subId's that are active,
1062     *         is never null but the length maybe 0.
1063     * @hide
1064     */
1065    public @NonNull int[] getActiveSubscriptionIdList() {
1066        int[] subId = null;
1067
1068        try {
1069            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1070            if (iSub != null) {
1071                subId = iSub.getActiveSubIdList();
1072            }
1073        } catch (RemoteException ex) {
1074            // ignore it
1075        }
1076
1077        if (subId == null) {
1078            subId = new int[0];
1079        }
1080
1081        return subId;
1082
1083    }
1084
1085    /**
1086     * Returns true if the device is considered roaming on the current
1087     * network for a subscription.
1088     * <p>
1089     * Availability: Only when user registered to a network.
1090     *
1091     * @param subId The subscription ID
1092     * @return true if the network for the subscription is roaming, false otherwise
1093     */
1094    public boolean isNetworkRoaming(int subId) {
1095        final int phoneId = getPhoneId(subId);
1096        if (phoneId < 0) {
1097            // What else can we do?
1098            return false;
1099        }
1100        return TelephonyManager.getDefault().isNetworkRoaming(subId);
1101    }
1102
1103    /**
1104     * Returns a constant indicating the state of sim for the subscription.
1105     *
1106     * @param subId
1107     *
1108     * {@See TelephonyManager#SIM_STATE_UNKNOWN}
1109     * {@See TelephonyManager#SIM_STATE_ABSENT}
1110     * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED}
1111     * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED}
1112     * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED}
1113     * {@See TelephonyManager#SIM_STATE_READY}
1114     * {@See TelephonyManager#SIM_STATE_NOT_READY}
1115     * {@See TelephonyManager#SIM_STATE_PERM_DISABLED}
1116     * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR}
1117     *
1118     * {@hide}
1119     */
1120    public static int getSimStateForSubscriber(int subId) {
1121        int simState;
1122
1123        try {
1124            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1125            simState = iSub.getSimStateForSubscriber(subId);
1126        } catch (RemoteException ex) {
1127            simState = TelephonyManager.SIM_STATE_UNKNOWN;
1128        }
1129        logd("getSimStateForSubscriber: simState=" + simState + " subId=" + subId);
1130        return simState;
1131    }
1132}
1133
1134