Home | History | Annotate | Download | only in telephony
      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 }
   1384