Home | History | Annotate | Download | only in ims
      1 /*
      2  * Copyright (c) 2013 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 com.android.ims;
     18 
     19 import android.app.PendingIntent;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.net.Uri;
     23 import android.os.AsyncTask;
     24 import android.os.IBinder;
     25 import android.os.Message;
     26 import android.os.PersistableBundle;
     27 import android.os.RemoteException;
     28 import android.os.ServiceManager;
     29 import android.os.SystemProperties;
     30 import android.provider.Settings;
     31 import android.telecom.TelecomManager;
     32 import android.telephony.CarrierConfigManager;
     33 import android.telephony.Rlog;
     34 import android.telephony.SubscriptionManager;
     35 import android.telephony.TelephonyManager;
     36 
     37 import com.android.ims.internal.IImsCallSession;
     38 import com.android.ims.internal.IImsEcbm;
     39 import com.android.ims.internal.IImsMultiEndpoint;
     40 import com.android.ims.internal.IImsRegistrationListener;
     41 import com.android.ims.internal.IImsService;
     42 import com.android.ims.internal.IImsUt;
     43 import com.android.ims.internal.ImsCallSession;
     44 import com.android.ims.internal.IImsConfig;
     45 
     46 import java.io.FileDescriptor;
     47 import java.io.PrintWriter;
     48 import java.util.HashMap;
     49 
     50 /**
     51  * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
     52  * the operator's IMS network. This class is the starting point for any IMS actions.
     53  * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
     54  * <p>The APIs in this class allows you to:</p>
     55  *
     56  * @hide
     57  */
     58 public class ImsManager {
     59 
     60     /*
     61      * Debug flag to override configuration flag
     62      */
     63     public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
     64     public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
     65     public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
     66     public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
     67     public static final String PROPERTY_DBG_WFC_AVAIL_OVERRIDE = "persist.dbg.wfc_avail_ovr";
     68     public static final int PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT = 0;
     69     public static final String PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE = "persist.dbg.allow_ims_off";
     70     public static final int PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT = 0;
     71 
     72     /**
     73      * For accessing the IMS related service.
     74      * Internal use only.
     75      * @hide
     76      */
     77     private static final String IMS_SERVICE = "ims";
     78 
     79     /**
     80      * The result code to be sent back with the incoming call {@link PendingIntent}.
     81      * @see #open(PendingIntent, ImsConnectionStateListener)
     82      */
     83     public static final int INCOMING_CALL_RESULT_CODE = 101;
     84 
     85     /**
     86      * Key to retrieve the call ID from an incoming call intent.
     87      * @see #open(PendingIntent, ImsConnectionStateListener)
     88      */
     89     public static final String EXTRA_CALL_ID = "android:imsCallID";
     90 
     91     /**
     92      * Action to broadcast when ImsService is up.
     93      * Internal use only.
     94      * @hide
     95      */
     96     public static final String ACTION_IMS_SERVICE_UP =
     97             "com.android.ims.IMS_SERVICE_UP";
     98 
     99     /**
    100      * Action to broadcast when ImsService is down.
    101      * Internal use only.
    102      * @hide
    103      */
    104     public static final String ACTION_IMS_SERVICE_DOWN =
    105             "com.android.ims.IMS_SERVICE_DOWN";
    106 
    107     /**
    108      * Action to broadcast when ImsService registration fails.
    109      * Internal use only.
    110      * @hide
    111      */
    112     public static final String ACTION_IMS_REGISTRATION_ERROR =
    113             "com.android.ims.REGISTRATION_ERROR";
    114 
    115     /**
    116      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
    117      * A long value; the phone ID corresponding to the IMS service coming up or down.
    118      * Internal use only.
    119      * @hide
    120      */
    121     public static final String EXTRA_PHONE_ID = "android:phone_id";
    122 
    123     /**
    124      * Action for the incoming call intent for the Phone app.
    125      * Internal use only.
    126      * @hide
    127      */
    128     public static final String ACTION_IMS_INCOMING_CALL =
    129             "com.android.ims.IMS_INCOMING_CALL";
    130 
    131     /**
    132      * Part of the ACTION_IMS_INCOMING_CALL intents.
    133      * An integer value; service identifier obtained from {@link ImsManager#open}.
    134      * Internal use only.
    135      * @hide
    136      */
    137     public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
    138 
    139     /**
    140      * Part of the ACTION_IMS_INCOMING_CALL intents.
    141      * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
    142      * The value "true" indicates that the incoming call is for USSD.
    143      * Internal use only.
    144      * @hide
    145      */
    146     public static final String EXTRA_USSD = "android:ussd";
    147 
    148     /**
    149      * Part of the ACTION_IMS_INCOMING_CALL intents.
    150      * A boolean value; Flag to indicate whether the call is an unknown
    151      * dialing call. Such calls are originated by sending commands (like
    152      * AT commands) directly to modem without Android involvement.
    153      * Even though they are not incoming calls, they are propagated
    154      * to Phone app using same ACTION_IMS_INCOMING_CALL intent.
    155      * Internal use only.
    156      * @hide
    157      */
    158     public static final String EXTRA_IS_UNKNOWN_CALL = "android:isUnknown";
    159 
    160     private static final String TAG = "ImsManager";
    161     private static final boolean DBG = true;
    162 
    163     private static HashMap<Integer, ImsManager> sImsManagerInstances =
    164             new HashMap<Integer, ImsManager>();
    165 
    166     private Context mContext;
    167     private int mPhoneId;
    168     private IImsService mImsService = null;
    169     private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
    170     // Ut interface for the supplementary service configuration
    171     private ImsUt mUt = null;
    172     // Interface to get/set ims config items
    173     private ImsConfig mConfig = null;
    174     private boolean mConfigUpdated = false;
    175 
    176     private ImsConfigListener mImsConfigListener;
    177 
    178     // ECBM interface
    179     private ImsEcbm mEcbm = null;
    180 
    181     private ImsMultiEndpoint mMultiEndpoint = null;
    182 
    183     // SystemProperties used as cache
    184     private static final String VOLTE_PROVISIONED_PROP = "net.lte.ims.volte.provisioned";
    185     private static final String WFC_PROVISIONED_PROP = "net.lte.ims.wfc.provisioned";
    186     private static final String VT_PROVISIONED_PROP = "net.lte.ims.vt.provisioned";
    187     // Flag indicating data enabled or not. This flag should be in sync with
    188     // DcTracker.isDataEnabled(). The flag will be set later during boot up.
    189     private static final String DATA_ENABLED_PROP = "net.lte.ims.data.enabled";
    190 
    191     public static final String TRUE = "true";
    192     public static final String FALSE = "false";
    193 
    194     /**
    195      * Gets a manager instance.
    196      *
    197      * @param context application context for creating the manager object
    198      * @param phoneId the phone ID for the IMS Service
    199      * @return the manager instance corresponding to the phoneId
    200      */
    201     public static ImsManager getInstance(Context context, int phoneId) {
    202         synchronized (sImsManagerInstances) {
    203             if (sImsManagerInstances.containsKey(phoneId))
    204                 return sImsManagerInstances.get(phoneId);
    205 
    206             ImsManager mgr = new ImsManager(context, phoneId);
    207             sImsManagerInstances.put(phoneId, mgr);
    208 
    209             return mgr;
    210         }
    211     }
    212 
    213     /**
    214      * Returns the user configuration of Enhanced 4G LTE Mode setting
    215      */
    216     public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
    217         // If user can't edit Enhanced 4G LTE Mode, it assumes Enhanced 4G LTE Mode is always true.
    218         // If user changes SIM from editable mode to uneditable mode, need to return true.
    219         if (!getBooleanCarrierConfig(context,
    220                     CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL)) {
    221             return true;
    222         }
    223         int enabled = android.provider.Settings.Global.getInt(
    224                     context.getContentResolver(),
    225                     android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
    226         return (enabled == 1) ? true : false;
    227     }
    228 
    229     /**
    230      * Change persistent Enhanced 4G LTE Mode setting
    231      */
    232     public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
    233         int value = enabled ? 1 : 0;
    234         android.provider.Settings.Global.putInt(
    235                 context.getContentResolver(),
    236                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
    237 
    238         if (isNonTtyOrTtyOnVolteEnabled(context)) {
    239             ImsManager imsManager = ImsManager.getInstance(context,
    240                     SubscriptionManager.getDefaultVoicePhoneId());
    241             if (imsManager != null) {
    242                 try {
    243                     imsManager.setAdvanced4GMode(enabled);
    244                 } catch (ImsException ie) {
    245                     // do nothing
    246                 }
    247             }
    248         }
    249     }
    250 
    251     /**
    252      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
    253      * supported.
    254      */
    255     public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
    256         if (getBooleanCarrierConfig(context,
    257                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
    258             return true;
    259         }
    260 
    261         return Settings.Secure.getInt(context.getContentResolver(),
    262                 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
    263                 == TelecomManager.TTY_MODE_OFF;
    264     }
    265 
    266     /**
    267      * Returns a platform configuration for VoLTE which may override the user setting.
    268      */
    269     public static boolean isVolteEnabledByPlatform(Context context) {
    270         if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
    271                 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
    272             return true;
    273         }
    274 
    275         return context.getResources().getBoolean(
    276                 com.android.internal.R.bool.config_device_volte_available)
    277                 && getBooleanCarrierConfig(context,
    278                         CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
    279                 && isGbaValid(context);
    280     }
    281 
    282     /**
    283      * Indicates whether VoLTE is provisioned on device
    284      */
    285     public static boolean isVolteProvisionedOnDevice(Context context) {
    286         if (getBooleanCarrierConfig(context,
    287                     CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
    288             ImsManager mgr = ImsManager.getInstance(context,
    289                     SubscriptionManager.getDefaultVoicePhoneId());
    290             if (mgr != null) {
    291                 return mgr.isVolteProvisioned();
    292             }
    293         }
    294 
    295         return true;
    296     }
    297 
    298     /**
    299      * Indicates whether VoWifi is provisioned on device
    300      */
    301     public static boolean isWfcProvisionedOnDevice(Context context) {
    302         if (getBooleanCarrierConfig(context,
    303                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
    304             ImsManager mgr = ImsManager.getInstance(context,
    305                     SubscriptionManager.getDefaultVoicePhoneId());
    306             if (mgr != null) {
    307                 return mgr.isWfcProvisioned();
    308             }
    309         }
    310 
    311         return true;
    312     }
    313 
    314     /**
    315      * Indicates whether VT is provisioned on device
    316      */
    317     public static boolean isVtProvisionedOnDevice(Context context) {
    318         if (getBooleanCarrierConfig(context,
    319                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
    320             ImsManager mgr = ImsManager.getInstance(context,
    321                     SubscriptionManager.getDefaultVoicePhoneId());
    322             if (mgr != null) {
    323                 return mgr.isVtProvisioned();
    324             }
    325         }
    326 
    327         return true;
    328     }
    329 
    330     /**
    331      * Returns a platform configuration for VT which may override the user setting.
    332      *
    333      * Note: VT presumes that VoLTE is enabled (these are configuration settings
    334      * which must be done correctly).
    335      */
    336     public static boolean isVtEnabledByPlatform(Context context) {
    337         if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
    338                 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
    339             return true;
    340         }
    341 
    342         return
    343                 context.getResources().getBoolean(
    344                         com.android.internal.R.bool.config_device_vt_available) &&
    345                 getBooleanCarrierConfig(context,
    346                         CarrierConfigManager.KEY_CARRIER_VT_AVAILABLE_BOOL) &&
    347                 isGbaValid(context);
    348     }
    349 
    350     /**
    351      * Returns the user configuration of VT setting
    352      */
    353     public static boolean isVtEnabledByUser(Context context) {
    354         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
    355                 android.provider.Settings.Global.VT_IMS_ENABLED,
    356                 ImsConfig.FeatureValueConstants.ON);
    357         return (enabled == 1) ? true : false;
    358     }
    359 
    360     /**
    361      * Change persistent VT enabled setting
    362      */
    363     public static void setVtSetting(Context context, boolean enabled) {
    364         int value = enabled ? 1 : 0;
    365         android.provider.Settings.Global.putInt(context.getContentResolver(),
    366                 android.provider.Settings.Global.VT_IMS_ENABLED, value);
    367 
    368         ImsManager imsManager = ImsManager.getInstance(context,
    369                 SubscriptionManager.getDefaultVoicePhoneId());
    370         if (imsManager != null) {
    371             try {
    372                 ImsConfig config = imsManager.getConfigInterface();
    373                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
    374                         TelephonyManager.NETWORK_TYPE_LTE,
    375                         enabled ? ImsConfig.FeatureValueConstants.ON
    376                                 : ImsConfig.FeatureValueConstants.OFF,
    377                         imsManager.mImsConfigListener);
    378 
    379                 if (enabled) {
    380                     log("setVtSetting() : turnOnIms");
    381                     imsManager.turnOnIms();
    382                 } else if (isTurnOffImsAllowedByPlatform(context)
    383                         && (!isVolteEnabledByPlatform(context)
    384                         || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
    385                     log("setVtSetting() : imsServiceAllowTurnOff -> turnOffIms");
    386                     imsManager.turnOffIms();
    387                 }
    388             } catch (ImsException e) {
    389                 loge("setVtSetting(): ", e);
    390             }
    391         }
    392     }
    393 
    394     /*
    395      * Returns whether turning off ims is allowed by platform.
    396      * The platform property may override the carrier config.
    397      */
    398     private static boolean isTurnOffImsAllowedByPlatform(Context context) {
    399         if (SystemProperties.getInt(PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE,
    400                 PROPERTY_DBG_ALLOW_IMS_OFF_OVERRIDE_DEFAULT) == 1) {
    401             return true;
    402         }
    403         return getBooleanCarrierConfig(context,
    404                 CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL);
    405     }
    406 
    407     /**
    408      * Returns the user configuration of WFC setting
    409      */
    410     public static boolean isWfcEnabledByUser(Context context) {
    411         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
    412                 android.provider.Settings.Global.WFC_IMS_ENABLED,
    413                 getBooleanCarrierConfig(context,
    414                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
    415                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
    416         return (enabled == 1) ? true : false;
    417     }
    418 
    419     /**
    420      * Change persistent WFC enabled setting
    421      */
    422     public static void setWfcSetting(Context context, boolean enabled) {
    423         int value = enabled ? 1 : 0;
    424         android.provider.Settings.Global.putInt(context.getContentResolver(),
    425                 android.provider.Settings.Global.WFC_IMS_ENABLED, value);
    426 
    427         ImsManager imsManager = ImsManager.getInstance(context,
    428                 SubscriptionManager.getDefaultVoicePhoneId());
    429         if (imsManager != null) {
    430             try {
    431                 ImsConfig config = imsManager.getConfigInterface();
    432                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
    433                         TelephonyManager.NETWORK_TYPE_IWLAN,
    434                         enabled ? ImsConfig.FeatureValueConstants.ON
    435                                 : ImsConfig.FeatureValueConstants.OFF,
    436                         imsManager.mImsConfigListener);
    437 
    438                 if (enabled) {
    439                     log("setWfcSetting() : turnOnIms");
    440                     imsManager.turnOnIms();
    441                 } else if (isTurnOffImsAllowedByPlatform(context)
    442                         && (!isVolteEnabledByPlatform(context)
    443                         || !isEnhanced4gLteModeSettingEnabledByUser(context))) {
    444                     log("setWfcSetting() : imsServiceAllowTurnOff -> turnOffIms");
    445                     imsManager.turnOffIms();
    446                 }
    447 
    448                 // Force IMS to register over LTE when turning off WFC
    449                 setWfcModeInternal(context, enabled
    450                         ? getWfcMode(context)
    451                         : ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
    452             } catch (ImsException e) {
    453                 loge("setWfcSetting(): ", e);
    454             }
    455         }
    456     }
    457 
    458     /**
    459      * Returns the user configuration of WFC modem setting
    460      */
    461     public static int getWfcMode(Context context) {
    462         int setting = android.provider.Settings.Global.getInt(context.getContentResolver(),
    463                 android.provider.Settings.Global.WFC_IMS_MODE, getIntCarrierConfig(context,
    464                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
    465         if (DBG) log("getWfcMode - setting=" + setting);
    466         return setting;
    467     }
    468 
    469     /**
    470      * Returns the user configuration of WFC modem setting
    471      */
    472     public static void setWfcMode(Context context, int wfcMode) {
    473         if (DBG) log("setWfcMode - setting=" + wfcMode);
    474         android.provider.Settings.Global.putInt(context.getContentResolver(),
    475                 android.provider.Settings.Global.WFC_IMS_MODE, wfcMode);
    476 
    477         setWfcModeInternal(context, wfcMode);
    478     }
    479 
    480     private static void setWfcModeInternal(Context context, int wfcMode) {
    481         final ImsManager imsManager = ImsManager.getInstance(context,
    482                 SubscriptionManager.getDefaultVoicePhoneId());
    483         if (imsManager != null) {
    484             final int value = wfcMode;
    485             Thread thread = new Thread(new Runnable() {
    486                 public void run() {
    487                     try {
    488                         imsManager.getConfigInterface().setProvisionedValue(
    489                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE,
    490                                 value);
    491                     } catch (ImsException e) {
    492                         // do nothing
    493                     }
    494                 }
    495             });
    496             thread.start();
    497         }
    498     }
    499 
    500     /**
    501      * Returns the user configuration of WFC roaming setting
    502      */
    503     public static boolean isWfcRoamingEnabledByUser(Context context) {
    504         int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(),
    505                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
    506                 getBooleanCarrierConfig(context,
    507                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
    508                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
    509         return (enabled == 1) ? true : false;
    510     }
    511 
    512     /**
    513      * Change persistent WFC roaming enabled setting
    514      */
    515     public static void setWfcRoamingSetting(Context context, boolean enabled) {
    516         android.provider.Settings.Global.putInt(context.getContentResolver(),
    517                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
    518                 enabled ? ImsConfig.FeatureValueConstants.ON
    519                         : ImsConfig.FeatureValueConstants.OFF);
    520 
    521         setWfcRoamingSettingInternal(context, enabled);
    522     }
    523 
    524     private static void setWfcRoamingSettingInternal(Context context, boolean enabled) {
    525         final ImsManager imsManager = ImsManager.getInstance(context,
    526                 SubscriptionManager.getDefaultVoicePhoneId());
    527         if (imsManager != null) {
    528             final int value = enabled
    529                     ? ImsConfig.FeatureValueConstants.ON
    530                     : ImsConfig.FeatureValueConstants.OFF;
    531             Thread thread = new Thread(new Runnable() {
    532                 public void run() {
    533                     try {
    534                         imsManager.getConfigInterface().setProvisionedValue(
    535                                 ImsConfig.ConfigConstants.VOICE_OVER_WIFI_ROAMING,
    536                                 value);
    537                     } catch (ImsException e) {
    538                         // do nothing
    539                     }
    540                 }
    541             });
    542             thread.start();
    543         }
    544     }
    545 
    546     /**
    547      * Returns a platform configuration for WFC which may override the user
    548      * setting. Note: WFC presumes that VoLTE is enabled (these are
    549      * configuration settings which must be done correctly).
    550      */
    551     public static boolean isWfcEnabledByPlatform(Context context) {
    552         if (SystemProperties.getInt(PROPERTY_DBG_WFC_AVAIL_OVERRIDE,
    553                 PROPERTY_DBG_WFC_AVAIL_OVERRIDE_DEFAULT) == 1) {
    554             return true;
    555         }
    556 
    557         return
    558                context.getResources().getBoolean(
    559                        com.android.internal.R.bool.config_device_wfc_ims_available) &&
    560                getBooleanCarrierConfig(context,
    561                        CarrierConfigManager.KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL) &&
    562                isGbaValid(context);
    563     }
    564 
    565     /**
    566      * If carrier requires that IMS is only available if GBA capable SIM is used,
    567      * then this function checks GBA bit in EF IST.
    568      *
    569      * Format of EF IST is defined in 3GPP TS 31.103 (Section 4.2.7).
    570      */
    571     private static boolean isGbaValid(Context context) {
    572         if (getBooleanCarrierConfig(context,
    573                 CarrierConfigManager.KEY_CARRIER_IMS_GBA_REQUIRED_BOOL)) {
    574             final TelephonyManager telephonyManager = TelephonyManager.getDefault();
    575             String efIst = telephonyManager.getIsimIst();
    576             if (efIst == null) {
    577                 loge("ISF is NULL");
    578                 return true;
    579             }
    580             boolean result = efIst != null && efIst.length() > 1 &&
    581                     (0x02 & (byte)efIst.charAt(1)) != 0;
    582             if (DBG) log("GBA capable=" + result + ", ISF=" + efIst);
    583             return result;
    584         }
    585         return true;
    586     }
    587 
    588     /**
    589      * This function should be called when ImsConfig.ACTION_IMS_CONFIG_CHANGED is received.
    590      *
    591      * We cannot register receiver in ImsManager because this would lead to resource leak.
    592      * ImsManager can be created in different processes and it is not notified when that process
    593      * is about to be terminated.
    594      *
    595      * @hide
    596      * */
    597     public static void onProvisionedValueChanged(Context context, int item, String value) {
    598         if (DBG) Rlog.d(TAG, "onProvisionedValueChanged: item=" + item + " val=" + value);
    599         ImsManager mgr = ImsManager.getInstance(context,
    600                 SubscriptionManager.getDefaultVoicePhoneId());
    601 
    602         switch (item) {
    603             case ImsConfig.ConfigConstants.VLT_SETTING_ENABLED:
    604                 mgr.setVolteProvisionedProperty(value.equals("1"));
    605                 if (DBG) Rlog.d(TAG,"isVoLteProvisioned = " + mgr.isVolteProvisioned());
    606                 break;
    607 
    608             case ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED:
    609                 mgr.setWfcProvisionedProperty(value.equals("1"));
    610                 if (DBG) Rlog.d(TAG,"isWfcProvisioned = " + mgr.isWfcProvisioned());
    611                 break;
    612 
    613             case ImsConfig.ConfigConstants.LVC_SETTING_ENABLED:
    614                 mgr.setVtProvisionedProperty(value.equals("1"));
    615                 if (DBG) Rlog.d(TAG,"isVtProvisioned = " + mgr.isVtProvisioned());
    616                 break;
    617 
    618         }
    619     }
    620 
    621     private class AsyncUpdateProvisionedValues extends AsyncTask<Void, Void, Void> {
    622         @Override
    623         protected Void doInBackground(Void... params) {
    624             // disable on any error
    625             setVolteProvisionedProperty(false);
    626             setWfcProvisionedProperty(false);
    627             setVtProvisionedProperty(false);
    628 
    629             try {
    630                 ImsConfig config = getConfigInterface();
    631                 if (config != null) {
    632                     setVolteProvisionedProperty(getProvisionedBool(config,
    633                             ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
    634                     if (DBG) Rlog.d(TAG, "isVoLteProvisioned = " + isVolteProvisioned());
    635 
    636                     setWfcProvisionedProperty(getProvisionedBool(config,
    637                             ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
    638                     if (DBG) Rlog.d(TAG, "isWfcProvisioned = " + isWfcProvisioned());
    639 
    640                     setVtProvisionedProperty(getProvisionedBool(config,
    641                             ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
    642                     if (DBG) Rlog.d(TAG, "isVtProvisioned = " + isVtProvisioned());
    643 
    644                 }
    645             } catch (ImsException ie) {
    646                 Rlog.e(TAG, "AsyncUpdateProvisionedValues error: ", ie);
    647             }
    648 
    649             return null;
    650         }
    651 
    652         private boolean getProvisionedBool(ImsConfig config, int item) throws ImsException {
    653             return config.getProvisionedValue(item) == ImsConfig.FeatureValueConstants.ON;
    654         }
    655     }
    656 
    657     /** Asynchronously get VoLTE, WFC, VT provisioning statuses */
    658     private void updateProvisionedValues() {
    659         if (getBooleanCarrierConfig(mContext,
    660                 CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL)) {
    661 
    662             new AsyncUpdateProvisionedValues().execute();
    663         }
    664     }
    665 
    666     /**
    667      * Sync carrier config and user settings with ImsConfig.
    668      *
    669      * @param context for the manager object
    670      * @param phoneId phone id
    671      * @param force update
    672      */
    673     public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
    674         if (!force) {
    675             if (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) {
    676                 log("updateImsServiceConfig: SIM not ready");
    677                 // Don't disable IMS if SIM is not ready
    678                 return;
    679             }
    680         }
    681 
    682         final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
    683         if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
    684             try {
    685                 imsManager.updateProvisionedValues();
    686 
    687                 // TODO: Extend ImsConfig API and set all feature values in single function call.
    688 
    689                 // Note: currently the order of updates is set to produce different order of
    690                 // setFeatureValue() function calls from setAdvanced4GMode(). This is done to
    691                 // differentiate this code path from vendor code perspective.
    692                 boolean isImsUsed = imsManager.updateVolteFeatureValue();
    693                 isImsUsed |= imsManager.updateWfcFeatureAndProvisionedValues();
    694                 isImsUsed |= imsManager.updateVideoCallFeatureValue();
    695 
    696                 if (isImsUsed || !isTurnOffImsAllowedByPlatform(context)) {
    697                     // Turn on IMS if it is used.
    698                     // Also, if turning off is not allowed for current carrier,
    699                     // we need to turn IMS on because it might be turned off before
    700                     // phone switched to current carrier.
    701                     log("updateImsServiceConfig: turnOnIms");
    702                     imsManager.turnOnIms();
    703                 } else {
    704                     // Turn off IMS if it is not used AND turning off is allowed for carrier.
    705                     log("updateImsServiceConfig: turnOffIms");
    706                     imsManager.turnOffIms();
    707                 }
    708 
    709                 imsManager.mConfigUpdated = true;
    710             } catch (ImsException e) {
    711                 loge("updateImsServiceConfig: ", e);
    712                 imsManager.mConfigUpdated = false;
    713             }
    714         }
    715     }
    716 
    717     /**
    718      * Update VoLTE config
    719      * @return whether feature is On
    720      * @throws ImsException
    721      */
    722     private boolean updateVolteFeatureValue() throws ImsException {
    723         boolean available = isVolteEnabledByPlatform(mContext);
    724         boolean enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext);
    725         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
    726         boolean isFeatureOn = available && enabled && isNonTty;
    727 
    728         log("updateVolteFeatureValue: available = " + available
    729                 + ", enabled = " + enabled
    730                 + ", nonTTY = " + isNonTty);
    731 
    732         getConfigInterface().setFeatureValue(
    733                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
    734                 TelephonyManager.NETWORK_TYPE_LTE,
    735                 isFeatureOn ?
    736                         ImsConfig.FeatureValueConstants.ON :
    737                         ImsConfig.FeatureValueConstants.OFF,
    738                 mImsConfigListener);
    739 
    740         return isFeatureOn;
    741     }
    742 
    743     /**
    744      * Update video call over LTE config
    745      * @return whether feature is On
    746      * @throws ImsException
    747      */
    748     private boolean updateVideoCallFeatureValue() throws ImsException {
    749         boolean available = isVtEnabledByPlatform(mContext);
    750         boolean enabled = isVtEnabledByUser(mContext);
    751         boolean isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext);
    752         boolean isDataEnabled = isDataEnabled();
    753 
    754         boolean isFeatureOn = available && enabled && isNonTty && isDataEnabled;
    755 
    756         log("updateVideoCallFeatureValue: available = " + available
    757                 + ", enabled = " + enabled
    758                 + ", nonTTY = " + isNonTty
    759                 + ", data enabled = " + isDataEnabled);
    760 
    761         getConfigInterface().setFeatureValue(
    762                 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
    763                 TelephonyManager.NETWORK_TYPE_LTE,
    764                 isFeatureOn ?
    765                         ImsConfig.FeatureValueConstants.ON :
    766                         ImsConfig.FeatureValueConstants.OFF,
    767                 mImsConfigListener);
    768 
    769         return isFeatureOn;
    770     }
    771 
    772     /**
    773      * Update WFC config
    774      * @return whether feature is On
    775      * @throws ImsException
    776      */
    777     private boolean updateWfcFeatureAndProvisionedValues() throws ImsException {
    778         boolean available = isWfcEnabledByPlatform(mContext);
    779         boolean enabled = isWfcEnabledByUser(mContext);
    780         int mode = getWfcMode(mContext);
    781         boolean roaming = isWfcRoamingEnabledByUser(mContext);
    782         boolean isFeatureOn = available && enabled;
    783 
    784         log("updateWfcFeatureAndProvisionedValues: available = " + available
    785                 + ", enabled = " + enabled
    786                 + ", mode = " + mode
    787                 + ", roaming = " + roaming);
    788 
    789         getConfigInterface().setFeatureValue(
    790                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI,
    791                 TelephonyManager.NETWORK_TYPE_IWLAN,
    792                 isFeatureOn ?
    793                         ImsConfig.FeatureValueConstants.ON :
    794                         ImsConfig.FeatureValueConstants.OFF,
    795                 mImsConfigListener);
    796 
    797         if (!isFeatureOn) {
    798             mode = ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
    799             roaming = false;
    800         }
    801         setWfcModeInternal(mContext, mode);
    802         setWfcRoamingSettingInternal(mContext, roaming);
    803 
    804         return isFeatureOn;
    805     }
    806 
    807     private ImsManager(Context context, int phoneId) {
    808         mContext = context;
    809         mPhoneId = phoneId;
    810         createImsService(true);
    811     }
    812 
    813     /*
    814      * Returns a flag indicating whether the IMS service is available.
    815      */
    816     public boolean isServiceAvailable() {
    817         if (mImsService != null) {
    818             return true;
    819         }
    820 
    821         IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
    822         if (binder != null) {
    823             return true;
    824         }
    825 
    826         return false;
    827     }
    828 
    829     public void setImsConfigListener(ImsConfigListener listener) {
    830         mImsConfigListener = listener;
    831     }
    832 
    833     /**
    834      * Opens the IMS service for making calls and/or receiving generic IMS calls.
    835      * The caller may make subsquent calls through {@link #makeCall}.
    836      * The IMS service will register the device to the operator's network with the credentials
    837      * (from ISIM) periodically in order to receive calls from the operator's network.
    838      * When the IMS service receives a new call, it will send out an intent with
    839      * the provided action string.
    840      * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
    841      *
    842      * @param serviceClass a service class specified in {@link ImsServiceClass}
    843      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
    844      * @param incomingCallPendingIntent When an incoming call is received,
    845      *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
    846      *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
    847      *        as the result code and the intent to fill in the call ID; It cannot be null
    848      * @param listener To listen to IMS registration events; It cannot be null
    849      * @return identifier (greater than 0) for the specified service
    850      * @throws NullPointerException if {@code incomingCallPendingIntent}
    851      *      or {@code listener} is null
    852      * @throws ImsException if calling the IMS service results in an error
    853      * @see #getCallId
    854      * @see #getServiceId
    855      */
    856     public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
    857             ImsConnectionStateListener listener) throws ImsException {
    858         checkAndThrowExceptionIfServiceUnavailable();
    859 
    860         if (incomingCallPendingIntent == null) {
    861             throw new NullPointerException("incomingCallPendingIntent can't be null");
    862         }
    863 
    864         if (listener == null) {
    865             throw new NullPointerException("listener can't be null");
    866         }
    867 
    868         int result = 0;
    869 
    870         try {
    871             result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
    872                     createRegistrationListenerProxy(serviceClass, listener));
    873         } catch (RemoteException e) {
    874             throw new ImsException("open()", e,
    875                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    876         }
    877 
    878         if (result <= 0) {
    879             // If the return value is a minus value,
    880             // it means that an error occurred in the service.
    881             // So, it needs to convert to the reason code specified in ImsReasonInfo.
    882             throw new ImsException("open()", (result * (-1)));
    883         }
    884 
    885         return result;
    886     }
    887 
    888     /**
    889      * Adds registration listener to the IMS service.
    890      *
    891      * @param serviceClass a service class specified in {@link ImsServiceClass}
    892      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
    893      * @param listener To listen to IMS registration events; It cannot be null
    894      * @throws NullPointerException if {@code listener} is null
    895      * @throws ImsException if calling the IMS service results in an error
    896      */
    897     public void addRegistrationListener(int serviceClass, ImsConnectionStateListener listener)
    898             throws ImsException {
    899         checkAndThrowExceptionIfServiceUnavailable();
    900 
    901         if (listener == null) {
    902             throw new NullPointerException("listener can't be null");
    903         }
    904 
    905         try {
    906             mImsService.addRegistrationListener(mPhoneId, serviceClass,
    907                     createRegistrationListenerProxy(serviceClass, listener));
    908         } catch (RemoteException e) {
    909             throw new ImsException("addRegistrationListener()", e,
    910                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    911         }
    912     }
    913 
    914     /**
    915      * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
    916      * All the resources that were allocated to the service are also released.
    917      *
    918      * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
    919      * @throws ImsException if calling the IMS service results in an error
    920      */
    921     public void close(int serviceId) throws ImsException {
    922         checkAndThrowExceptionIfServiceUnavailable();
    923 
    924         try {
    925             mImsService.close(serviceId);
    926         } catch (RemoteException e) {
    927             throw new ImsException("close()", e,
    928                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    929         } finally {
    930             mUt = null;
    931             mConfig = null;
    932             mEcbm = null;
    933             mMultiEndpoint = null;
    934         }
    935     }
    936 
    937     /**
    938      * Gets the configuration interface to provision / withdraw the supplementary service settings.
    939      *
    940      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    941      * @return the Ut interface instance
    942      * @throws ImsException if getting the Ut interface results in an error
    943      */
    944     public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
    945             throws ImsException {
    946         // FIXME: manage the multiple Ut interfaces based on the service id
    947         if (mUt == null) {
    948             checkAndThrowExceptionIfServiceUnavailable();
    949 
    950             try {
    951                 IImsUt iUt = mImsService.getUtInterface(serviceId);
    952 
    953                 if (iUt == null) {
    954                     throw new ImsException("getSupplementaryServiceConfiguration()",
    955                             ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
    956                 }
    957 
    958                 mUt = new ImsUt(iUt);
    959             } catch (RemoteException e) {
    960                 throw new ImsException("getSupplementaryServiceConfiguration()", e,
    961                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    962             }
    963         }
    964 
    965         return mUt;
    966     }
    967 
    968     /**
    969      * Checks if the IMS service has successfully registered to the IMS network
    970      * with the specified service & call type.
    971      *
    972      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    973      * @param serviceType a service type that is specified in {@link ImsCallProfile}
    974      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
    975      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
    976      * @param callType a call type that is specified in {@link ImsCallProfile}
    977      *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
    978      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
    979      *        {@link ImsCallProfile#CALL_TYPE_VT}
    980      *        {@link ImsCallProfile#CALL_TYPE_VS}
    981      * @return true if the specified service id is connected to the IMS network;
    982      *        false otherwise
    983      * @throws ImsException if calling the IMS service results in an error
    984      */
    985     public boolean isConnected(int serviceId, int serviceType, int callType)
    986             throws ImsException {
    987         checkAndThrowExceptionIfServiceUnavailable();
    988 
    989         try {
    990             return mImsService.isConnected(serviceId, serviceType, callType);
    991         } catch (RemoteException e) {
    992             throw new ImsException("isServiceConnected()", e,
    993                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    994         }
    995     }
    996 
    997     /**
    998      * Checks if the specified IMS service is opend.
    999      *
   1000      * @param serviceId a service id which is obtained from {@link ImsManager#open}
   1001      * @return true if the specified service id is opened; false otherwise
   1002      * @throws ImsException if calling the IMS service results in an error
   1003      */
   1004     public boolean isOpened(int serviceId) throws ImsException {
   1005         checkAndThrowExceptionIfServiceUnavailable();
   1006 
   1007         try {
   1008             return mImsService.isOpened(serviceId);
   1009         } catch (RemoteException e) {
   1010             throw new ImsException("isOpened()", e,
   1011                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1012         }
   1013     }
   1014 
   1015     /**
   1016      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
   1017      *
   1018      * @param serviceId a service id which is obtained from {@link ImsManager#open}
   1019      * @param serviceType a service type that is specified in {@link ImsCallProfile}
   1020      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
   1021      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
   1022      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
   1023      * @param callType a call type that is specified in {@link ImsCallProfile}
   1024      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
   1025      *        {@link ImsCallProfile#CALL_TYPE_VT}
   1026      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
   1027      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
   1028      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
   1029      *        {@link ImsCallProfile#CALL_TYPE_VS}
   1030      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
   1031      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
   1032      * @return a {@link ImsCallProfile} object
   1033      * @throws ImsException if calling the IMS service results in an error
   1034      */
   1035     public ImsCallProfile createCallProfile(int serviceId,
   1036             int serviceType, int callType) throws ImsException {
   1037         checkAndThrowExceptionIfServiceUnavailable();
   1038 
   1039         try {
   1040             return mImsService.createCallProfile(serviceId, serviceType, callType);
   1041         } catch (RemoteException e) {
   1042             throw new ImsException("createCallProfile()", e,
   1043                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1044         }
   1045     }
   1046 
   1047     /**
   1048      * Creates a {@link ImsCall} to make a call.
   1049      *
   1050      * @param serviceId a service id which is obtained from {@link ImsManager#open}
   1051      * @param profile a call profile to make the call
   1052      *      (it contains service type, call type, media information, etc.)
   1053      * @param participants participants to invite the conference call
   1054      * @param listener listen to the call events from {@link ImsCall}
   1055      * @return a {@link ImsCall} object
   1056      * @throws ImsException if calling the IMS service results in an error
   1057      */
   1058     public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
   1059             ImsCall.Listener listener) throws ImsException {
   1060         if (DBG) {
   1061             log("makeCall :: serviceId=" + serviceId
   1062                     + ", profile=" + profile);
   1063         }
   1064 
   1065         checkAndThrowExceptionIfServiceUnavailable();
   1066 
   1067         ImsCall call = new ImsCall(mContext, profile);
   1068 
   1069         call.setListener(listener);
   1070         ImsCallSession session = createCallSession(serviceId, profile);
   1071 
   1072         if ((callees != null) && (callees.length == 1)) {
   1073             call.start(session, callees[0]);
   1074         } else {
   1075             call.start(session, callees);
   1076         }
   1077 
   1078         return call;
   1079     }
   1080 
   1081     /**
   1082      * Creates a {@link ImsCall} to take an incoming call.
   1083      *
   1084      * @param serviceId a service id which is obtained from {@link ImsManager#open}
   1085      * @param incomingCallIntent the incoming call broadcast intent
   1086      * @param listener to listen to the call events from {@link ImsCall}
   1087      * @return a {@link ImsCall} object
   1088      * @throws ImsException if calling the IMS service results in an error
   1089      */
   1090     public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
   1091             ImsCall.Listener listener) throws ImsException {
   1092         if (DBG) {
   1093             log("takeCall :: serviceId=" + serviceId
   1094                     + ", incomingCall=" + incomingCallIntent);
   1095         }
   1096 
   1097         checkAndThrowExceptionIfServiceUnavailable();
   1098 
   1099         if (incomingCallIntent == null) {
   1100             throw new ImsException("Can't retrieve session with null intent",
   1101                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
   1102         }
   1103 
   1104         int incomingServiceId = getServiceId(incomingCallIntent);
   1105 
   1106         if (serviceId != incomingServiceId) {
   1107             throw new ImsException("Service id is mismatched in the incoming call intent",
   1108                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
   1109         }
   1110 
   1111         String callId = getCallId(incomingCallIntent);
   1112 
   1113         if (callId == null) {
   1114             throw new ImsException("Call ID missing in the incoming call intent",
   1115                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
   1116         }
   1117 
   1118         try {
   1119             IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
   1120 
   1121             if (session == null) {
   1122                 throw new ImsException("No pending session for the call",
   1123                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
   1124             }
   1125 
   1126             ImsCall call = new ImsCall(mContext, session.getCallProfile());
   1127 
   1128             call.attachSession(new ImsCallSession(session));
   1129             call.setListener(listener);
   1130 
   1131             return call;
   1132         } catch (Throwable t) {
   1133             throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
   1134         }
   1135     }
   1136 
   1137     /**
   1138      * Gets the config interface to get/set service/capability parameters.
   1139      *
   1140      * @return the ImsConfig instance.
   1141      * @throws ImsException if getting the setting interface results in an error.
   1142      */
   1143     public ImsConfig getConfigInterface() throws ImsException {
   1144 
   1145         if (mConfig == null) {
   1146             checkAndThrowExceptionIfServiceUnavailable();
   1147 
   1148             try {
   1149                 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
   1150                 if (config == null) {
   1151                     throw new ImsException("getConfigInterface()",
   1152                             ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
   1153                 }
   1154                 mConfig = new ImsConfig(config, mContext);
   1155             } catch (RemoteException e) {
   1156                 throw new ImsException("getConfigInterface()", e,
   1157                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1158             }
   1159         }
   1160         if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
   1161         return mConfig;
   1162     }
   1163 
   1164     public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
   1165             throws ImsException {
   1166 
   1167         checkAndThrowExceptionIfServiceUnavailable();
   1168 
   1169         try {
   1170             mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
   1171         } catch (RemoteException e) {
   1172             throw new ImsException("setTTYMode()", e,
   1173                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1174         }
   1175 
   1176         if (!getBooleanCarrierConfig(context,
   1177                 CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
   1178             setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
   1179                     isEnhanced4gLteModeSettingEnabledByUser(context));
   1180         }
   1181     }
   1182 
   1183     /**
   1184      * Get the boolean config from carrier config manager.
   1185      *
   1186      * @param context the context to get carrier service
   1187      * @param key config key defined in CarrierConfigManager
   1188      * @return boolean value of corresponding key.
   1189      */
   1190     private static boolean getBooleanCarrierConfig(Context context, String key) {
   1191         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
   1192                 Context.CARRIER_CONFIG_SERVICE);
   1193         PersistableBundle b = null;
   1194         if (configManager != null) {
   1195             b = configManager.getConfig();
   1196         }
   1197         if (b != null) {
   1198             return b.getBoolean(key);
   1199         } else {
   1200             // Return static default defined in CarrierConfigManager.
   1201             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
   1202         }
   1203     }
   1204 
   1205     /**
   1206      * Get the int config from carrier config manager.
   1207      *
   1208      * @param context the context to get carrier service
   1209      * @param key config key defined in CarrierConfigManager
   1210      * @return integer value of corresponding key.
   1211      */
   1212     private static int getIntCarrierConfig(Context context, String key) {
   1213         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
   1214                 Context.CARRIER_CONFIG_SERVICE);
   1215         PersistableBundle b = null;
   1216         if (configManager != null) {
   1217             b = configManager.getConfig();
   1218         }
   1219         if (b != null) {
   1220             return b.getInt(key);
   1221         } else {
   1222             // Return static default defined in CarrierConfigManager.
   1223             return CarrierConfigManager.getDefaultConfig().getInt(key);
   1224         }
   1225     }
   1226 
   1227     /**
   1228      * Gets the call ID from the specified incoming call broadcast intent.
   1229      *
   1230      * @param incomingCallIntent the incoming call broadcast intent
   1231      * @return the call ID or null if the intent does not contain it
   1232      */
   1233     private static String getCallId(Intent incomingCallIntent) {
   1234         if (incomingCallIntent == null) {
   1235             return null;
   1236         }
   1237 
   1238         return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
   1239     }
   1240 
   1241     /**
   1242      * Gets the service type from the specified incoming call broadcast intent.
   1243      *
   1244      * @param incomingCallIntent the incoming call broadcast intent
   1245      * @return the service identifier or -1 if the intent does not contain it
   1246      */
   1247     private static int getServiceId(Intent incomingCallIntent) {
   1248         if (incomingCallIntent == null) {
   1249             return (-1);
   1250         }
   1251 
   1252         return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
   1253     }
   1254 
   1255     /**
   1256      * Binds the IMS service only if the service is not created.
   1257      */
   1258     private void checkAndThrowExceptionIfServiceUnavailable()
   1259             throws ImsException {
   1260         if (mImsService == null) {
   1261             createImsService(true);
   1262 
   1263             if (mImsService == null) {
   1264                 throw new ImsException("Service is unavailable",
   1265                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1266             }
   1267         }
   1268     }
   1269 
   1270     private static String getImsServiceName(int phoneId) {
   1271         // TODO: MSIM implementation needs to decide on service name as a function of phoneId
   1272         return IMS_SERVICE;
   1273     }
   1274 
   1275     /**
   1276      * Binds the IMS service to make/receive the call.
   1277      */
   1278     private void createImsService(boolean checkService) {
   1279         if (checkService) {
   1280             IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
   1281 
   1282             if (binder == null) {
   1283                 return;
   1284             }
   1285         }
   1286 
   1287         IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
   1288 
   1289         if (b != null) {
   1290             try {
   1291                 b.linkToDeath(mDeathRecipient, 0);
   1292             } catch (RemoteException e) {
   1293             }
   1294         }
   1295 
   1296         mImsService = IImsService.Stub.asInterface(b);
   1297     }
   1298 
   1299     /**
   1300      * Creates a {@link ImsCallSession} with the specified call profile.
   1301      * Use other methods, if applicable, instead of interacting with
   1302      * {@link ImsCallSession} directly.
   1303      *
   1304      * @param serviceId a service id which is obtained from {@link ImsManager#open}
   1305      * @param profile a call profile to make the call
   1306      */
   1307     private ImsCallSession createCallSession(int serviceId,
   1308             ImsCallProfile profile) throws ImsException {
   1309         try {
   1310             return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
   1311         } catch (RemoteException e) {
   1312             return null;
   1313         }
   1314     }
   1315 
   1316     private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
   1317             ImsConnectionStateListener listener) {
   1318         ImsRegistrationListenerProxy proxy =
   1319                 new ImsRegistrationListenerProxy(serviceClass, listener);
   1320         return proxy;
   1321     }
   1322 
   1323     private static void log(String s) {
   1324         Rlog.d(TAG, s);
   1325     }
   1326 
   1327     private static void loge(String s) {
   1328         Rlog.e(TAG, s);
   1329     }
   1330 
   1331     private static void loge(String s, Throwable t) {
   1332         Rlog.e(TAG, s, t);
   1333     }
   1334 
   1335     /**
   1336      * Used for turning on IMS.if its off already
   1337      */
   1338     private void turnOnIms() throws ImsException {
   1339         checkAndThrowExceptionIfServiceUnavailable();
   1340 
   1341         try {
   1342             mImsService.turnOnIms(mPhoneId);
   1343         } catch (RemoteException e) {
   1344             throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1345         }
   1346     }
   1347 
   1348     private boolean isImsTurnOffAllowed() {
   1349         return isTurnOffImsAllowedByPlatform(mContext)
   1350                 && (!isWfcEnabledByPlatform(mContext)
   1351                 || !isWfcEnabledByUser(mContext));
   1352     }
   1353 
   1354     private void setLteFeatureValues(boolean turnOn) {
   1355         log("setLteFeatureValues: " + turnOn);
   1356         try {
   1357             ImsConfig config = getConfigInterface();
   1358             if (config != null) {
   1359                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
   1360                         TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, mImsConfigListener);
   1361 
   1362                 if (isVtEnabledByPlatform(mContext)) {
   1363                     boolean enableViLte = turnOn && isVtEnabledByUser(mContext) &&
   1364                             isDataEnabled();
   1365                     config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
   1366                             TelephonyManager.NETWORK_TYPE_LTE,
   1367                             enableViLte ? 1 : 0,
   1368                             mImsConfigListener);
   1369                 }
   1370             }
   1371         } catch (ImsException e) {
   1372             loge("setLteFeatureValues: exception ", e);
   1373         }
   1374     }
   1375 
   1376     private void setAdvanced4GMode(boolean turnOn) throws ImsException {
   1377         checkAndThrowExceptionIfServiceUnavailable();
   1378 
   1379         // if turnOn: first set feature values then call turnOnIms()
   1380         // if turnOff: only set feature values if IMS turn off is not allowed. If turn off is
   1381         // allowed, first call turnOffIms() then set feature values
   1382         if (turnOn) {
   1383             setLteFeatureValues(turnOn);
   1384             log("setAdvanced4GMode: turnOnIms");
   1385             turnOnIms();
   1386         } else {
   1387             if (isImsTurnOffAllowed()) {
   1388                 log("setAdvanced4GMode: turnOffIms");
   1389                 turnOffIms();
   1390             }
   1391             setLteFeatureValues(turnOn);
   1392         }
   1393     }
   1394 
   1395     /**
   1396      * Used for turning off IMS completely in order to make the device CSFB'ed.
   1397      * Once turned off, all calls will be over CS.
   1398      */
   1399     private void turnOffIms() throws ImsException {
   1400         checkAndThrowExceptionIfServiceUnavailable();
   1401 
   1402         try {
   1403             mImsService.turnOffIms(mPhoneId);
   1404         } catch (RemoteException e) {
   1405             throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1406         }
   1407     }
   1408 
   1409     /**
   1410      * Death recipient class for monitoring IMS service.
   1411      */
   1412     private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
   1413         @Override
   1414         public void binderDied() {
   1415             mImsService = null;
   1416             mUt = null;
   1417             mConfig = null;
   1418             mEcbm = null;
   1419             mMultiEndpoint = null;
   1420 
   1421             if (mContext != null) {
   1422                 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
   1423                 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
   1424                 mContext.sendBroadcast(new Intent(intent));
   1425             }
   1426         }
   1427     }
   1428 
   1429     /**
   1430      * Adapter class for {@link IImsRegistrationListener}.
   1431      */
   1432     private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
   1433         private int mServiceClass;
   1434         private ImsConnectionStateListener mListener;
   1435 
   1436         public ImsRegistrationListenerProxy(int serviceClass,
   1437                 ImsConnectionStateListener listener) {
   1438             mServiceClass = serviceClass;
   1439             mListener = listener;
   1440         }
   1441 
   1442         public boolean isSameProxy(int serviceClass) {
   1443             return (mServiceClass == serviceClass);
   1444         }
   1445 
   1446         @Deprecated
   1447         public void registrationConnected() {
   1448             if (DBG) {
   1449                 log("registrationConnected ::");
   1450             }
   1451 
   1452             if (mListener != null) {
   1453                 mListener.onImsConnected();
   1454             }
   1455         }
   1456 
   1457         @Deprecated
   1458         public void registrationProgressing() {
   1459             if (DBG) {
   1460                 log("registrationProgressing ::");
   1461             }
   1462 
   1463             if (mListener != null) {
   1464                 mListener.onImsProgressing();
   1465             }
   1466         }
   1467 
   1468         @Override
   1469         public void registrationConnectedWithRadioTech(int imsRadioTech) {
   1470             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
   1471             //       values in ServiceState.java.
   1472             if (DBG) {
   1473                 log("registrationConnectedWithRadioTech :: imsRadioTech=" + imsRadioTech);
   1474             }
   1475 
   1476             if (mListener != null) {
   1477                 mListener.onImsConnected();
   1478             }
   1479         }
   1480 
   1481         @Override
   1482         public void registrationProgressingWithRadioTech(int imsRadioTech) {
   1483             // Note: imsRadioTech value maps to RIL_RADIO_TECHNOLOGY
   1484             //       values in ServiceState.java.
   1485             if (DBG) {
   1486                 log("registrationProgressingWithRadioTech :: imsRadioTech=" + imsRadioTech);
   1487             }
   1488 
   1489             if (mListener != null) {
   1490                 mListener.onImsProgressing();
   1491             }
   1492         }
   1493 
   1494         @Override
   1495         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) {
   1496             if (DBG) {
   1497                 log("registrationDisconnected :: imsReasonInfo" + imsReasonInfo);
   1498             }
   1499 
   1500             if (mListener != null) {
   1501                 mListener.onImsDisconnected(imsReasonInfo);
   1502             }
   1503         }
   1504 
   1505         @Override
   1506         public void registrationResumed() {
   1507             if (DBG) {
   1508                 log("registrationResumed ::");
   1509             }
   1510 
   1511             if (mListener != null) {
   1512                 mListener.onImsResumed();
   1513             }
   1514         }
   1515 
   1516         @Override
   1517         public void registrationSuspended() {
   1518             if (DBG) {
   1519                 log("registrationSuspended ::");
   1520             }
   1521 
   1522             if (mListener != null) {
   1523                 mListener.onImsSuspended();
   1524             }
   1525         }
   1526 
   1527         @Override
   1528         public void registrationServiceCapabilityChanged(int serviceClass, int event) {
   1529             log("registrationServiceCapabilityChanged :: serviceClass=" +
   1530                     serviceClass + ", event=" + event);
   1531 
   1532             if (mListener != null) {
   1533                 mListener.onImsConnected();
   1534             }
   1535         }
   1536 
   1537         @Override
   1538         public void registrationFeatureCapabilityChanged(int serviceClass,
   1539                 int[] enabledFeatures, int[] disabledFeatures) {
   1540             log("registrationFeatureCapabilityChanged :: serviceClass=" +
   1541                     serviceClass);
   1542             if (mListener != null) {
   1543                 mListener.onFeatureCapabilityChanged(serviceClass,
   1544                         enabledFeatures, disabledFeatures);
   1545             }
   1546         }
   1547 
   1548         @Override
   1549         public void voiceMessageCountUpdate(int count) {
   1550             log("voiceMessageCountUpdate :: count=" + count);
   1551 
   1552             if (mListener != null) {
   1553                 mListener.onVoiceMessageCountChanged(count);
   1554             }
   1555         }
   1556 
   1557         @Override
   1558         public void registrationAssociatedUriChanged(Uri[] uris) {
   1559             if (DBG) log("registrationAssociatedUriChanged ::");
   1560 
   1561             if (mListener != null) {
   1562                 mListener.registrationAssociatedUriChanged(uris);
   1563             }
   1564         }
   1565     }
   1566 
   1567     /**
   1568      * Gets the ECBM interface to request ECBM exit.
   1569      *
   1570      * @param serviceId a service id which is obtained from {@link ImsManager#open}
   1571      * @return the ECBM interface instance
   1572      * @throws ImsException if getting the ECBM interface results in an error
   1573      */
   1574     public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
   1575         if (mEcbm == null) {
   1576             checkAndThrowExceptionIfServiceUnavailable();
   1577 
   1578             try {
   1579                 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
   1580 
   1581                 if (iEcbm == null) {
   1582                     throw new ImsException("getEcbmInterface()",
   1583                             ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
   1584                 }
   1585                 mEcbm = new ImsEcbm(iEcbm);
   1586             } catch (RemoteException e) {
   1587                 throw new ImsException("getEcbmInterface()", e,
   1588                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1589             }
   1590         }
   1591         return mEcbm;
   1592     }
   1593 
   1594     /**
   1595      * Gets the Multi-Endpoint interface to subscribe to multi-enpoint notifications..
   1596      *
   1597      * @param serviceId a service id which is obtained from {@link ImsManager#open}
   1598      * @return the multi-endpoint interface instance
   1599      * @throws ImsException if getting the multi-endpoint interface results in an error
   1600      */
   1601     public ImsMultiEndpoint getMultiEndpointInterface(int serviceId) throws ImsException {
   1602         if (mMultiEndpoint == null) {
   1603             checkAndThrowExceptionIfServiceUnavailable();
   1604 
   1605             try {
   1606                 IImsMultiEndpoint iImsMultiEndpoint = mImsService.getMultiEndpointInterface(
   1607                         serviceId);
   1608 
   1609                 if (iImsMultiEndpoint == null) {
   1610                     throw new ImsException("getMultiEndpointInterface()",
   1611                             ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED);
   1612                 }
   1613                 mMultiEndpoint = new ImsMultiEndpoint(iImsMultiEndpoint);
   1614             } catch (RemoteException e) {
   1615                 throw new ImsException("getMultiEndpointInterface()", e,
   1616                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
   1617             }
   1618         }
   1619         return mMultiEndpoint;
   1620     }
   1621 
   1622     /**
   1623      * Resets ImsManager settings back to factory defaults.
   1624      *
   1625      * @hide
   1626      */
   1627     public static void factoryReset(Context context) {
   1628         // Set VoLTE to default
   1629         android.provider.Settings.Global.putInt(context.getContentResolver(),
   1630                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED,
   1631                 ImsConfig.FeatureValueConstants.ON);
   1632 
   1633         // Set VoWiFi to default
   1634         android.provider.Settings.Global.putInt(context.getContentResolver(),
   1635                 android.provider.Settings.Global.WFC_IMS_ENABLED,
   1636                 getBooleanCarrierConfig(context,
   1637                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL) ?
   1638                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
   1639 
   1640         // Set VoWiFi mode to default
   1641         android.provider.Settings.Global.putInt(context.getContentResolver(),
   1642                 android.provider.Settings.Global.WFC_IMS_MODE,
   1643                 getIntCarrierConfig(context,
   1644                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
   1645 
   1646         // Set VoWiFi roaming to default
   1647         android.provider.Settings.Global.putInt(context.getContentResolver(),
   1648                 android.provider.Settings.Global.WFC_IMS_ROAMING_ENABLED,
   1649                 getBooleanCarrierConfig(context,
   1650                         CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL) ?
   1651                         ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF);
   1652 
   1653         // Set VT to default
   1654         android.provider.Settings.Global.putInt(context.getContentResolver(),
   1655                 android.provider.Settings.Global.VT_IMS_ENABLED,
   1656                 ImsConfig.FeatureValueConstants.ON);
   1657 
   1658         // Push settings to ImsConfig
   1659         ImsManager.updateImsServiceConfig(context,
   1660                 SubscriptionManager.getDefaultVoicePhoneId(), true);
   1661     }
   1662 
   1663     private boolean isDataEnabled() {
   1664         return SystemProperties.getBoolean(DATA_ENABLED_PROP, true);
   1665     }
   1666 
   1667     /**
   1668      * Set data enabled/disabled flag.
   1669      * @param enabled True if data is enabled, otherwise disabled.
   1670      */
   1671     public void setDataEnabled(boolean enabled) {
   1672         log("setDataEnabled: " + enabled);
   1673         SystemProperties.set(DATA_ENABLED_PROP, enabled ? TRUE : FALSE);
   1674     }
   1675 
   1676     private boolean isVolteProvisioned() {
   1677         return SystemProperties.getBoolean(VOLTE_PROVISIONED_PROP, true);
   1678     }
   1679 
   1680     private void setVolteProvisionedProperty(boolean provisioned) {
   1681         SystemProperties.set(VOLTE_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
   1682     }
   1683 
   1684     private boolean isWfcProvisioned() {
   1685         return SystemProperties.getBoolean(WFC_PROVISIONED_PROP, true);
   1686     }
   1687 
   1688     private void setWfcProvisionedProperty(boolean provisioned) {
   1689         SystemProperties.set(WFC_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
   1690     }
   1691 
   1692     private boolean isVtProvisioned() {
   1693         return SystemProperties.getBoolean(VT_PROVISIONED_PROP, true);
   1694     }
   1695 
   1696     private void setVtProvisionedProperty(boolean provisioned) {
   1697         SystemProperties.set(VT_PROVISIONED_PROP, provisioned ? TRUE : FALSE);
   1698     }
   1699 
   1700     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1701         pw.println("ImsManager:");
   1702         pw.println("  mPhoneId = " + mPhoneId);
   1703         pw.println("  mConfigUpdated = " + mConfigUpdated);
   1704         pw.println("  mImsService = " + mImsService);
   1705         pw.println("  mDataEnabled = " + isDataEnabled());
   1706 
   1707         pw.println("  isGbaValid = " + isGbaValid(mContext));
   1708         pw.println("  isImsTurnOffAllowed = " + isImsTurnOffAllowed());
   1709         pw.println("  isNonTtyOrTtyOnVolteEnabled = " + isNonTtyOrTtyOnVolteEnabled(mContext));
   1710 
   1711         pw.println("  isVolteEnabledByPlatform = " + isVolteEnabledByPlatform(mContext));
   1712         pw.println("  isVolteProvisionedOnDevice = " + isVolteProvisionedOnDevice(mContext));
   1713         pw.println("  isEnhanced4gLteModeSettingEnabledByUser = " +
   1714                 isEnhanced4gLteModeSettingEnabledByUser(mContext));
   1715         pw.println("  isVtEnabledByPlatform = " + isVtEnabledByPlatform(mContext));
   1716         pw.println("  isVtEnabledByUser = " + isVtEnabledByUser(mContext));
   1717 
   1718         pw.println("  isWfcEnabledByPlatform = " + isWfcEnabledByPlatform(mContext));
   1719         pw.println("  isWfcEnabledByUser = " + isWfcEnabledByUser(mContext));
   1720         pw.println("  getWfcMode = " + getWfcMode(mContext));
   1721         pw.println("  isWfcRoamingEnabledByUser = " + isWfcRoamingEnabledByUser(mContext));
   1722 
   1723         pw.println("  isVtProvisionedOnDevice = " + isVtProvisionedOnDevice(mContext));
   1724         pw.println("  isWfcProvisionedOnDevice = " + isWfcProvisionedOnDevice(mContext));
   1725         pw.flush();
   1726     }
   1727 }
   1728