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