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