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