2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.telephony;
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;
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;
42 * SubscriptionManager is the application interface to SubscriptionController
43 * and provides information about the current Telephony Subscriptions.
45 * You do not instantiate this class directly; instead, you retrieve
46 * a reference to an instance through {@link #from}.
48 * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE.
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;
55 /** An invalid subscription identifier */
56 public static final int INVALID_SUBSCRIPTION_ID = -1;
58 /** Base value for Dummy SUBSCRIPTION_ID's. */
59 /** FIXME: Remove DummySubId's, but for now have them map just below INVALID_SUBSCRIPTION_ID
61 public static final int DUMMY_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1;
63 /** An invalid phone identifier */
65 public static final int INVALID_PHONE_INDEX = -1;
67 /** An invalid slot identifier */
69 public static final int INVALID_SIM_SLOT_INDEX = -1;
71 /** Indicates the caller wants the default sub id. */
73 public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE;
76 * Indicates the caller wants the default phone id.
77 * Used in SubscriptionController and Phone but do we really need it???
80 public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE;
82 /** Indicates the caller wants the default slot id. NOT used remove? */
84 public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE;
86 /** Minimum possible subid that represents a subscription */
88 public static final int MIN_SUBSCRIPTION_ID_VALUE = 0;
90 /** Maximum possible subid that represents a subscription */
92 public static final int MAX_SUBSCRIPTION_ID_VALUE = DEFAULT_SUBSCRIPTION_ID - 1;
95 public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
98 * TelephonyProvider unique key column name is the subscription id.
99 * <P>Type: TEXT (String)</P>
102 public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
105 * TelephonyProvider column name for SIM ICC Identifier
106 * <P>Type: TEXT (String)</P>
109 public static final String ICC_ID = "icc_id";
112 * TelephonyProvider column name for user SIM_SlOT_INDEX
113 * <P>Type: INTEGER (int)</P>
116 public static final String SIM_SLOT_INDEX = "sim_id";
118 /** SIM is not inserted */
120 public static final int SIM_NOT_INSERTED = -1;
123 * TelephonyProvider column name for user displayed name.
124 * <P>Type: TEXT (String)</P>
127 public static final String DISPLAY_NAME = "display_name";
130 * TelephonyProvider column name for the service provider name for the SIM.
131 * <P>Type: TEXT (String)</P>
134 public static final String CARRIER_NAME = "carrier_name";
137 * Default name resource
140 public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName;
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
148 public static final String NAME_SOURCE = "name_source";
151 * The name_source is undefined
154 public static final int NAME_SOURCE_UNDEFINDED = -1;
157 * The name_source is the default
160 public static final int NAME_SOURCE_DEFAULT_SOURCE = 0;
163 * The name_source is from the SIM
166 public static final int NAME_SOURCE_SIM_SOURCE = 1;
169 * The name_source is from the user
172 public static final int NAME_SOURCE_USER_INPUT = 2;
175 * TelephonyProvider column name for the color of a SIM.
176 * <P>Type: INTEGER (int)</P>
179 public static final String COLOR = "color";
182 public static final int COLOR_1 = 0;
185 public static final int COLOR_2 = 1;
188 public static final int COLOR_3 = 2;
191 public static final int COLOR_4 = 3;
194 public static final int COLOR_DEFAULT = COLOR_1;
197 * TelephonyProvider column name for the phone number of a SIM.
198 * <P>Type: TEXT (String)</P>
201 public static final String NUMBER = "number";
204 * TelephonyProvider column name for the number display format of a SIM.
205 * <P>Type: INTEGER (int)</P>
208 public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
211 public static final int DISPLAY_NUMBER_NONE = 0;
214 public static final int DISPLAY_NUMBER_FIRST = 1;
217 public static final int DISPLAY_NUMBER_LAST = 2;
220 public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST;
223 * TelephonyProvider column name for permission for data roaming of a SIM.
224 * <P>Type: INTEGER (int)</P>
227 public static final String DATA_ROAMING = "data_roaming";
229 /** Indicates that data roaming is enabled for a subscription */
230 public static final int DATA_ROAMING_ENABLE = 1;
232 /** Indicates that data roaming is disabled for a subscription */
233 public static final int DATA_ROAMING_DISABLE = 0;
235 /** Sim provisioning status: provisioned */
237 public static final int SIM_PROVISIONED = 0;
239 /** Sim provisioning status: un-provisioned due to cold sim */
241 public static final int SIM_UNPROVISIONED_COLD = 1;
243 /** Sim provisioning status: un-provisioned due to out of credit */
245 public static final int SIM_UNPROVISIONED_OUT_OF_CREDIT = 2;
247 /** Maximum possible sim provisioning status */
249 public static final int MAX_SIM_PROVISIONING_STATUS = SIM_UNPROVISIONED_OUT_OF_CREDIT;
252 public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
255 * TelephonyProvider column name for the MCC associated with a SIM.
256 * <P>Type: INTEGER (int)</P>
259 public static final String MCC = "mcc";
262 * TelephonyProvider column name for the MNC associated with a SIM.
263 * <P>Type: INTEGER (int)</P>
266 public static final String MNC = "mnc";
269 * TelephonyProvider column name for the sim provisioning status associated with a SIM.
270 * <P>Type: INTEGER (int)</P>
273 public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status";
276 * TelephonyProvider column name for extreme threat in CB settings
279 public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
282 * TelephonyProvider column name for severe threat in CB settings
285 public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
288 * TelephonyProvider column name for amber alert in CB settings
291 public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
294 * TelephonyProvider column name for emergency alert in CB settings
297 public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
300 * TelephonyProvider column name for alert sound duration in CB settings
303 public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
306 * TelephonyProvider column name for alert reminder interval in CB settings
309 public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
312 * TelephonyProvider column name for enabling vibrate in CB settings
315 public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
318 * TelephonyProvider column name for enabling alert speech in CB settings
321 public static final String CB_ALERT_SPEECH = "enable_alert_speech";
324 * TelephonyProvider column name for ETWS test alert in CB settings
327 public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
330 * TelephonyProvider column name for enable channel50 alert in CB settings
333 public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
336 * TelephonyProvider column name for CMAS test alert in CB settings
339 public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts";
342 * TelephonyProvider column name for Opt out dialog in CB settings
345 public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
348 * Broadcast Action: The user has changed one of the default subs related to
349 * data, phone calls, or sms</p>
351 * TODO: Change to a listener
354 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
355 public static final String SUB_DEFAULT_CHANGED_ACTION =
356 "android.intent.action.SUB_DEFAULT_CHANGED";
358 private final Context mContext;
361 * A listener class for monitoring changes to {@link SubscriptionInfo} records.
363 * Override the onSubscriptionsChanged method in the object that extends this
364 * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
365 * to register your listener and to unregister invoke
366 * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
368 * Permissions android.Manifest.permission.READ_PHONE_STATE is required
369 * for #onSubscriptionsChanged to be invoked.
371 public static class OnSubscriptionsChangedListener {
372 private final Handler mHandler = new Handler() {
374 public void handleMessage(Message msg) {
376 log("handleMessage: invoke the overriden onSubscriptionsChanged()");
378 OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
383 * Callback invoked when there is any change to any SubscriptionInfo. Typically
384 * this method would invoke {@link #getActiveSubscriptionInfoList}
386 public void onSubscriptionsChanged() {
387 if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
391 * The callback methods need to be called on the handler thread where
392 * this object was created. If the binder did that for us it'd be nice.
394 IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
396 public void onSubscriptionsChanged() {
397 if (DBG) log("callback: received, sendEmptyMessage(0) to handler");
398 mHandler.sendEmptyMessage(0);
402 private void log(String s) {
408 public SubscriptionManager(Context context) {
409 if (DBG) logd("SubscriptionManager created");
414 * Get an instance of the SubscriptionManager from the Context.
415 * This invokes {@link android.content.Context#getSystemService
416 * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}.
418 * @param context to use.
419 * @return SubscriptionManager instance
421 public static SubscriptionManager from(Context context) {
422 return (SubscriptionManager) context.getSystemService(
423 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
427 * Register for changes to the list of active {@link SubscriptionInfo} records or to the
428 * individual records themselves. When a change occurs the onSubscriptionsChanged method of
429 * the listener will be invoked immediately if there has been a notification.
431 * @param listener an instance of {@link OnSubscriptionsChangedListener} with
432 * onSubscriptionsChanged overridden.
434 public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
435 String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
437 logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
438 + " listener=" + listener);
441 // We use the TelephonyRegistry as it runs in the system and thus is always
442 // available. Where as SubscriptionController could crash and not be available
443 ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
444 "telephony.registry"));
446 tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
448 } catch (RemoteException ex) {
454 * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary
455 * as the listener will automatically be unregistered if an attempt to invoke the listener
458 * @param listener that is to be unregistered.
460 public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
461 String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
463 logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
464 + " listener=" + listener);
467 // We use the TelephonyRegistry as its runs in the system and thus is always
468 // available where as SubscriptionController could crash and not be available
469 ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
470 "telephony.registry"));
472 tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
474 } catch (RemoteException ex) {
480 * Get the active SubscriptionInfo with the input subId.
482 * @param subId The unique SubscriptionInfo key in database.
483 * @return SubscriptionInfo, maybe null if its not active.
485 public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
486 if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
487 if (!isValidSubscriptionId(subId)) {
489 logd("[getActiveSubscriptionInfo]- invalid subId");
494 SubscriptionInfo subInfo = null;
497 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
499 subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName());
501 } catch (RemoteException ex) {
510 * Get the active SubscriptionInfo associated with the iccId
511 * @param iccId the IccId of SIM card
512 * @return SubscriptionInfo, maybe null if its not active
515 public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) {
516 if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId);
518 logd("[getActiveSubscriptionInfoForIccIndex]- null iccid");
522 SubscriptionInfo result = null;
525 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
527 result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName());
529 } catch (RemoteException ex) {
537 * Get the active SubscriptionInfo associated with the slotIdx
538 * @param slotIdx the slot which the subscription is inserted
539 * @return SubscriptionInfo, maybe null if its not active
541 public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx) {
542 if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx);
543 if (!isValidSlotId(slotIdx)) {
544 logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIdx");
548 SubscriptionInfo result = null;
551 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
553 result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIdx,
554 mContext.getOpPackageName());
556 } catch (RemoteException ex) {
564 * @return List of all SubscriptionInfo records in database,
565 * include those that were inserted before, maybe empty but not null.
568 public List<SubscriptionInfo> getAllSubscriptionInfoList() {
569 if (VDBG) logd("[getAllSubscriptionInfoList]+");
571 List<SubscriptionInfo> result = null;
574 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
576 result = iSub.getAllSubInfoList(mContext.getOpPackageName());
578 } catch (RemoteException ex) {
582 if (result == null) {
583 result = new ArrayList<SubscriptionInfo>();
589 * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
590 * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
592 * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
595 * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener}
596 * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be
597 * invoked in the future.
600 * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
603 * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
604 * then by {@link SubscriptionInfo#getSubscriptionId}.
608 public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
609 List<SubscriptionInfo> result = null;
612 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
614 result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
616 } catch (RemoteException ex) {
623 * @return the count of all subscriptions in the database, this includes
624 * all subscriptions that have been seen.
627 public int getAllSubscriptionInfoCount() {
628 if (VDBG) logd("[getAllSubscriptionInfoCount]+");
633 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
635 result = iSub.getAllSubInfoCount(mContext.getOpPackageName());
637 } catch (RemoteException ex) {
645 * @return the current number of active subscriptions. There is no guarantee the value
646 * returned by this method will be the same as the length of the list returned by
647 * {@link #getActiveSubscriptionInfoList}.
649 public int getActiveSubscriptionInfoCount() {
653 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
655 result = iSub.getActiveSubInfoCount(mContext.getOpPackageName());
657 } catch (RemoteException ex) {
665 * @return the maximum number of active subscriptions that will be returned by
666 * {@link #getActiveSubscriptionInfoList} and the value returned by
667 * {@link #getActiveSubscriptionInfoCount}.
669 public int getActiveSubscriptionInfoCountMax() {
673 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
675 result = iSub.getActiveSubInfoCountMax();
677 } catch (RemoteException ex) {
685 * Add a new SubscriptionInfo to SubscriptionInfo database if needed
686 * @param iccId the IccId of the SIM card
687 * @param slotId the slot which the SIM is inserted
688 * @return the URL of the newly created row or the updated row
691 public Uri addSubscriptionInfoRecord(String iccId, int slotId) {
692 if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
694 logd("[addSubscriptionInfoRecord]- null iccId");
696 if (!isValidSlotId(slotId)) {
697 logd("[addSubscriptionInfoRecord]- invalid slotId");
701 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
703 // FIXME: This returns 1 on success, 0 on error should should we return it?
704 iSub.addSubInfoRecord(iccId, slotId);
706 } catch (RemoteException ex) {
710 // FIXME: Always returns null?
716 * Set SIM icon tint color by simInfo index
717 * @param tint the RGB value of icon tint color of the SIM
718 * @param subId the unique SubInfoRecord index in database
719 * @return the number of records updated
722 public int setIconTint(int tint, int subId) {
723 if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
724 if (!isValidSubscriptionId(subId)) {
725 logd("[setIconTint]- fail");
732 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
734 result = iSub.setIconTint(tint, subId);
736 } catch (RemoteException ex) {
745 * Set display name by simInfo index
746 * @param displayName the display name of SIM card
747 * @param subId the unique SubscriptionInfo index in database
748 * @return the number of records updated
751 public int setDisplayName(String displayName, int subId) {
752 return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED);
756 * Set display name by simInfo index with name source
757 * @param displayName the display name of SIM card
758 * @param subId the unique SubscriptionInfo index in database
759 * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE,
760 * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED
761 * @return the number of records updated or < 0 if invalid subId
764 public int setDisplayName(String displayName, int subId, long nameSource) {
766 logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId
767 + " nameSource:" + nameSource);
769 if (!isValidSubscriptionId(subId)) {
770 logd("[setDisplayName]- fail");
777 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
779 result = iSub.setDisplayNameUsingSrc(displayName, subId, nameSource);
781 } catch (RemoteException ex) {
790 * Set phone number by subId
791 * @param number the phone number of the SIM
792 * @param subId the unique SubscriptionInfo index in database
793 * @return the number of records updated
796 public int setDisplayNumber(String number, int subId) {
797 if (number == null || !isValidSubscriptionId(subId)) {
798 logd("[setDisplayNumber]- fail");
805 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
807 result = iSub.setDisplayNumber(number, subId);
809 } catch (RemoteException ex) {
818 * Set data roaming by simInfo index
819 * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
820 * @param subId the unique SubscriptionInfo index in database
821 * @return the number of records updated
824 public int setDataRoaming(int roaming, int subId) {
825 if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
826 if (roaming < 0 || !isValidSubscriptionId(subId)) {
827 logd("[setDataRoaming]- fail");
834 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
836 result = iSub.setDataRoaming(roaming, subId);
838 } catch (RemoteException ex) {
846 * Set Sim Provisioning Status by subscription ID
847 * @param simProvisioningStatus with the subscription
848 * {@See SubscriptionManager#SIM_PROVISIONED}
849 * {@See SubscriptionManager#SIM_UNPROVISIONED_COLD}
850 * {@See SubscriptionManager#SIM_UNPROVISIONED_OUT_OF_CREDIT}
851 * @param subId the unique SubInfoRecord index in database
852 * @return the number of records updated
853 * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
856 public int setSimProvisioningStatus(int simProvisioningStatus, int subId) {
858 logd("[setSimProvisioningStatus]+ status:" + simProvisioningStatus + " subId:" + subId);
860 if (simProvisioningStatus < 0 || simProvisioningStatus > MAX_SIM_PROVISIONING_STATUS ||
861 !isValidSubscriptionId(subId)) {
862 logd("[setSimProvisioningStatus]- fail");
869 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
871 result = iSub.setSimProvisioningStatus(simProvisioningStatus, subId);
873 } catch (RemoteException ex) {
880 * Get slotId associated with the subscription.
881 * @return slotId as a positive integer or a negative value if an error either
882 * SIM_NOT_INSERTED or < 0 if an invalid slot index
885 public static int getSlotId(int subId) {
886 if (!isValidSubscriptionId(subId)) {
888 logd("[getSlotId]- fail");
892 int result = INVALID_SIM_SLOT_INDEX;
895 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
897 result = iSub.getSlotId(subId);
899 } catch (RemoteException ex) {
908 public static int[] getSubId(int slotId) {
909 if (!isValidSlotId(slotId)) {
910 logd("[getSubId]- fail");
917 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
919 subId = iSub.getSubId(slotId);
921 } catch (RemoteException ex) {
929 public static int getPhoneId(int subId) {
930 if (!isValidSubscriptionId(subId)) {
932 logd("[getPhoneId]- fail");
934 return INVALID_PHONE_INDEX;
937 int result = INVALID_PHONE_INDEX;
940 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
942 result = iSub.getPhoneId(subId);
944 } catch (RemoteException ex) {
948 if (VDBG) logd("[getPhoneId]- phoneId=" + result);
953 private static void logd(String msg) {
954 Rlog.d(LOG_TAG, msg);
958 * Returns the system's default subscription id.
960 * For a voice capable device, it will return getDefaultVoiceSubscriptionId.
961 * For a data only device, it will return the getDefaultDataSubscriptionId.
962 * May return an INVALID_SUBSCRIPTION_ID on error.
964 * @return the "system" default subscription id.
966 public static int getDefaultSubscriptionId() {
967 int subId = INVALID_SUBSCRIPTION_ID;
970 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
972 subId = iSub.getDefaultSubId();
974 } catch (RemoteException ex) {
978 if (VDBG) logd("getDefaultSubId=" + subId);
983 * Returns the system's default voice subscription id.
985 * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
987 * @return the default voice subscription Id.
989 public static int getDefaultVoiceSubscriptionId() {
990 int subId = INVALID_SUBSCRIPTION_ID;
993 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
995 subId = iSub.getDefaultVoiceSubId();
997 } catch (RemoteException ex) {
1001 if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
1006 public void setDefaultVoiceSubId(int subId) {
1007 if (VDBG) logd("setDefaultVoiceSubId sub id = " + subId);
1009 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1011 iSub.setDefaultVoiceSubId(subId);
1013 } catch (RemoteException ex) {
1019 * Return the SubscriptionInfo for default voice subscription.
1021 * Will return null on data only devices, or on error.
1023 * @return the SubscriptionInfo for the default voice subscription.
1026 public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
1027 return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId());
1031 public static int getDefaultVoicePhoneId() {
1032 return getPhoneId(getDefaultVoiceSubscriptionId());
1036 * Returns the system's default SMS subscription id.
1038 * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
1040 * @return the default SMS subscription Id.
1042 public static int getDefaultSmsSubscriptionId() {
1043 int subId = INVALID_SUBSCRIPTION_ID;
1046 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1048 subId = iSub.getDefaultSmsSubId();
1050 } catch (RemoteException ex) {
1054 if (VDBG) logd("getDefaultSmsSubscriptionId, sub id = " + subId);
1059 public void setDefaultSmsSubId(int subId) {
1060 if (VDBG) logd("setDefaultSmsSubId sub id = " + subId);
1062 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1064 iSub.setDefaultSmsSubId(subId);
1066 } catch (RemoteException ex) {
1072 * Return the SubscriptionInfo for default voice subscription.
1074 * Will return null on data only devices, or on error.
1076 * @return the SubscriptionInfo for the default SMS subscription.
1079 public SubscriptionInfo getDefaultSmsSubscriptionInfo() {
1080 return getActiveSubscriptionInfo(getDefaultSmsSubscriptionId());
1084 public int getDefaultSmsPhoneId() {
1085 return getPhoneId(getDefaultSmsSubscriptionId());
1089 * Returns the system's default data subscription id.
1091 * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID.
1093 * @return the default data subscription Id.
1095 public static int getDefaultDataSubscriptionId() {
1096 int subId = INVALID_SUBSCRIPTION_ID;
1099 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1101 subId = iSub.getDefaultDataSubId();
1103 } catch (RemoteException ex) {
1107 if (VDBG) logd("getDefaultDataSubscriptionId, sub id = " + subId);
1112 public void setDefaultDataSubId(int subId) {
1113 if (VDBG) logd("setDataSubscription sub id = " + subId);
1115 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1117 iSub.setDefaultDataSubId(subId);
1119 } catch (RemoteException ex) {
1125 * Return the SubscriptionInfo for default data subscription.
1127 * Will return null on voice only devices, or on error.
1129 * @return the SubscriptionInfo for the default data subscription.
1132 public SubscriptionInfo getDefaultDataSubscriptionInfo() {
1133 return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
1137 public int getDefaultDataPhoneId() {
1138 return getPhoneId(getDefaultDataSubscriptionId());
1142 public void clearSubscriptionInfo() {
1144 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1146 iSub.clearSubInfo();
1148 } catch (RemoteException ex) {
1155 //FIXME this is vulnerable to race conditions
1157 public boolean allDefaultsSelected() {
1158 if (!isValidSubscriptionId(getDefaultDataSubscriptionId())) {
1161 if (!isValidSubscriptionId(getDefaultSmsSubscriptionId())) {
1164 if (!isValidSubscriptionId(getDefaultVoiceSubscriptionId())) {
1171 * If a default is set to subscription which is not active, this will reset that default back to
1172 * an invalid subscription id, i.e. < 0.
1175 public void clearDefaultsForInactiveSubIds() {
1176 if (VDBG) logd("clearDefaultsForInactiveSubIds");
1178 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1180 iSub.clearDefaultsForInactiveSubIds();
1182 } catch (RemoteException ex) {
1188 * @return true if a valid subId else false
1191 public static boolean isValidSubscriptionId(int subId) {
1192 return subId > INVALID_SUBSCRIPTION_ID ;
1196 * @return true if subId is an usable subId value else false. A
1197 * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
1200 public static boolean isUsableSubIdValue(int subId) {
1201 return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
1205 public static boolean isValidSlotId(int slotId) {
1206 return slotId >= 0 && slotId < TelephonyManager.getDefault().getSimCount();
1210 public static boolean isValidPhoneId(int phoneId) {
1211 return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
1215 public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
1216 int[] subIds = SubscriptionManager.getSubId(phoneId);
1217 if (subIds != null && subIds.length > 0) {
1218 putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]);
1220 logd("putPhoneIdAndSubIdExtra: no valid subs");
1225 public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) {
1226 if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
1227 intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
1228 intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
1229 //FIXME this is using phoneId and slotId interchangeably
1230 //Eventually, this should be removed as it is not the slot id
1231 intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
1235 * @return the list of subId's that are active,
1236 * is never null but the length maybe 0.
1239 public @NonNull int[] getActiveSubscriptionIdList() {
1243 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1245 subId = iSub.getActiveSubIdList();
1247 } catch (RemoteException ex) {
1251 if (subId == null) {
1260 * Returns true if the device is considered roaming on the current
1261 * network for a subscription.
1263 * Availability: Only when user registered to a network.
1265 * @param subId The subscription ID
1266 * @return true if the network for the subscription is roaming, false otherwise
1268 public boolean isNetworkRoaming(int subId) {
1269 final int phoneId = getPhoneId(subId);
1271 // What else can we do?
1274 return TelephonyManager.getDefault().isNetworkRoaming(subId);
1278 * Returns a constant indicating the state of sim for the slot idx.
1282 * {@See TelephonyManager#SIM_STATE_UNKNOWN}
1283 * {@See TelephonyManager#SIM_STATE_ABSENT}
1284 * {@See TelephonyManager#SIM_STATE_PIN_REQUIRED}
1285 * {@See TelephonyManager#SIM_STATE_PUK_REQUIRED}
1286 * {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED}
1287 * {@See TelephonyManager#SIM_STATE_READY}
1288 * {@See TelephonyManager#SIM_STATE_NOT_READY}
1289 * {@See TelephonyManager#SIM_STATE_PERM_DISABLED}
1290 * {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR}
1294 public static int getSimStateForSlotIdx(int slotIdx) {
1295 int simState = TelephonyManager.SIM_STATE_UNKNOWN;
1298 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1300 simState = iSub.getSimStateForSlotIdx(slotIdx);
1302 } catch (RemoteException ex) {
1309 * Store properties associated with SubscriptionInfo in database
1310 * @param subId Subscription Id of Subscription
1311 * @param propKey Column name in database associated with SubscriptionInfo
1312 * @param propValue Value to store in DB for particular subId & column name
1315 public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
1317 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1319 iSub.setSubscriptionProperty(subId, propKey, propValue);
1321 } catch (RemoteException ex) {
1327 * Store properties associated with SubscriptionInfo in database
1328 * @param subId Subscription Id of Subscription
1329 * @param propKey Column name in SubscriptionInfo database
1330 * @return Value associated with subId and propKey column in database
1333 private static String getSubscriptionProperty(int subId, String propKey,
1335 String resultValue = null;
1337 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1339 resultValue = iSub.getSubscriptionProperty(subId, propKey,
1340 context.getOpPackageName());
1342 } catch (RemoteException ex) {
1349 * Returns boolean value corresponding to query result.
1350 * @param subId Subscription Id of Subscription
1351 * @param propKey Column name in SubscriptionInfo database
1352 * @param defValue Default boolean value to be returned
1353 * @return boolean result value to be returned
1356 public static boolean getBooleanSubscriptionProperty(int subId, String propKey,
1357 boolean defValue, Context context) {
1358 String result = getSubscriptionProperty(subId, propKey, context);
1359 if (result != null) {
1361 return Integer.parseInt(result) == 1;
1362 } catch (NumberFormatException err) {
1363 logd("getBooleanSubscriptionProperty NumberFormat exception");
1370 * Returns integer value corresponding to query result.
1371 * @param subId Subscription Id of Subscription
1372 * @param propKey Column name in SubscriptionInfo database
1373 * @param defValue Default integer value to be returned
1374 * @return integer result value to be returned
1377 public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue,
1379 String result = getSubscriptionProperty(subId, propKey, context);
1380 if (result != null) {
1382 return Integer.parseInt(result);
1383 } catch (NumberFormatException err) {
1384 logd("getBooleanSubscriptionProperty NumberFormat exception");
1391 * Returns the resources associated with Subscription.
1392 * @param context Context object
1393 * @param subId Subscription Id of Subscription who's resources are required
1394 * @return Resources associated with Subscription.
1397 public static Resources getResourcesForSubId(Context context, int subId) {
1398 final SubscriptionInfo subInfo =
1399 SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
1401 Configuration config = context.getResources().getConfiguration();
1402 Configuration newConfig = new Configuration();
1403 newConfig.setTo(config);
1404 if (subInfo != null) {
1405 newConfig.mcc = subInfo.getMcc();
1406 newConfig.mnc = subInfo.getMnc();
1407 if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
1409 DisplayMetrics metrics = context.getResources().getDisplayMetrics();
1410 DisplayMetrics newMetrics = new DisplayMetrics();
1411 newMetrics.setTo(metrics);
1412 return new Resources(context.getResources().getAssets(), newMetrics, newConfig);
1416 * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
1417 * and the SIM providing the subscription is present in a slot and in "LOADED" state.
1420 public boolean isActiveSubId(int subId) {
1422 ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
1424 return iSub.isActiveSubId(subId);
1426 } catch (RemoteException ex) {