ImsManager.java revision abbd7880cf8e29274dfc0508568fbffe9f49cad7
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 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        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 phoneId) {
212        mContext = context;
213        mPhoneId = phoneId;
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(mPhoneId, 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(mPhoneId);
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 phoneId) {
578        // TODO: MSIM implementation needs to decide on service name as a function of phoneId
579        return IMS_SERVICE;
580    }
581
582    /**
583     * Binds the IMS service to make/receive the call.
584     */
585    private void createImsService(boolean checkService) {
586        if (checkService) {
587            IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId));
588
589            if (binder == null) {
590                return;
591            }
592        }
593
594        IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId));
595
596        if (b != null) {
597            try {
598                b.linkToDeath(mDeathRecipient, 0);
599            } catch (RemoteException e) {
600            }
601        }
602
603        mImsService = IImsService.Stub.asInterface(b);
604    }
605
606    /**
607     * Creates a {@link ImsCallSession} with the specified call profile.
608     * Use other methods, if applicable, instead of interacting with
609     * {@link ImsCallSession} directly.
610     *
611     * @param serviceId a service id which is obtained from {@link ImsManager#open}
612     * @param profile a call profile to make the call
613     */
614    private ImsCallSession createCallSession(int serviceId,
615            ImsCallProfile profile) throws ImsException {
616        try {
617            return new ImsCallSession(mImsService.createCallSession(serviceId, profile, null));
618        } catch (RemoteException e) {
619            return null;
620        }
621    }
622
623    private ImsRegistrationListenerProxy createRegistrationListenerProxy(int serviceClass,
624            ImsConnectionStateListener listener) {
625        ImsRegistrationListenerProxy proxy =
626                new ImsRegistrationListenerProxy(serviceClass, listener);
627        return proxy;
628    }
629
630    private void log(String s) {
631        Rlog.d(TAG, s);
632    }
633
634    private void loge(String s) {
635        Rlog.e(TAG, s);
636    }
637
638    private void loge(String s, Throwable t) {
639        Rlog.e(TAG, s, t);
640    }
641
642    /**
643     * Used for turning on IMS.if its off already
644     */
645    public void turnOnIms() throws ImsException {
646        checkAndThrowExceptionIfServiceUnavailable();
647
648        try {
649            mImsService.turnOnIms(mPhoneId);
650        } catch (RemoteException e) {
651            throw new ImsException("turnOnIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
652        }
653    }
654
655    public void setAdvanced4GMode(boolean turnOn) throws ImsException {
656        checkAndThrowExceptionIfServiceUnavailable();
657
658        ImsConfig config = getConfigInterface();
659        if (config != null) {
660            config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE,
661                    TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
662            if (isVtEnabledByPlatform(mContext)) {
663                // TODO: once VT is available on platform replace the '1' with the current
664                // user configuration of VT.
665                config.setFeatureValue(ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE,
666                        TelephonyManager.NETWORK_TYPE_LTE, turnOn ? 1 : 0, null);
667            }
668        }
669
670        if (turnOn) {
671            turnOnIms();
672        } else if (mContext.getResources().getBoolean(
673                com.android.internal.R.bool.imsServiceAllowTurnOff)) {
674            log("setAdvanced4GMode() : imsServiceAllowTurnOff -> turnOffIms");
675            turnOffIms();
676        }
677    }
678
679    /**
680     * Used for turning off IMS completely in order to make the device CSFB'ed.
681     * Once turned off, all calls will be over CS.
682     */
683    public void turnOffIms() throws ImsException {
684        checkAndThrowExceptionIfServiceUnavailable();
685
686        try {
687            mImsService.turnOffIms(mPhoneId);
688        } catch (RemoteException e) {
689            throw new ImsException("turnOffIms() ", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
690        }
691    }
692
693    /**
694     * Death recipient class for monitoring IMS service.
695     */
696    private class ImsServiceDeathRecipient implements IBinder.DeathRecipient {
697        @Override
698        public void binderDied() {
699            mImsService = null;
700            mUt = null;
701            mConfig = null;
702            mEcbm = null;
703
704            if (mContext != null) {
705                Intent intent = new Intent(ACTION_IMS_SERVICE_DOWN);
706                intent.putExtra(EXTRA_PHONE_ID, mPhoneId);
707                mContext.sendBroadcast(new Intent(intent));
708            }
709        }
710    }
711
712    /**
713     * Adapter class for {@link IImsRegistrationListener}.
714     */
715    private class ImsRegistrationListenerProxy extends IImsRegistrationListener.Stub {
716        private int mServiceClass;
717        private ImsConnectionStateListener mListener;
718
719        public ImsRegistrationListenerProxy(int serviceClass,
720                ImsConnectionStateListener listener) {
721            mServiceClass = serviceClass;
722            mListener = listener;
723        }
724
725        public boolean isSameProxy(int serviceClass) {
726            return (mServiceClass == serviceClass);
727        }
728
729        @Override
730        public void registrationConnected() {
731            if (DBG) {
732                log("registrationConnected ::");
733            }
734
735            if (mListener != null) {
736                mListener.onImsConnected();
737            }
738        }
739
740        @Override
741        public void registrationDisconnected() {
742            if (DBG) {
743                log("registrationDisconnected ::");
744            }
745
746            if (mListener != null) {
747                mListener.onImsDisconnected();
748            }
749        }
750
751        @Override
752        public void registrationResumed() {
753            if (DBG) {
754                log("registrationResumed ::");
755            }
756
757            if (mListener != null) {
758                mListener.onImsResumed();
759            }
760        }
761
762        @Override
763        public void registrationSuspended() {
764            if (DBG) {
765                log("registrationSuspended ::");
766            }
767
768            if (mListener != null) {
769                mListener.onImsSuspended();
770            }
771        }
772
773        @Override
774        public void registrationServiceCapabilityChanged(int serviceClass, int event) {
775            log("registrationServiceCapabilityChanged :: serviceClass=" +
776                    serviceClass + ", event=" + event);
777
778            if (mListener != null) {
779                mListener.onImsConnected();
780            }
781        }
782
783        @Override
784        public void registrationFeatureCapabilityChanged(int serviceClass,
785                int[] enabledFeatures, int[] disabledFeatures) {
786            log("registrationFeatureCapabilityChanged :: serviceClass=" +
787                    serviceClass);
788            if (mListener != null) {
789                mListener.onFeatureCapabilityChanged(serviceClass,
790                        enabledFeatures, disabledFeatures);
791            }
792        }
793
794    }
795    /**
796     * Gets the ECBM interface to request ECBM exit.
797     *
798     * @param serviceId a service id which is obtained from {@link ImsManager#open}
799     * @return the ECBM interface instance
800     * @throws ImsException if getting the ECBM interface results in an error
801     */
802    public ImsEcbm getEcbmInterface(int serviceId) throws ImsException {
803        if (mEcbm == null) {
804            checkAndThrowExceptionIfServiceUnavailable();
805
806            try {
807                IImsEcbm iEcbm = mImsService.getEcbmInterface(serviceId);
808
809                if (iEcbm == null) {
810                    throw new ImsException("getEcbmInterface()",
811                            ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED);
812                }
813                mEcbm = new ImsEcbm(iEcbm);
814            } catch (RemoteException e) {
815                throw new ImsException("getEcbmInterface()", e,
816                        ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
817            }
818        }
819        return mEcbm;
820    }
821}
822