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