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.os.IBinder;
     23 import android.os.Message;
     24 import android.os.RemoteException;
     25 import android.os.ServiceManager;
     26 import android.os.SystemProperties;
     27 import android.provider.Settings;
     28 import android.telecom.TelecomManager;
     29 import android.telephony.Rlog;
     30 import android.telephony.SubscriptionManager;
     31 import android.telephony.TelephonyManager;
     32 
     33 import com.android.ims.internal.IImsCallSession;
     34 import com.android.ims.internal.IImsEcbm;
     35 import com.android.ims.internal.IImsEcbmListener;
     36 import com.android.ims.internal.IImsRegistrationListener;
     37 import com.android.ims.internal.IImsService;
     38 import com.android.ims.internal.IImsUt;
     39 import com.android.ims.internal.ImsCallSession;
     40 import com.android.ims.internal.IImsConfig;
     41 
     42 import java.util.HashMap;
     43 
     44 /**
     45  * Provides APIs for IMS services, such as initiating IMS calls, and provides access to
     46  * the operator's IMS network. This class is the starting point for any IMS actions.
     47  * You can acquire an instance of it with {@link #getInstance getInstance()}.</p>
     48  * <p>The APIs in this class allows you to:</p>
     49  *
     50  * @hide
     51  */
     52 public class ImsManager {
     53 
     54     /*
     55      * Debug flag to override configuration flag
     56      */
     57     public static final String PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE = "persist.dbg.volte_avail_ovr";
     58     public static final int PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT = 0;
     59     public static final String PROPERTY_DBG_VT_AVAIL_OVERRIDE = "persist.dbg.vt_avail_ovr";
     60     public static final int PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT = 0;
     61 
     62     /**
     63      * For accessing the IMS related service.
     64      * Internal use only.
     65      * @hide
     66      */
     67     private static final String IMS_SERVICE = "ims";
     68 
     69     /**
     70      * The result code to be sent back with the incoming call {@link PendingIntent}.
     71      * @see #open(PendingIntent, ImsConnectionStateListener)
     72      */
     73     public static final int INCOMING_CALL_RESULT_CODE = 101;
     74 
     75     /**
     76      * Key to retrieve the call ID from an incoming call intent.
     77      * @see #open(PendingIntent, ImsConnectionStateListener)
     78      */
     79     public static final String EXTRA_CALL_ID = "android:imsCallID";
     80 
     81     /**
     82      * Action to broadcast when ImsService is up.
     83      * Internal use only.
     84      * @hide
     85      */
     86     public static final String ACTION_IMS_SERVICE_UP =
     87             "com.android.ims.IMS_SERVICE_UP";
     88 
     89     /**
     90      * Action to broadcast when ImsService is down.
     91      * Internal use only.
     92      * @hide
     93      */
     94     public static final String ACTION_IMS_SERVICE_DOWN =
     95             "com.android.ims.IMS_SERVICE_DOWN";
     96 
     97     /**
     98      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
     99      * A long value; the phone ID corresponding to the IMS service coming up or down.
    100      * Internal use only.
    101      * @hide
    102      */
    103     public static final String EXTRA_PHONE_ID = "android:phone_id";
    104 
    105     /**
    106      * Action for the incoming call intent for the Phone app.
    107      * Internal use only.
    108      * @hide
    109      */
    110     public static final String ACTION_IMS_INCOMING_CALL =
    111             "com.android.ims.IMS_INCOMING_CALL";
    112 
    113     /**
    114      * Part of the ACTION_IMS_INCOMING_CALL intents.
    115      * An integer value; service identifier obtained from {@link ImsManager#open}.
    116      * Internal use only.
    117      * @hide
    118      */
    119     public static final String EXTRA_SERVICE_ID = "android:imsServiceId";
    120 
    121     /**
    122      * Part of the ACTION_IMS_INCOMING_CALL intents.
    123      * An boolean value; Flag to indicate that the incoming call is a normal call or call for USSD.
    124      * The value "true" indicates that the incoming call is for USSD.
    125      * Internal use only.
    126      * @hide
    127      */
    128     public static final String EXTRA_USSD = "android:ussd";
    129 
    130     private static final String TAG = "ImsManager";
    131     private static final boolean DBG = true;
    132 
    133     private static HashMap<Integer, ImsManager> sImsManagerInstances =
    134             new HashMap<Integer, ImsManager>();
    135 
    136     private Context mContext;
    137     private int mPhoneId;
    138     private IImsService mImsService = null;
    139     private ImsServiceDeathRecipient mDeathRecipient = new ImsServiceDeathRecipient();
    140     // Ut interface for the supplementary service configuration
    141     private ImsUt mUt = null;
    142     // Interface to get/set ims config items
    143     private ImsConfig mConfig = null;
    144 
    145     // ECBM interface
    146     private ImsEcbm mEcbm = null;
    147 
    148     /**
    149      * Gets a manager instance.
    150      *
    151      * @param context application context for creating the manager object
    152      * @param phoneId the phone ID for the IMS Service
    153      * @return the manager instance corresponding to the phoneId
    154      */
    155     public static ImsManager getInstance(Context context, int phoneId) {
    156         synchronized (sImsManagerInstances) {
    157             if (sImsManagerInstances.containsKey(phoneId))
    158                 return sImsManagerInstances.get(phoneId);
    159 
    160             ImsManager mgr = new ImsManager(context, phoneId);
    161             sImsManagerInstances.put(phoneId, mgr);
    162 
    163             return mgr;
    164         }
    165     }
    166 
    167     /**
    168      * Returns the user configuration of Enhanced 4G LTE Mode setting
    169      */
    170     public static boolean isEnhanced4gLteModeSettingEnabledByUser(Context context) {
    171         int enabled = android.provider.Settings.Global.getInt(
    172                     context.getContentResolver(),
    173                     android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, ImsConfig.FeatureValueConstants.ON);
    174         return (enabled == 1)? true:false;
    175     }
    176 
    177     /**
    178      * Change persistent Enhanced 4G LTE Mode setting
    179      */
    180     public static void setEnhanced4gLteModeSetting(Context context, boolean enabled) {
    181         int value = enabled ? 1 : 0;
    182         android.provider.Settings.Global.putInt(
    183                 context.getContentResolver(),
    184                 android.provider.Settings.Global.ENHANCED_4G_MODE_ENABLED, value);
    185 
    186         if (isNonTtyOrTtyOnVolteEnabled(context)) {
    187             ImsManager imsManager = ImsManager.getInstance(context,
    188                     SubscriptionManager.getDefaultVoicePhoneId());
    189             if (imsManager != null) {
    190                 try {
    191                     imsManager.setAdvanced4GMode(enabled);
    192                 } catch (ImsException ie) {
    193                     // do nothing
    194                 }
    195             }
    196         }
    197     }
    198 
    199     /**
    200      * Indicates whether the call is non-TTY or if TTY - whether TTY on VoLTE is
    201      * supported.
    202      */
    203     public static boolean isNonTtyOrTtyOnVolteEnabled(Context context) {
    204         if (context.getResources().getBoolean(
    205                 com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
    206             return true;
    207         }
    208 
    209         return Settings.Secure.getInt(context.getContentResolver(),
    210                 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF)
    211                 == TelecomManager.TTY_MODE_OFF;
    212     }
    213 
    214     /**
    215      * Returns a platform configuration for VoLTE which may override the user setting.
    216      */
    217     public static boolean isVolteEnabledByPlatform(Context context) {
    218         if (SystemProperties.getInt(PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE,
    219                 PROPERTY_DBG_VOLTE_AVAIL_OVERRIDE_DEFAULT) == 1) {
    220             return true;
    221         }
    222 
    223         boolean disabledByGlobalSetting = android.provider.Settings.Global.getInt(
    224                 context.getContentResolver(),
    225                 android.provider.Settings.Global.VOLTE_FEATURE_DISABLED, 0) == 1;
    226 
    227         return context.getResources().getBoolean(
    228                 com.android.internal.R.bool.config_device_volte_available) && context.getResources()
    229                 .getBoolean(com.android.internal.R.bool.config_carrier_volte_available)
    230                 && !disabledByGlobalSetting;
    231     }
    232 
    233     /*
    234      * Indicates whether VoLTE is provisioned on device
    235      */
    236     public static boolean isVolteProvisionedOnDevice(Context context) {
    237         boolean isProvisioned = true;
    238         if (context.getResources().getBoolean(
    239                         com.android.internal.R.bool.config_carrier_volte_provisioned)) {
    240             isProvisioned = false; // disable on any error
    241             ImsManager mgr = ImsManager.getInstance(context,
    242                     SubscriptionManager.getDefaultVoiceSubId());
    243             if (mgr != null) {
    244                 try {
    245                     ImsConfig config = mgr.getConfigInterface();
    246                     if (config != null) {
    247                         isProvisioned = config.getVolteProvisioned();
    248                     }
    249                 } catch (ImsException ie) {
    250                     // do nothing
    251                 }
    252             }
    253         }
    254 
    255         return isProvisioned;
    256     }
    257 
    258     /**
    259      * Returns a platform configuration for VT which may override the user setting.
    260      *
    261      * Note: VT presumes that VoLTE is enabled (these are configuration settings
    262      * which must be done correctly).
    263      */
    264     public static boolean isVtEnabledByPlatform(Context context) {
    265         if (SystemProperties.getInt(PROPERTY_DBG_VT_AVAIL_OVERRIDE,
    266                 PROPERTY_DBG_VT_AVAIL_OVERRIDE_DEFAULT) == 1) {
    267             return true;
    268         }
    269 
    270         return
    271                 context.getResources().getBoolean(
    272                         com.android.internal.R.bool.config_device_vt_available) &&
    273                 context.getResources().getBoolean(
    274                         com.android.internal.R.bool.config_carrier_vt_available);
    275     }
    276 
    277     private ImsManager(Context context, int phoneId) {
    278         mContext = context;
    279         mPhoneId = phoneId;
    280         createImsService(true);
    281     }
    282 
    283     /*
    284      * Returns a flag indicating whether the IMS service is available.
    285      */
    286     public boolean isServiceAvailable() {
    287         if (mImsService != null) {
    288             return true;
    289         }
    290 
    291         IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
    292         if (binder != null) {
    293             return true;
    294         }
    295 
    296         return false;
    297     }
    298 
    299     /**
    300      * Opens the IMS service for making calls and/or receiving generic IMS calls.
    301      * The caller may make subsquent calls through {@link #makeCall}.
    302      * The IMS service will register the device to the operator's network with the credentials
    303      * (from ISIM) periodically in order to receive calls from the operator's network.
    304      * When the IMS service receives a new call, it will send out an intent with
    305      * the provided action string.
    306      * The intent contains a call ID extra {@link getCallId} and it can be used to take a call.
    307      *
    308      * @param serviceClass a service class specified in {@link ImsServiceClass}
    309      *      For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
    310      * @param incomingCallPendingIntent When an incoming call is received,
    311      *        the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
    312      *        send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
    313      *        as the result code and the intent to fill in the call ID; It cannot be null
    314      * @param listener To listen to IMS registration events; It cannot be null
    315      * @return identifier (greater than 0) for the specified service
    316      * @throws NullPointerException if {@code incomingCallPendingIntent}
    317      *      or {@code listener} is null
    318      * @throws ImsException if calling the IMS service results in an error
    319      * @see #getCallId
    320      * @see #getServiceId
    321      */
    322     public int open(int serviceClass, PendingIntent incomingCallPendingIntent,
    323             ImsConnectionStateListener listener) throws ImsException {
    324         checkAndThrowExceptionIfServiceUnavailable();
    325 
    326         if (incomingCallPendingIntent == null) {
    327             throw new NullPointerException("incomingCallPendingIntent can't be null");
    328         }
    329 
    330         if (listener == null) {
    331             throw new NullPointerException("listener can't be null");
    332         }
    333 
    334         int result = 0;
    335 
    336         try {
    337             result = mImsService.open(mPhoneId, serviceClass, incomingCallPendingIntent,
    338                     createRegistrationListenerProxy(serviceClass, listener));
    339         } catch (RemoteException e) {
    340             throw new ImsException("open()", e,
    341                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    342         }
    343 
    344         if (result <= 0) {
    345             // If the return value is a minus value,
    346             // it means that an error occurred in the service.
    347             // So, it needs to convert to the reason code specified in ImsReasonInfo.
    348             throw new ImsException("open()", (result * (-1)));
    349         }
    350 
    351         return result;
    352     }
    353 
    354     /**
    355      * Closes the specified service ({@link ImsServiceClass}) not to make/receive calls.
    356      * All the resources that were allocated to the service are also released.
    357      *
    358      * @param serviceId a service id to be closed which is obtained from {@link ImsManager#open}
    359      * @throws ImsException if calling the IMS service results in an error
    360      */
    361     public void close(int serviceId) throws ImsException {
    362         checkAndThrowExceptionIfServiceUnavailable();
    363 
    364         try {
    365             mImsService.close(serviceId);
    366         } catch (RemoteException e) {
    367             throw new ImsException("close()", e,
    368                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    369         } finally {
    370             mUt = null;
    371             mConfig = null;
    372             mEcbm = null;
    373         }
    374     }
    375 
    376     /**
    377      * Gets the configuration interface to provision / withdraw the supplementary service settings.
    378      *
    379      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    380      * @return the Ut interface instance
    381      * @throws ImsException if getting the Ut interface results in an error
    382      */
    383     public ImsUtInterface getSupplementaryServiceConfiguration(int serviceId)
    384             throws ImsException {
    385         // FIXME: manage the multiple Ut interfaces based on the service id
    386         if (mUt == null) {
    387             checkAndThrowExceptionIfServiceUnavailable();
    388 
    389             try {
    390                 IImsUt iUt = mImsService.getUtInterface(serviceId);
    391 
    392                 if (iUt == null) {
    393                     throw new ImsException("getSupplementaryServiceConfiguration()",
    394                             ImsReasonInfo.CODE_UT_NOT_SUPPORTED);
    395                 }
    396 
    397                 mUt = new ImsUt(iUt);
    398             } catch (RemoteException e) {
    399                 throw new ImsException("getSupplementaryServiceConfiguration()", e,
    400                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    401             }
    402         }
    403 
    404         return mUt;
    405     }
    406 
    407     /**
    408      * Checks if the IMS service has successfully registered to the IMS network
    409      * with the specified service & call type.
    410      *
    411      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    412      * @param serviceType a service type that is specified in {@link ImsCallProfile}
    413      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
    414      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
    415      * @param callType a call type that is specified in {@link ImsCallProfile}
    416      *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
    417      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
    418      *        {@link ImsCallProfile#CALL_TYPE_VT}
    419      *        {@link ImsCallProfile#CALL_TYPE_VS}
    420      * @return true if the specified service id is connected to the IMS network;
    421      *        false otherwise
    422      * @throws ImsException if calling the IMS service results in an error
    423      */
    424     public boolean isConnected(int serviceId, int serviceType, int callType)
    425             throws ImsException {
    426         checkAndThrowExceptionIfServiceUnavailable();
    427 
    428         try {
    429             return mImsService.isConnected(serviceId, serviceType, callType);
    430         } catch (RemoteException e) {
    431             throw new ImsException("isServiceConnected()", e,
    432                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    433         }
    434     }
    435 
    436     /**
    437      * Checks if the specified IMS service is opend.
    438      *
    439      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    440      * @return true if the specified service id is opened; false otherwise
    441      * @throws ImsException if calling the IMS service results in an error
    442      */
    443     public boolean isOpened(int serviceId) throws ImsException {
    444         checkAndThrowExceptionIfServiceUnavailable();
    445 
    446         try {
    447             return mImsService.isOpened(serviceId);
    448         } catch (RemoteException e) {
    449             throw new ImsException("isOpened()", e,
    450                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    451         }
    452     }
    453 
    454     /**
    455      * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
    456      *
    457      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    458      * @param serviceType a service type that is specified in {@link ImsCallProfile}
    459      *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
    460      *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
    461      *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
    462      * @param callType a call type that is specified in {@link ImsCallProfile}
    463      *        {@link ImsCallProfile#CALL_TYPE_VOICE}
    464      *        {@link ImsCallProfile#CALL_TYPE_VT}
    465      *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
    466      *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
    467      *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
    468      *        {@link ImsCallProfile#CALL_TYPE_VS}
    469      *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
    470      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
    471      * @return a {@link ImsCallProfile} object
    472      * @throws ImsException if calling the IMS service results in an error
    473      */
    474     public ImsCallProfile createCallProfile(int serviceId,
    475             int serviceType, int callType) throws ImsException {
    476         checkAndThrowExceptionIfServiceUnavailable();
    477 
    478         try {
    479             return mImsService.createCallProfile(serviceId, serviceType, callType);
    480         } catch (RemoteException e) {
    481             throw new ImsException("createCallProfile()", e,
    482                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    483         }
    484     }
    485 
    486     /**
    487      * Creates a {@link ImsCall} to make a call.
    488      *
    489      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    490      * @param profile a call profile to make the call
    491      *      (it contains service type, call type, media information, etc.)
    492      * @param participants participants to invite the conference call
    493      * @param listener listen to the call events from {@link ImsCall}
    494      * @return a {@link ImsCall} object
    495      * @throws ImsException if calling the IMS service results in an error
    496      */
    497     public ImsCall makeCall(int serviceId, ImsCallProfile profile, String[] callees,
    498             ImsCall.Listener listener) throws ImsException {
    499         if (DBG) {
    500             log("makeCall :: serviceId=" + serviceId
    501                     + ", profile=" + profile + ", callees=" + callees);
    502         }
    503 
    504         checkAndThrowExceptionIfServiceUnavailable();
    505 
    506         ImsCall call = new ImsCall(mContext, profile);
    507 
    508         call.setListener(listener);
    509         ImsCallSession session = createCallSession(serviceId, profile);
    510 
    511         if ((callees != null) && (callees.length == 1)) {
    512             call.start(session, callees[0]);
    513         } else {
    514             call.start(session, callees);
    515         }
    516 
    517         return call;
    518     }
    519 
    520     /**
    521      * Creates a {@link ImsCall} to take an incoming call.
    522      *
    523      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    524      * @param incomingCallIntent the incoming call broadcast intent
    525      * @param listener to listen to the call events from {@link ImsCall}
    526      * @return a {@link ImsCall} object
    527      * @throws ImsException if calling the IMS service results in an error
    528      */
    529     public ImsCall takeCall(int serviceId, Intent incomingCallIntent,
    530             ImsCall.Listener listener) throws ImsException {
    531         if (DBG) {
    532             log("takeCall :: serviceId=" + serviceId
    533                     + ", incomingCall=" + incomingCallIntent);
    534         }
    535 
    536         checkAndThrowExceptionIfServiceUnavailable();
    537 
    538         if (incomingCallIntent == null) {
    539             throw new ImsException("Can't retrieve session with null intent",
    540                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
    541         }
    542 
    543         int incomingServiceId = getServiceId(incomingCallIntent);
    544 
    545         if (serviceId != incomingServiceId) {
    546             throw new ImsException("Service id is mismatched in the incoming call intent",
    547                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
    548         }
    549 
    550         String callId = getCallId(incomingCallIntent);
    551 
    552         if (callId == null) {
    553             throw new ImsException("Call ID missing in the incoming call intent",
    554                     ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT);
    555         }
    556 
    557         try {
    558             IImsCallSession session = mImsService.getPendingCallSession(serviceId, callId);
    559 
    560             if (session == null) {
    561                 throw new ImsException("No pending session for the call",
    562                         ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL);
    563             }
    564 
    565             ImsCall call = new ImsCall(mContext, session.getCallProfile());
    566 
    567             call.attachSession(new ImsCallSession(session));
    568             call.setListener(listener);
    569 
    570             return call;
    571         } catch (Throwable t) {
    572             throw new ImsException("takeCall()", t, ImsReasonInfo.CODE_UNSPECIFIED);
    573         }
    574     }
    575 
    576     /**
    577      * Gets the config interface to get/set service/capability parameters.
    578      *
    579      * @return the ImsConfig instance.
    580      * @throws ImsException if getting the setting interface results in an error.
    581      */
    582     public ImsConfig getConfigInterface() throws ImsException {
    583 
    584         if (mConfig == null) {
    585             checkAndThrowExceptionIfServiceUnavailable();
    586 
    587             try {
    588                 IImsConfig config = mImsService.getConfigInterface(mPhoneId);
    589                 if (config == null) {
    590                     throw new ImsException("getConfigInterface()",
    591                             ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
    592                 }
    593                 mConfig = new ImsConfig(config, mContext);
    594             } catch (RemoteException e) {
    595                 throw new ImsException("getConfigInterface()", e,
    596                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    597             }
    598         }
    599         if (DBG) log("getConfigInterface(), mConfig= " + mConfig);
    600         return mConfig;
    601     }
    602 
    603     public void setUiTTYMode(Context context, int serviceId, int uiTtyMode, Message onComplete)
    604             throws ImsException {
    605 
    606         checkAndThrowExceptionIfServiceUnavailable();
    607 
    608         try {
    609             mImsService.setUiTTYMode(serviceId, uiTtyMode, onComplete);
    610         } catch (RemoteException e) {
    611             throw new ImsException("setTTYMode()", e,
    612                     ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    613         }
    614 
    615         if (!context.getResources().getBoolean(
    616                 com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
    617             setAdvanced4GMode((uiTtyMode == TelecomManager.TTY_MODE_OFF) &&
    618                     isEnhanced4gLteModeSettingEnabledByUser(context));
    619         }
    620     }
    621 
    622     /**
    623      * Gets the call ID from the specified incoming call broadcast intent.
    624      *
    625      * @param incomingCallIntent the incoming call broadcast intent
    626      * @return the call ID or null if the intent does not contain it
    627      */
    628     private static String getCallId(Intent incomingCallIntent) {
    629         if (incomingCallIntent == null) {
    630             return null;
    631         }
    632 
    633         return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
    634     }
    635 
    636     /**
    637      * Gets the service type from the specified incoming call broadcast intent.
    638      *
    639      * @param incomingCallIntent the incoming call broadcast intent
    640      * @return the service identifier or -1 if the intent does not contain it
    641      */
    642     private static int getServiceId(Intent incomingCallIntent) {
    643         if (incomingCallIntent == null) {
    644             return (-1);
    645         }
    646 
    647         return incomingCallIntent.getIntExtra(EXTRA_SERVICE_ID, -1);
    648     }
    649 
    650     /**
    651      * Binds the IMS service only if the service is not created.
    652      */
    653     private void checkAndThrowExceptionIfServiceUnavailable()
    654             throws ImsException {
    655         if (mImsService == null) {
    656             createImsService(true);
    657 
    658             if (mImsService == null) {
    659                 throw new ImsException("Service is unavailable",
    660                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    661             }
    662         }
    663     }
    664 
    665     private static String getImsServiceName(int phoneId) {
    666         // TODO: MSIM implementation needs to decide on service name as a function of phoneId
    667         return IMS_SERVICE;
    668     }
    669 
    670     /**
    671      * Binds the IMS service to make/receive the call.
    672      */
    673     private void createImsService(boolean checkService) {
    674         if (checkService) {
    675             IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
    676 
    677             if (binder == null) {
    678                 return;
    679             }
    680         }
    681 
    682         IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
    683 
    684         if (b != null) {
    685             try {
    686                 b.linkToDeath(mDeathRecipient, 0);
    687             } catch (RemoteException e) {
    688             }
    689         }
    690 
    691         mImsService = IImsService.Stub.asInterface(b);
    692     }
    693 
    694     /**
    695      * Creates a {@link ImsCallSession} with the specified call profile.
    696      * Use other methods, if applicable, instead of interacting with
    697      * {@link ImsCallSession} directly.
    698      *
    699      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    700      * @param profile a call profile to make the call
    701      */
    702     private ImsCallSession createCallSession(int serviceId,
    703             ImsCallProfile profile) throws ImsException {
    704         try {
    705             return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
    706         } catch (RemoteException e) {
    707             return null;
    708         }
    709     }
    710 
    711     private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
    712             ImsConnectionStateListener listener) {
    713         ImsRegistrationListenerProxy proxy =
    714                 new ImsRegistrationListenerProxy(serviceClass, listener);
    715         return proxy;
    716     }
    717 
    718     private void log(String s) {
    719         Rlog.d(TAG, s);
    720     }
    721 
    722     private void loge(String s) {
    723         Rlog.e(TAG, s);
    724     }
    725 
    726     private void loge(String s, Throwable t) {
    727         Rlog.e(TAG, s, t);
    728     }
    729 
    730     /**
    731      * Used for turning on IMS.if its off already
    732      */
    733     private void turnOnIms() throws ImsException {
    734         checkAndThrowExceptionIfServiceUnavailable();
    735 
    736         try {
    737             mImsService.turnOnIms(mPhoneId);
    738         } catch (RemoteException e) {
    739             throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    740         }
    741     }
    742 
    743     private void setAdvanced4GMode(boolean turnOn) throws ImsException {
    744         checkAndThrowExceptionIfServiceUnavailable();
    745 
    746         ImsConfig config = getConfigInterface();
    747         if (config != null) {
    748             config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
    749                     TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
    750             if (isVtEnabledByPlatform(mContext)) {
    751                 // TODO: once VT is available on platform replace the '1' with the current
    752                 // user configuration of VT.
    753                 config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
    754                         TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
    755             }
    756         }
    757 
    758         if (turnOn) {
    759             turnOnIms();
    760         } else if (mContext.getResources().getBoolean(
    761                 com.android.internal.R.bool.imsServiceAllowTurnOff)) {
    762             log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
    763             turnOffIms();
    764         }
    765     }
    766 
    767     /**
    768      * Used for turning off IMS completely in order to make the device CSFB'ed.
    769      * Once turned off, all calls will be over CS.
    770      */
    771     private void turnOffIms() throws ImsException {
    772         checkAndThrowExceptionIfServiceUnavailable();
    773 
    774         try {
    775             mImsService.turnOffIms(mPhoneId);
    776         } catch (RemoteException e) {
    777             throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    778         }
    779     }
    780 
    781     /**
    782      * Death recipient class for monitoring IMS service.
    783      */
    784     private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
    785         @Override
    786         public void binderDied() {
    787             mImsService = null;
    788             mUt = null;
    789             mConfig = null;
    790             mEcbm = null;
    791 
    792             if (mContext != null) {
    793                 Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
    794                 intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
    795                 mContext.sendBroadcast(new Intent(intent));
    796             }
    797         }
    798     }
    799 
    800     /**
    801      * Adapter class for {@link IImsRegistrationListener}.
    802      */
    803     private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
    804         private int mServiceClass;
    805         private ImsConnectionStateListener mListener;
    806 
    807         public ImsRegistrationListenerProxy(int serviceClass,
    808                 ImsConnectionStateListener listener) {
    809             mServiceClass = serviceClass;
    810             mListener = listener;
    811         }
    812 
    813         public boolean isSameProxy(int serviceClass) {
    814             return (mServiceClass == serviceClass);
    815         }
    816 
    817         @Override
    818         public void registrationConnected() {
    819             if (DBG) {
    820                 log("registrationConnected ::");
    821             }
    822 
    823             if (mListener != null) {
    824                 mListener.onImsConnected();
    825             }
    826         }
    827 
    828         @Override
    829         public void registrationDisconnected() {
    830             if (DBG) {
    831                 log("registrationDisconnected ::");
    832             }
    833 
    834             if (mListener != null) {
    835                 mListener.onImsDisconnected();
    836             }
    837         }
    838 
    839         @Override
    840         public void registrationResumed() {
    841             if (DBG) {
    842                 log("registrationResumed ::");
    843             }
    844 
    845             if (mListener != null) {
    846                 mListener.onImsResumed();
    847             }
    848         }
    849 
    850         @Override
    851         public void registrationSuspended() {
    852             if (DBG) {
    853                 log("registrationSuspended ::");
    854             }
    855 
    856             if (mListener != null) {
    857                 mListener.onImsSuspended();
    858             }
    859         }
    860 
    861         @Override
    862         public void registrationServiceCapabilityChanged(int serviceClass, int event) {
    863             log("registrationServiceCapabilityChanged :: serviceClass=" +
    864                     serviceClass + ", event=" + event);
    865 
    866             if (mListener != null) {
    867                 mListener.onImsConnected();
    868             }
    869         }
    870 
    871         @Override
    872         public void registrationFeatureCapabilityChanged(int serviceClass,
    873                 int[] enabledFeatures, int[] disabledFeatures) {
    874             log("registrationFeatureCapabilityChanged :: serviceClass=" +
    875                     serviceClass);
    876             if (mListener != null) {
    877                 mListener.onFeatureCapabilityChanged(serviceClass,
    878                         enabledFeatures, disabledFeatures);
    879             }
    880         }
    881 
    882     }
    883     /**
    884      * Gets the ECBM interface to request ECBM exit.
    885      *
    886      * @param serviceId a service id which is obtained from {@link ImsManager#open}
    887      * @return the ECBM interface instance
    888      * @throws ImsException if getting the ECBM interface results in an error
    889      */
    890     public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
    891         if (mEcbm == null) {
    892             checkAndThrowExceptionIfServiceUnavailable();
    893 
    894             try {
    895                 IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
    896 
    897                 if (iEcbm == null) {
    898                     throw new ImsException("getEcbmInterface()",
    899                             ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
    900                 }
    901                 mEcbm = new ImsEcbm(iEcbm);
    902             } catch (RemoteException e) {
    903                 throw new ImsException("getEcbmInterface()", e,
    904                         ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
    905             }
    906         }
    907         return mEcbm;
    908     }
    909 }
    910