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