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