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