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