SubscriptionManager.java revision 598d24c55817cfbd00b6dafdf772334a7039fe3e
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.content.res.Configuration;
25import android.content.res.Resources;
26import android.net.Uri;
27import android.telephony.Rlog;
28import android.os.Handler;
29import android.os.Message;
30import android.os.ServiceManager;
31import android.os.RemoteException;
32import android.util.DisplayMetrics;
33
34import com.android.internal.telephony.ISub;
35import com.android.internal.telephony.IOnSubscriptionsChangedListener;
36import com.android.internal.telephony.ITelephonyRegistry;
37import com.android.internal.telephony.PhoneConstants;
38import java.util.ArrayList;
39import java.util.List;
40
41/**
42 * SubscriptionManager is the application interface to SubscriptionController
43 * and provides information about the current Telephony Subscriptions.
44 * * <p>
45 * You do not instantiate this class directly; instead, you retrieve
46 * a reference to an instance through {@link #from}.
47 * <p>
48 * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE.
49 */
50public class SubscriptionManager {
51    private static final String LOG_TAG = "SubscriptionManager";
52    private static final boolean DBG = false;
53    private static final boolean VDBG = false;
54
55    /** An invalid subscription identifier */
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 Phone 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    /** @hide */
239    public static final int SIM_PROVISIONED = 0;
240
241    /**
242     * TelephonyProvider column name for the MCC associated with a SIM.
243     * <P>Type: INTEGER (int)</P>
244     * @hide
245     */
246    public static final String MCC = "mcc";
247
248    /**
249     * TelephonyProvider column name for the MNC associated with a SIM.
250     * <P>Type: INTEGER (int)</P>
251     * @hide
252     */
253    public static final String MNC = "mnc";
254
255    /**
256     * TelephonyProvider column name for the sim provisioning status associated with a SIM.
257     * <P>Type: INTEGER (int)</P>
258     * @hide
259     */
260    public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
261
262    /**
263     *  TelephonyProvider column name for extreme threat in CB settings
264     * @hide
265     */
266    public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
267
268    /**
269     * TelephonyProvider column name for severe threat in CB settings
270     *@hide
271     */
272    public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
273
274    /**
275     * TelephonyProvider column name for amber alert in CB settings
276     *@hide
277     */
278    public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
279
280    /**
281     * TelephonyProvider column name for emergency alert in CB settings
282     *@hide
283     */
284    public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
285
286    /**
287     * TelephonyProvider column name for alert sound duration in CB settings
288     *@hide
289     */
290    public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
291
292    /**
293     * TelephonyProvider column name for alert reminder interval in CB settings
294     *@hide
295     */
296    public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
297
298    /**
299     * TelephonyProvider column name for enabling vibrate in CB settings
300     *@hide
301     */
302    public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
303
304    /**
305     * TelephonyProvider column name for enabling alert speech in CB settings
306     *@hide
307     */
308    public static final String CB_ALERT_SPEECH = "enable_alert_speech";
309
310    /**
311     * TelephonyProvider column name for ETWS test alert in CB settings
312     *@hide
313     */
314    public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
315
316    /**
317     * TelephonyProvider column name for enable channel50 alert in CB settings
318     *@hide
319     */
320    public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
321
322    /**
323     * TelephonyProvider column name for CMAS test alert in CB settings
324     *@hide
325     */
326    public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts";
327
328    /**
329     * TelephonyProvider column name for Opt out dialog in CB settings
330     *@hide
331     */
332    public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
333
334    /**
335     * Broadcast Action: The user has changed one of the default subs related to
336     * data, phone calls, or sms</p>
337     *
338     * TODO: Change to a listener
339     * @hide
340     */
341    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
342    public static final String SUB_DEFAULT_CHANGED_ACTION =
343        "android.intent.action.SUB_DEFAULT_CHANGED";
344
345    /**
346     * Broadcast Action: The default subscription has changed.  This has the following
347     * extra values:</p>
348     * The {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default subscription index
349     */
350    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
351    public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED
352            = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
353
354    /**
355     * Broadcast Action: The default sms subscription has changed.  This has the following
356     * extra values:</p>
357     * {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default sms
358     * subscription index
359     */
360    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
361    public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED
362            = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
363
364    /**
365     * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and
366     * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription
367     * which has changed.
368     */
369    public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
370
371    private final Context mContext;
372
373    /**
374     * A listener class for monitoring changes to {@link SubscriptionInfo} records.
375     * <p>
376     * Override the onSubscriptionsChanged method in the object that extends this
377     * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
378     * to register your listener and to unregister invoke
379     * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
380     * <p>
381     * Permissions android.Manifest.permission.READ_PHONE_STATE is required
382     * for #onSubscriptionsChanged to be invoked.
383     */
384    public static class OnSubscriptionsChangedListener {
385        private final Handler mHandler  = new Handler() {
386            @Override
387            public void handleMessage(Message msg) {
388                if (DBG) {
389                    log("handleMessage: invoke the overriden onSubscriptionsChanged()");
390                }
391                OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
392            }
393        };
394
395        /**
396         * Callback invoked when there is any change to any SubscriptionInfo. Typically
397         * this method would invoke {@link #getActiveSubscriptionInfoList}
398         */
399        public void onSubscriptionsChanged() {
400            if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
401        }
402
403        /**
404         * The callback methods need to be called on the handler thread where
405         * this object was created.  If the binder did that for us it'd be nice.
406         */
407        IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
408            @Override
409            public void onSubscriptionsChanged() {
410                if (DBG) log("callback: received, sendEmptyMessage(0) to handler");
411                mHandler.sendEmptyMessage(0);
412            }
413        };
414
415        private void log(String s) {
416            Rlog.d(LOG_TAG, s);
417        }
418    }
419
420    /** @hide */
421    public SubscriptionManager(Context context) {
422        if (DBG) logd("SubscriptionManager created");
423        mContext = context;
424    }
425
426    /**
427     * Get an instance of the SubscriptionManager from the Context.
428     * This invokes {@link android.content.Context#getSystemService
429     * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}.
430     *
431     * @param context to use.
432     * @return SubscriptionManager instance
433     */
434    public static SubscriptionManager from(Context context) {
435        return (SubscriptionManager) context.getSystemService(
436                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
437    }
438
439    /**
440     * Register for changes to the list of active {@link SubscriptionInfo} records or to the
441     * individual records themselves. When a change occurs the onSubscriptionsChanged method of
442     * the listener will be invoked immediately if there has been a notification.
443     *
444     * @param listener an instance of {@link OnSubscriptionsChangedListener} with
445     *                 onSubscriptionsChanged overridden.
446     */
447    public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
448        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
449        if (DBG) {
450            logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
451                    + " listener=" + listener);
452        }
453        try {
454            // We use the TelephonyRegistry as it runs in the system and thus is always
455            // available. Where as SubscriptionController could crash and not be available
456            ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
457                    "telephony.registry"));
458            if (tr != null) {
459                tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
460            }
461        } catch (RemoteException ex) {
462            // Should not happen
463        }
464    }
465
466    /**
467     * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary
468     * as the listener will automatically be unregistered if an attempt to invoke the listener
469     * fails.
470     *
471     * @param listener that is to be unregistered.
472     */
473    public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
474        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
475        if (DBG) {
476            logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
477                    + " listener=" + listener);
478        }
479        try {
480            // We use the TelephonyRegistry as its runs in the system and thus is always
481            // available where as SubscriptionController could crash and not be available
482            ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
483                    "telephony.registry"));
484            if (tr != null) {
485                tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
486            }
487        } catch (RemoteException ex) {
488            // Should not happen
489        }
490    }
491
492    /**
493     * Get the active SubscriptionInfo with the input subId.
494     *
495     * @param subId The unique SubscriptionInfo key in database.
496     * @return SubscriptionInfo, maybe null if its not active.
497     */
498    public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
499        if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
500        if (!isValidSubscriptionId(subId)) {
501            if (DBG) {
502                logd("[getActiveSubscriptionInfo]- invalid subId");
503            }
504            return null;
505        }
506
507        SubscriptionInfo subInfo = null;
508
509        try {
510            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
511            if (iSub != null) {
512                subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName());
513            }
514        } catch (RemoteException ex) {
515            // ignore it
516        }
517
518        return subInfo;
519
520    }
521
522    /**
523     * Get the active SubscriptionInfo associated with the iccId
524     * @param iccId the IccId of SIM card
525     * @return SubscriptionInfo, maybe null if its not active
526     * @hide
527     */
528    public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) {
529        if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId);
530        if (iccId == null) {
531            logd("[getActiveSubscriptionInfoForIccIndex]- null iccid");
532            return null;
533        }
534
535        SubscriptionInfo result = null;
536
537        try {
538            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
539            if (iSub != null) {
540                result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName());
541            }
542        } catch (RemoteException ex) {
543            // ignore it
544        }
545
546        return result;
547    }
548
549    /**
550     * Get the active SubscriptionInfo associated with the slotIndex
551     * @param slotIndex the slot which the subscription is inserted
552     * @return SubscriptionInfo, maybe null if its not active
553     */
554    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
555        if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
556        if (!isValidSlotIndex(slotIndex)) {
557            logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIndex");
558            return null;
559        }
560
561        SubscriptionInfo result = null;
562
563        try {
564            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
565            if (iSub != null) {
566                result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
567                        mContext.getOpPackageName());
568            }
569        } catch (RemoteException ex) {
570            // ignore it
571        }
572
573        return result;
574    }
575
576    /**
577     * @return List of all SubscriptionInfo records in database,
578     * include those that were inserted before, maybe empty but not null.
579     * @hide
580     */
581    public List<SubscriptionInfo> getAllSubscriptionInfoList() {
582        if (VDBG) logd("[getAllSubscriptionInfoList]+");
583
584        List<SubscriptionInfo> result = null;
585
586        try {
587            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
588            if (iSub != null) {
589                result = iSub.getAllSubInfoList(mContext.getOpPackageName());
590            }
591        } catch (RemoteException ex) {
592            // ignore it
593        }
594
595        if (result == null) {
596            result = new ArrayList<SubscriptionInfo>();
597        }
598        return result;
599    }
600
601    /**
602     * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
603     * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
604     *
605     * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
606     * <ul>
607     * <li>
608     * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener}
609     * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be
610     * invoked in the future.
611     * </li>
612     * <li>
613     * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
614     * </li>
615     * <li>
616     * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
617     * then by {@link SubscriptionInfo#getSubscriptionId}.
618     * </li>
619     * </ul>
620     */
621    public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
622        List<SubscriptionInfo> result = null;
623
624        try {
625            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
626            if (iSub != null) {
627                result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
628            }
629        } catch (RemoteException ex) {
630            // ignore it
631        }
632        return result;
633    }
634
635    /**
636     * @return the count of all subscriptions in the database, this includes
637     * all subscriptions that have been seen.
638     * @hide
639     */
640    public int getAllSubscriptionInfoCount() {
641        if (VDBG) logd("[getAllSubscriptionInfoCount]+");
642
643        int result = 0;
644
645        try {
646            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
647            if (iSub != null) {
648                result = iSub.getAllSubInfoCount(mContext.getOpPackageName());
649            }
650        } catch (RemoteException ex) {
651            // ignore it
652        }
653
654        return result;
655    }
656
657    /**
658     * @return the current number of active subscriptions. There is no guarantee the value
659     * returned by this method will be the same as the length of the list returned by
660     * {@link #getActiveSubscriptionInfoList}.
661     */
662    public int getActiveSubscriptionInfoCount() {
663        int result = 0;
664
665        try {
666            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
667            if (iSub != null) {
668                result = iSub.getActiveSubInfoCount(mContext.getOpPackageName());
669            }
670        } catch (RemoteException ex) {
671            // ignore it
672        }
673
674        return result;
675    }
676
677    /**
678     * @return the maximum number of active subscriptions that will be returned by
679     * {@link #getActiveSubscriptionInfoList} and the value returned by
680     * {@link #getActiveSubscriptionInfoCount}.
681     */
682    public int getActiveSubscriptionInfoCountMax() {
683        int result = 0;
684
685        try {
686            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
687            if (iSub != null) {
688                result = iSub.getActiveSubInfoCountMax();
689            }
690        } catch (RemoteException ex) {
691            // ignore it
692        }
693
694        return result;
695    }
696
697    /**
698     * Add a new SubscriptionInfo to SubscriptionInfo database if needed
699     * @param iccId the IccId of the SIM card
700     * @param slotIndex the slot which the SIM is inserted
701     * @return the URL of the newly created row or the updated row
702     * @hide
703     */
704    public Uri addSubscriptionInfoRecord(String iccId, int slotIndex) {
705        if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotIndex:" + slotIndex);
706        if (iccId == null) {
707            logd("[addSubscriptionInfoRecord]- null iccId");
708        }
709        if (!isValidSlotIndex(slotIndex)) {
710            logd("[addSubscriptionInfoRecord]- invalid slotIndex");
711        }
712
713        try {
714            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
715            if (iSub != null) {
716                // FIXME: This returns 1 on success, 0 on error should should we return it?
717                iSub.addSubInfoRecord(iccId, slotIndex);
718            }
719        } catch (RemoteException ex) {
720            // ignore it
721        }
722
723        // FIXME: Always returns null?
724        return null;
725
726    }
727
728    /**
729     * Set SIM icon tint color by simInfo index
730     * @param tint the RGB value of icon tint color of the SIM
731     * @param subId the unique SubInfoRecord index in database
732     * @return the number of records updated
733     * @hide
734     */
735    public int setIconTint(int tint, int subId) {
736        if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
737        if (!isValidSubscriptionId(subId)) {
738            logd("[setIconTint]- fail");
739            return -1;
740        }
741
742        int result = 0;
743
744        try {
745            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
746            if (iSub != null) {
747                result = iSub.setIconTint(tint, subId);
748            }
749        } catch (RemoteException ex) {
750            // ignore it
751        }
752
753        return result;
754
755    }
756
757    /**
758     * Set display name by simInfo index
759     * @param displayName the display name of SIM card
760     * @param subId the unique SubscriptionInfo index in database
761     * @return the number of records updated
762     * @hide
763     */
764    public int setDisplayName(String displayName, int subId) {
765        return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED);
766    }
767
768    /**
769     * Set display name by simInfo index with name source
770     * @param displayName the display name of SIM card
771     * @param subId the unique SubscriptionInfo index in database
772     * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
773     *                   2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
774     * @return the number of records updated or < 0 if invalid subId
775     * @hide
776     */
777    public int setDisplayName(String displayName, int subId, long nameSource) {
778        if (VDBG) {
779            logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
780                    + " nameSource:" + nameSource);
781        }
782        if (!isValidSubscriptionId(subId)) {
783            logd("[setDisplayName]- fail");
784            return -1;
785        }
786
787        int result = 0;
788
789        try {
790            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
791            if (iSub != null) {
792                result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource);
793            }
794        } catch (RemoteException ex) {
795            // ignore it
796        }
797
798        return result;
799
800    }
801
802    /**
803     * Set phone number by subId
804     * @param number the phone number of the SIM
805     * @param subId the unique SubscriptionInfo index in database
806     * @return the number of records updated
807     * @hide
808     */
809    public int setDisplayNumber(String number, int subId) {
810        if (number == null || !isValidSubscriptionId(subId)) {
811            logd("[setDisplayNumber]- fail");
812            return -1;
813        }
814
815        int result = 0;
816
817        try {
818            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
819            if (iSub != null) {
820                result = iSub.setDisplayNumber(number, subId);
821            }
822        } catch (RemoteException ex) {
823            // ignore it
824        }
825
826        return result;
827
828    }
829
830    /**
831     * Set data roaming by simInfo index
832     * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
833     * @param subId the unique SubscriptionInfo index in database
834     * @return the number of records updated
835     * @hide
836     */
837    public int setDataRoaming(int roaming, int subId) {
838        if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
839        if (roaming < 0 || !isValidSubscriptionId(subId)) {
840            logd("[setDataRoaming]- fail");
841            return -1;
842        }
843
844        int result = 0;
845
846        try {
847            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
848            if (iSub != null) {
849                result = iSub.setDataRoaming(roaming, subId);
850            }
851        } catch (RemoteException ex) {
852            // ignore it
853        }
854
855        return result;
856    }
857
858    /**
859     * Get slotIndex associated with the subscription.
860     * @return slotIndex as a positive integer or a negative value if an error either
861     * SIM_NOT_INSERTED or < 0 if an invalid slot index
862     * @hide
863     */
864    public static int getSlotIndex(int subId) {
865        if (!isValidSubscriptionId(subId)) {
866            if (DBG) {
867                logd("[getSlotIndex]- fail");
868            }
869        }
870
871        int result = INVALID_SIM_SLOT_INDEX;
872
873        try {
874            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
875            if (iSub != null) {
876                result = iSub.getSlotIndex(subId);
877            }
878        } catch (RemoteException ex) {
879            // ignore it
880        }
881
882        return result;
883
884    }
885
886    /** @hide */
887    public static int[] getSubId(int slotIndex) {
888        if (!isValidSlotIndex(slotIndex)) {
889            logd("[getSubId]- fail");
890            return null;
891        }
892
893        int[] subId = null;
894
895        try {
896            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
897            if (iSub != null) {
898                subId = iSub.getSubId(slotIndex);
899            }
900        } catch (RemoteException ex) {
901            // ignore it
902        }
903
904        return subId;
905    }
906
907    /** @hide */
908    public static int getPhoneId(int subId) {
909        if (!isValidSubscriptionId(subId)) {
910            if (DBG) {
911                logd("[getPhoneId]- fail");
912            }
913            return INVALID_PHONE_INDEX;
914        }
915
916        int result = INVALID_PHONE_INDEX;
917
918        try {
919            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
920            if (iSub != null) {
921                result = iSub.getPhoneId(subId);
922            }
923        } catch (RemoteException ex) {
924            // ignore it
925        }
926
927        if (VDBG) logd("[getPhoneId]- phoneId=" + result);
928        return result;
929
930    }
931
932    private static void logd(String msg) {
933        Rlog.d(LOG_TAG, msg);
934    }
935
936    /**
937     * Returns the system's default subscription id.
938     *
939     * For a voice capable device, it will return getDefaultVoiceSubscriptionId.
940     * For a data only device, it will return the getDefaultDataSubscriptionId.
941     * May return an INVALID_SUBSCRIPTION_ID on error.
942     *
943     * @return the "system" default subscription id.
944     */
945    public static int getDefaultSubscriptionId() {
946        int subId = INVALID_SUBSCRIPTION_ID;
947
948        try {
949            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
950            if (iSub != null) {
951                subId = iSub.getDefaultSubId();
952            }
953        } catch (RemoteException ex) {
954            // ignore it
955        }
956
957        if (VDBG) logd("getDefaultSubId=" + subId);
958        return subId;
959    }
960
961    /**
962     * Returns the system's default voice subscription id.
963     *
964     * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
965     *
966     * @return the default voice subscription Id.
967     */
968    public static int getDefaultVoiceSubscriptionId() {
969        int subId = INVALID_SUBSCRIPTION_ID;
970
971        try {
972            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
973            if (iSub != null) {
974                subId = iSub.getDefaultVoiceSubId();
975            }
976        } catch (RemoteException ex) {
977            // ignore it
978        }
979
980        if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
981        return subId;
982    }
983
984    /** @hide */
985    public void setDefaultVoiceSubId(int subId) {
986        if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId);
987        try {
988            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
989            if (iSub != null) {
990                iSub.setDefaultVoiceSubId(subId);
991            }
992        } catch (RemoteException ex) {
993            // ignore it
994        }
995    }
996
997    /**
998     * Return the SubscriptionInfo for default voice subscription.
999     *
1000     * Will return null on data only devices, or on error.
1001     *
1002     * @return the SubscriptionInfo for the default voice subscription.
1003     * @hide
1004     */
1005    public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
1006        return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId());
1007    }
1008
1009    /** @hide */
1010    public static int getDefaultVoicePhoneId() {
1011        return getPhoneId(getDefaultVoiceSubscriptionId());
1012    }
1013
1014    /**
1015     * Returns the system's default SMS subscription id.
1016     *
1017     * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
1018     *
1019     * @return the default SMS subscription Id.
1020     */
1021    public static int getDefaultSmsSubscriptionId() {
1022        int subId = INVALID_SUBSCRIPTION_ID;
1023
1024        try {
1025            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1026            if (iSub != null) {
1027                subId = iSub.getDefaultSmsSubId();
1028            }
1029        } catch (RemoteException ex) {
1030            // ignore it
1031        }
1032
1033        if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId);
1034        return subId;
1035    }
1036
1037    /** @hide */
1038    public void setDefaultSmsSubId(int subId) {
1039        if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
1040        try {
1041            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1042            if (iSub != null) {
1043                iSub.setDefaultSmsSubId(subId);
1044            }
1045        } catch (RemoteException ex) {
1046            // ignore it
1047        }
1048    }
1049
1050    /**
1051     * Return the SubscriptionInfo for default voice subscription.
1052     *
1053     * Will return null on data only devices, or on error.
1054     *
1055     * @return the SubscriptionInfo for the default SMS subscription.
1056     * @hide
1057     */
1058    public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
1059        return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId());
1060    }
1061
1062    /** @hide */
1063    public int getDefaultSmsPhoneId() {
1064        return getPhoneId(getDefaultSmsSubscriptionId());
1065    }
1066
1067    /**
1068     * Returns the system's default data subscription id.
1069     *
1070     * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID.
1071     *
1072     * @return the default data subscription Id.
1073     */
1074    public static int getDefaultDataSubscriptionId() {
1075        int subId = INVALID_SUBSCRIPTION_ID;
1076
1077        try {
1078            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1079            if (iSub != null) {
1080                subId = iSub.getDefaultDataSubId();
1081            }
1082        } catch (RemoteException ex) {
1083            // ignore it
1084        }
1085
1086        if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId);
1087        return subId;
1088    }
1089
1090    /** @hide */
1091    public void setDefaultDataSubId(int subId) {
1092        if (VDBG) logd("setDataSubscription sub id = " + subId);
1093        try {
1094            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1095            if (iSub != null) {
1096                iSub.setDefaultDataSubId(subId);
1097            }
1098        } catch (RemoteException ex) {
1099            // ignore it
1100        }
1101    }
1102
1103    /**
1104     * Return the SubscriptionInfo for default data subscription.
1105     *
1106     * Will return null on voice only devices, or on error.
1107     *
1108     * @return the SubscriptionInfo for the default data subscription.
1109     * @hide
1110     */
1111    public SubscriptionInfo getDefaultDataSubscriptionInfo() {
1112        return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
1113    }
1114
1115    /** @hide */
1116    public int getDefaultDataPhoneId() {
1117        return getPhoneId(getDefaultDataSubscriptionId());
1118    }
1119
1120    /** @hide */
1121    public void clearSubscriptionInfo() {
1122        try {
1123            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1124            if (iSub != null) {
1125                iSub.clearSubInfo();
1126            }
1127        } catch (RemoteException ex) {
1128            // ignore it
1129        }
1130
1131        return;
1132    }
1133
1134    //FIXME this is vulnerable to race conditions
1135    /** @hide */
1136    public boolean allDefaultsSelected() {
1137        if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) {
1138            return false;
1139        }
1140        if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) {
1141            return false;
1142        }
1143        if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) {
1144            return false;
1145        }
1146        return true;
1147    }
1148
1149    /**
1150     * If a default is set to subscription which is not active, this will reset that default back to
1151     * an invalid subscription id, i.e. < 0.
1152     * @hide
1153     */
1154    public void clearDefaultsForInactiveSubIds() {
1155        if (VDBG) logd("clearDefaultsForInactiveSubIds");
1156        try {
1157            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1158            if (iSub != null) {
1159                iSub.clearDefaultsForInactiveSubIds();
1160            }
1161        } catch (RemoteException ex) {
1162            // ignore it
1163        }
1164    }
1165
1166    /**
1167     * @return true if a valid subId else false
1168     * @hide
1169     */
1170    public static boolean isValidSubscriptionId(int subId) {
1171        return subId > INVALID_SUBSCRIPTION_ID ;
1172    }
1173
1174    /**
1175     * @return true if subId is an usable subId value else false. A
1176     * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
1177     * @hide
1178     */
1179    public static boolean isUsableSubIdValue(int subId) {
1180        return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
1181    }
1182
1183    /** @hide */
1184    public static boolean isValidSlotIndex(int slotIndex) {
1185        return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSimCount();
1186    }
1187
1188    /** @hide */
1189    public static boolean isValidPhoneId(int phoneId) {
1190        return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
1191    }
1192
1193    /** @hide */
1194    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
1195        int[] subIds = SubscriptionManager.getSubId(phoneId);
1196        if (subIds != null && subIds.length > 0) {
1197            putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]);
1198        } else {
1199            logd("putPhoneIdAndSubIdExtra: no valid subs");
1200        }
1201    }
1202
1203    /** @hide */
1204    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) {
1205        if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
1206        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1207        intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
1208        intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
1209        //FIXME this is using phoneId and slotIndex interchangeably
1210        //Eventually, this should be removed as it is not the slot id
1211        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
1212    }
1213
1214    /**
1215     * @return the list of subId's that are active,
1216     *         is never null but the length maybe 0.
1217     * @hide
1218     */
1219    public @NonNull int[] getActiveSubscriptionIdList() {
1220        int[] subId = null;
1221
1222        try {
1223            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1224            if (iSub != null) {
1225                subId = iSub.getActiveSubIdList();
1226            }
1227        } catch (RemoteException ex) {
1228            // ignore it
1229        }
1230
1231        if (subId == null) {
1232            subId = new int[0];
1233        }
1234
1235        return subId;
1236
1237    }
1238
1239    /**
1240     * Returns true if the device is considered roaming on the current
1241     * network for a subscription.
1242     * <p>
1243     * Availability: Only when user registered to a network.
1244     *
1245     * @param subId The subscription ID
1246     * @return true if the network for the subscription is roaming, false otherwise
1247     */
1248    public boolean isNetworkRoaming(int subId) {
1249        final int phoneId = getPhoneId(subId);
1250        if (phoneId < 0) {
1251            // What else can we do?
1252            return false;
1253        }
1254        return TelephonyManager.getDefault().isNetworkRoaming(subId);
1255    }
1256
1257    /**
1258     * Returns a constant indicating the state of sim for the slot index.
1259     *
1260     * @param slotIndex
1261     *
1262     * {@See TelephonyManager#SIM_STATE_UNKNOWN}
1263     * {@See TelephonyManager#SIM_STATE_ABSENT}
1264     * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED}
1265     * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED}
1266     * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED}
1267     * {@See TelephonyManager#SIM_STATE_READY}
1268     * {@See TelephonyManager#SIM_STATE_NOT_READY}
1269     * {@See TelephonyManager#SIM_STATE_PERM_DISABLED}
1270     * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR}
1271     *
1272     * {@hide}
1273     */
1274    public static int getSimStateForSlotIndex(int slotIndex) {
1275        int simState = TelephonyManager.SIM_STATE_UNKNOWN;
1276
1277        try {
1278            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1279            if (iSub != null) {
1280                simState = iSub.getSimStateForSlotIndex(slotIndex);
1281            }
1282        } catch (RemoteException ex) {
1283        }
1284
1285        return simState;
1286    }
1287
1288    /**
1289     * Store properties associated with SubscriptionInfo in database
1290     * @param subId Subscription Id of Subscription
1291     * @param propKey Column name in database associated with SubscriptionInfo
1292     * @param propValue Value to store in DB for particular subId & column name
1293     * @hide
1294     */
1295    public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
1296        try {
1297            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1298            if (iSub != null) {
1299                iSub.setSubscriptionProperty(subId, propKey, propValue);
1300            }
1301        } catch (RemoteException ex) {
1302            // ignore it
1303        }
1304    }
1305
1306    /**
1307     * Store properties associated with SubscriptionInfo in database
1308     * @param subId Subscription Id of Subscription
1309     * @param propKey Column name in SubscriptionInfo database
1310     * @return Value associated with subId and propKey column in database
1311     * @hide
1312     */
1313    private static String getSubscriptionProperty(int subId, String propKey,
1314            Context context) {
1315        String resultValue = null;
1316        try {
1317            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1318            if (iSub != null) {
1319                resultValue = iSub.getSubscriptionProperty(subId, propKey,
1320                    context.getOpPackageName());
1321            }
1322        } catch (RemoteException ex) {
1323            // ignore it
1324        }
1325        return resultValue;
1326    }
1327
1328    /**
1329     * Returns boolean value corresponding to query result.
1330     * @param subId Subscription Id of Subscription
1331     * @param propKey Column name in SubscriptionInfo database
1332     * @param defValue Default boolean value to be returned
1333     * @return boolean result value to be returned
1334     * @hide
1335     */
1336    public static boolean getBooleanSubscriptionProperty(int subId, String propKey,
1337            boolean defValue, Context context) {
1338        String result = getSubscriptionProperty(subId, propKey, context);
1339        if (result != null) {
1340            try {
1341                return Integer.parseInt(result) == 1;
1342            } catch (NumberFormatException err) {
1343                logd("getBooleanSubscriptionProperty NumberFormat exception");
1344            }
1345        }
1346        return defValue;
1347    }
1348
1349    /**
1350     * Returns integer value corresponding to query result.
1351     * @param subId Subscription Id of Subscription
1352     * @param propKey Column name in SubscriptionInfo database
1353     * @param defValue Default integer value to be returned
1354     * @return integer result value to be returned
1355     * @hide
1356     */
1357    public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue,
1358            Context context) {
1359        String result = getSubscriptionProperty(subId, propKey, context);
1360        if (result != null) {
1361            try {
1362                return Integer.parseInt(result);
1363            } catch (NumberFormatException err) {
1364                logd("getBooleanSubscriptionProperty NumberFormat exception");
1365            }
1366        }
1367        return defValue;
1368    }
1369
1370    /**
1371     * Returns the resources associated with Subscription.
1372     * @param context Context object
1373     * @param subId Subscription Id of Subscription who's resources are required
1374     * @return Resources associated with Subscription.
1375     * @hide
1376     */
1377    public static Resources getResourcesForSubId(Context context, int subId) {
1378        final SubscriptionInfo subInfo =
1379                SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
1380
1381        Configuration config = context.getResources().getConfiguration();
1382        Configuration newConfig = new Configuration();
1383        newConfig.setTo(config);
1384        if (subInfo != null) {
1385            newConfig.mcc = subInfo.getMcc();
1386            newConfig.mnc = subInfo.getMnc();
1387            if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
1388        }
1389        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
1390        DisplayMetrics newMetrics = new DisplayMetrics();
1391        newMetrics.setTo(metrics);
1392        return new Resources(context.getResources().getAssets(), newMetrics, newConfig);
1393    }
1394
1395    /**
1396     * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
1397     * and the SIM providing the subscription is present in a slot and in "LOADED" state.
1398     * @hide
1399     */
1400    public boolean isActiveSubId(int subId) {
1401        try {
1402            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1403            if (iSub != null) {
1404                return iSub.isActiveSubId(subId);
1405            }
1406        } catch (RemoteException ex) {
1407        }
1408        return false;
1409    }
1410}
1411