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