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