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