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