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