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