OSDN Git Service

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