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.internal.telephony.imsphone; 18 19 import java.io.FileDescriptor; 20 import java.io.PrintWriter; 21 import java.util.ArrayList; 22 import java.util.List; 23 24 import android.app.PendingIntent; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.SharedPreferences; 30 import android.os.AsyncResult; 31 import android.os.Handler; 32 import android.os.Message; 33 import android.os.Registrant; 34 import android.os.RegistrantList; 35 import android.os.RemoteException; 36 import android.os.SystemProperties; 37 import android.preference.PreferenceManager; 38 import android.telecom.VideoProfile; 39 import android.telephony.DisconnectCause; 40 import android.telephony.PhoneNumberUtils; 41 import android.telephony.Rlog; 42 import android.telephony.ServiceState; 43 44 import com.android.ims.ImsCall; 45 import com.android.ims.ImsCallProfile; 46 import com.android.ims.ImsConfig; 47 import com.android.ims.ImsConnectionStateListener; 48 import com.android.ims.ImsEcbm; 49 import com.android.ims.ImsException; 50 import com.android.ims.ImsManager; 51 import com.android.ims.ImsReasonInfo; 52 import com.android.ims.ImsServiceClass; 53 import com.android.ims.ImsUtInterface; 54 import com.android.ims.internal.IImsVideoCallProvider; 55 import com.android.ims.internal.ImsVideoCallProviderWrapper; 56 import com.android.internal.telephony.Call; 57 import com.android.internal.telephony.CallStateException; 58 import com.android.internal.telephony.CallTracker; 59 import com.android.internal.telephony.CommandException; 60 import com.android.internal.telephony.CommandsInterface; 61 import com.android.internal.telephony.Connection; 62 import com.android.internal.telephony.Phone; 63 import com.android.internal.telephony.PhoneBase; 64 import com.android.internal.telephony.PhoneConstants; 65 import com.android.internal.telephony.TelephonyProperties; 66 67 /** 68 * {@hide} 69 */ 70 public final class ImsPhoneCallTracker extends CallTracker { 71 static final String LOG_TAG = "ImsPhoneCallTracker"; 72 73 private static final boolean DBG = true; 74 75 private boolean mIsVolteEnabled = false; 76 private boolean mIsVtEnabled = false; 77 78 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 79 @Override 80 public void onReceive(Context context, Intent intent) { 81 if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) { 82 if (DBG) log("onReceive : incoming call intent"); 83 84 if (mImsManager == null) return; 85 86 if (mServiceId < 0) return; 87 88 try { 89 // Network initiated USSD will be treated by mImsUssdListener 90 boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false); 91 if (isUssd) { 92 if (DBG) log("onReceive : USSD"); 93 mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener); 94 if (mUssdSession != null) { 95 mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE); 96 } 97 return; 98 } 99 100 // Normal MT call 101 ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener); 102 103 ImsPhoneConnection conn = new ImsPhoneConnection(mPhone.getContext(), imsCall, 104 ImsPhoneCallTracker.this, mRingingCall); 105 addConnection(conn); 106 107 IImsVideoCallProvider imsVideoCallProvider = 108 imsCall.getCallSession().getVideoCallProvider(); 109 if (imsVideoCallProvider != null) { 110 ImsVideoCallProviderWrapper imsVideoCallProviderWrapper = 111 new ImsVideoCallProviderWrapper(imsVideoCallProvider); 112 conn.setVideoProvider(imsVideoCallProviderWrapper); 113 } 114 115 if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) || 116 (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) { 117 conn.update(imsCall, ImsPhoneCall.State.WAITING); 118 } 119 120 mPhone.notifyNewRingingConnection(conn); 121 mPhone.notifyIncomingRing(); 122 123 updatePhoneState(); 124 mPhone.notifyPreciseCallStateChanged(); 125 } catch (ImsException e) { 126 loge("onReceive : exception " + e); 127 } catch (RemoteException e) { 128 } 129 } 130 } 131 }; 132 133 //***** Constants 134 135 static final int MAX_CONNECTIONS = 7; 136 static final int MAX_CONNECTIONS_PER_CALL = 5; 137 138 private static final int EVENT_HANGUP_PENDINGMO = 18; 139 private static final int EVENT_RESUME_BACKGROUND = 19; 140 private static final int EVENT_DIAL_PENDINGMO = 20; 141 142 private static final int TIMEOUT_HANGUP_PENDINGMO = 500; 143 144 //***** Instance Variables 145 private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>(); 146 private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 147 private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 148 149 ImsPhoneCall mRingingCall = new ImsPhoneCall(this); 150 ImsPhoneCall mForegroundCall = new ImsPhoneCall(this); 151 ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this); 152 ImsPhoneCall mHandoverCall = new ImsPhoneCall(this); 153 154 private ImsPhoneConnection mPendingMO; 155 private int mClirMode = CommandsInterface.CLIR_DEFAULT; 156 private Object mSyncHold = new Object(); 157 158 private ImsCall mUssdSession = null; 159 private Message mPendingUssd = null; 160 161 ImsPhone mPhone; 162 163 private boolean mDesiredMute = false; // false = mute off 164 private boolean mOnHoldToneStarted = false; 165 166 PhoneConstants.State mState = PhoneConstants.State.IDLE; 167 168 private ImsManager mImsManager; 169 private int mServiceId = -1; 170 171 private Call.SrvccState mSrvccState = Call.SrvccState.NONE; 172 173 private boolean mIsInEmergencyCall = false; 174 175 private int pendingCallClirMode; 176 private int pendingCallVideoState; 177 private boolean pendingCallInEcm = false; 178 179 //***** Events 180 181 182 //***** Constructors 183 184 ImsPhoneCallTracker(ImsPhone phone) { 185 this.mPhone = phone; 186 187 IntentFilter intentfilter = new IntentFilter(); 188 intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL); 189 mPhone.getContext().registerReceiver(mReceiver, intentfilter); 190 191 Thread t = new Thread() { 192 public void run() { 193 getImsService(); 194 } 195 }; 196 t.start(); 197 } 198 199 private PendingIntent createIncomingCallPendingIntent() { 200 Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL); 201 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 202 return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, 203 PendingIntent.FLAG_UPDATE_CURRENT); 204 } 205 206 private void getImsService() { 207 if (DBG) log("getImsService"); 208 mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getSubId()); 209 try { 210 mServiceId = mImsManager.open(ImsServiceClass.MMTEL, 211 createIncomingCallPendingIntent(), 212 mImsConnectionStateListener); 213 214 // Get the ECBM interface and set IMSPhone's listener object for notifications 215 getEcbmInterface().setEcbmStateListener(mPhone.mImsEcbmStateListener); 216 if (mPhone.isInEcm()) { 217 // Call exit ECBM which will invoke onECBMExited 218 mPhone.exitEmergencyCallbackMode(); 219 } 220 } catch (ImsException e) { 221 loge("getImsService: " + e); 222 //Leave mImsManager as null, then CallStateException will be thrown when dialing 223 mImsManager = null; 224 } 225 } 226 227 public void dispose() { 228 if (DBG) log("dispose"); 229 mRingingCall.dispose(); 230 mBackgroundCall.dispose(); 231 mForegroundCall.dispose(); 232 mHandoverCall.dispose(); 233 234 clearDisconnected(); 235 mPhone.getContext().unregisterReceiver(mReceiver); 236 } 237 238 @Override 239 protected void finalize() { 240 log("ImsPhoneCallTracker finalized"); 241 } 242 243 //***** Instance Methods 244 245 //***** Public Methods 246 @Override 247 public void registerForVoiceCallStarted(Handler h, int what, Object obj) { 248 Registrant r = new Registrant(h, what, obj); 249 mVoiceCallStartedRegistrants.add(r); 250 } 251 252 @Override 253 public void unregisterForVoiceCallStarted(Handler h) { 254 mVoiceCallStartedRegistrants.remove(h); 255 } 256 257 @Override 258 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 259 Registrant r = new Registrant(h, what, obj); 260 mVoiceCallEndedRegistrants.add(r); 261 } 262 263 @Override 264 public void unregisterForVoiceCallEnded(Handler h) { 265 mVoiceCallEndedRegistrants.remove(h); 266 } 267 268 Connection 269 dial(String dialString, int videoState) throws CallStateException { 270 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()); 271 int oirMode = sp.getInt(PhoneBase.CLIR_KEY, CommandsInterface.CLIR_DEFAULT); 272 return dial(dialString, oirMode, videoState); 273 } 274 275 /** 276 * oirMode is one of the CLIR_ constants 277 */ 278 synchronized Connection 279 dial(String dialString, int clirMode, int videoState) throws CallStateException { 280 boolean isPhoneInEcmMode = SystemProperties.getBoolean( 281 TelephonyProperties.PROPERTY_INECM_MODE, false); 282 boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString); 283 284 if (DBG) log("dial clirMode=" + clirMode); 285 286 // note that this triggers call state changed notif 287 clearDisconnected(); 288 289 if (mImsManager == null) { 290 throw new CallStateException("service not available"); 291 } 292 293 if (!canDial()) { 294 throw new CallStateException("cannot dial in current state"); 295 } 296 297 if (isPhoneInEcmMode && isEmergencyNumber) { 298 handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER); 299 } 300 301 boolean holdBeforeDial = false; 302 303 // The new call must be assigned to the foreground call. 304 // That call must be idle, so place anything that's 305 // there on hold 306 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 307 if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) { 308 //we should have failed in !canDial() above before we get here 309 throw new CallStateException("cannot dial in current state"); 310 } 311 // foreground call is empty for the newly dialed connection 312 holdBeforeDial = true; 313 switchWaitingOrHoldingAndActive(); 314 } 315 316 ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE; 317 ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE; 318 319 mClirMode = clirMode; 320 321 synchronized (mSyncHold) { 322 if (holdBeforeDial) { 323 fgState = mForegroundCall.getState(); 324 bgState = mBackgroundCall.getState(); 325 326 //holding foreground call failed 327 if (fgState == ImsPhoneCall.State.ACTIVE) { 328 throw new CallStateException("cannot dial in current state"); 329 } 330 331 //holding foreground call succeeded 332 if (bgState == ImsPhoneCall.State.HOLDING) { 333 holdBeforeDial = false; 334 } 335 } 336 337 mPendingMO = new ImsPhoneConnection(mPhone.getContext(), 338 checkForTestEmergencyNumber(dialString), this, mForegroundCall); 339 } 340 addConnection(mPendingMO); 341 342 if (!holdBeforeDial) { 343 if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) { 344 dialInternal(mPendingMO, clirMode, videoState); 345 } else { 346 try { 347 getEcbmInterface().exitEmergencyCallbackMode(); 348 } catch (ImsException e) { 349 e.printStackTrace(); 350 throw new CallStateException("service not available"); 351 } 352 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 353 pendingCallClirMode = clirMode; 354 pendingCallVideoState = videoState; 355 pendingCallInEcm = true; 356 } 357 } 358 359 updatePhoneState(); 360 mPhone.notifyPreciseCallStateChanged(); 361 362 return mPendingMO; 363 } 364 365 private void handleEcmTimer(int action) { 366 mPhone.handleTimerInEmergencyCallbackMode(action); 367 switch (action) { 368 case ImsPhone.CANCEL_ECM_TIMER: 369 break; 370 case ImsPhone.RESTART_ECM_TIMER: 371 break; 372 default: 373 log("handleEcmTimer, unsupported action " + action); 374 } 375 } 376 377 private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState) { 378 if (conn == null) { 379 return; 380 } 381 382 if (conn.getAddress()== null || conn.getAddress().length() == 0 383 || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { 384 // Phone number is invalid 385 conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER); 386 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 387 return; 388 } 389 390 // Always unmute when initiating a new call 391 setMute(false); 392 int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ? 393 ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL; 394 int callType = ImsCallProfile.getCallTypeFromVideoState(videoState); 395 //TODO(vt): Is this sufficient? At what point do we know the video state of the call? 396 conn.setVideoState(videoState); 397 398 try { 399 String[] callees = new String[] { conn.getAddress() }; 400 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 401 serviceType, callType); 402 profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode); 403 404 ImsCall imsCall = mImsManager.makeCall(mServiceId, profile, 405 callees, mImsCallListener); 406 conn.setImsCall(imsCall); 407 408 IImsVideoCallProvider imsVideoCallProvider = 409 imsCall.getCallSession().getVideoCallProvider(); 410 if (imsVideoCallProvider != null) { 411 ImsVideoCallProviderWrapper imsVideoCallProviderWrapper = 412 new ImsVideoCallProviderWrapper(imsVideoCallProvider); 413 conn.setVideoProvider(imsVideoCallProviderWrapper); 414 } 415 } catch (ImsException e) { 416 loge("dialInternal : " + e); 417 conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 418 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 419 } catch (RemoteException e) { 420 } 421 } 422 423 /** 424 * Accepts a call with the specified video state. The video state is the video state that the 425 * user has agreed upon in the InCall UI. 426 * 427 * @param videoState The video State 428 * @throws CallStateException 429 */ 430 void acceptCall (int videoState) throws CallStateException { 431 if (DBG) log("acceptCall"); 432 433 if (mForegroundCall.getState().isAlive() 434 && mBackgroundCall.getState().isAlive()) { 435 throw new CallStateException("cannot accept call"); 436 } 437 438 if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING) 439 && mForegroundCall.getState().isAlive()) { 440 setMute(false); 441 switchWaitingOrHoldingAndActive(); 442 } else if (mRingingCall.getState().isRinging()) { 443 if (DBG) log("acceptCall: incoming..."); 444 // Always unmute when answering a new call 445 setMute(false); 446 try { 447 ImsCall imsCall = mRingingCall.getImsCall(); 448 if (imsCall != null) { 449 imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState)); 450 } else { 451 throw new CallStateException("no valid ims call"); 452 } 453 } catch (ImsException e) { 454 throw new CallStateException("cannot accept call"); 455 } 456 } else { 457 throw new CallStateException("phone not ringing"); 458 } 459 } 460 461 void 462 rejectCall () throws CallStateException { 463 if (DBG) log("rejectCall"); 464 465 if (mRingingCall.getState().isRinging()) { 466 hangup(mRingingCall); 467 } else { 468 throw new CallStateException("phone not ringing"); 469 } 470 } 471 472 void 473 switchWaitingOrHoldingAndActive() throws CallStateException { 474 if (DBG) log("switchWaitingOrHoldingAndActive"); 475 476 if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) { 477 throw new CallStateException("cannot be in the incoming state"); 478 } 479 480 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 481 ImsCall imsCall = mForegroundCall.getImsCall(); 482 if (imsCall == null) { 483 throw new CallStateException("no ims call"); 484 } 485 486 mForegroundCall.switchWith(mBackgroundCall); 487 488 try { 489 imsCall.hold(); 490 } catch (ImsException e) { 491 mForegroundCall.switchWith(mBackgroundCall); 492 throw new CallStateException(e.getMessage()); 493 } 494 } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 495 resumeWaitingOrHolding(); 496 } 497 } 498 499 void 500 conference() { 501 if (DBG) log("conference"); 502 503 ImsCall fgImsCall = mForegroundCall.getImsCall(); 504 if (fgImsCall == null) { 505 log("conference no foreground ims call"); 506 return; 507 } 508 509 ImsCall bgImsCall = mBackgroundCall.getImsCall(); 510 if (bgImsCall == null) { 511 log("conference no background ims call"); 512 return; 513 } 514 515 try { 516 fgImsCall.merge(bgImsCall); 517 } catch (ImsException e) { 518 log("conference " + e.getMessage()); 519 } 520 } 521 522 void 523 explicitCallTransfer() { 524 //TODO : implement 525 } 526 527 void 528 clearDisconnected() { 529 if (DBG) log("clearDisconnected"); 530 531 internalClearDisconnected(); 532 533 updatePhoneState(); 534 mPhone.notifyPreciseCallStateChanged(); 535 } 536 537 boolean 538 canConference() { 539 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 540 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING 541 && !mBackgroundCall.isFull() 542 && !mForegroundCall.isFull(); 543 } 544 545 boolean 546 canDial() { 547 boolean ret; 548 int serviceState = mPhone.getServiceState().getState(); 549 String disableCall = SystemProperties.get( 550 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 551 552 ret = (serviceState != ServiceState.STATE_POWER_OFF) 553 && mPendingMO == null 554 && !mRingingCall.isRinging() 555 && !disableCall.equals("true") 556 && (!mForegroundCall.getState().isAlive() 557 || !mBackgroundCall.getState().isAlive()); 558 559 return ret; 560 } 561 562 boolean 563 canTransfer() { 564 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 565 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING; 566 } 567 568 //***** Private Instance Methods 569 570 private void 571 internalClearDisconnected() { 572 mRingingCall.clearDisconnected(); 573 mForegroundCall.clearDisconnected(); 574 mBackgroundCall.clearDisconnected(); 575 mHandoverCall.clearDisconnected(); 576 } 577 578 private void 579 updatePhoneState() { 580 PhoneConstants.State oldState = mState; 581 582 if (mRingingCall.isRinging()) { 583 mState = PhoneConstants.State.RINGING; 584 } else if (mPendingMO != null || 585 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 586 mState = PhoneConstants.State.OFFHOOK; 587 } else { 588 mState = PhoneConstants.State.IDLE; 589 } 590 591 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 592 mVoiceCallEndedRegistrants.notifyRegistrants( 593 new AsyncResult(null, null, null)); 594 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 595 mVoiceCallStartedRegistrants.notifyRegistrants ( 596 new AsyncResult(null, null, null)); 597 } 598 599 if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState); 600 601 if (mState != oldState) { 602 mPhone.notifyPhoneStateChanged(); 603 } 604 } 605 606 private void 607 handleRadioNotAvailable() { 608 // handlePollCalls will clear out its 609 // call list when it gets the CommandException 610 // error result from this 611 pollCallsWhenSafe(); 612 } 613 614 private void 615 dumpState() { 616 List l; 617 618 log("Phone State:" + mState); 619 620 log("Ringing call: " + mRingingCall.toString()); 621 622 l = mRingingCall.getConnections(); 623 for (int i = 0, s = l.size(); i < s; i++) { 624 log(l.get(i).toString()); 625 } 626 627 log("Foreground call: " + mForegroundCall.toString()); 628 629 l = mForegroundCall.getConnections(); 630 for (int i = 0, s = l.size(); i < s; i++) { 631 log(l.get(i).toString()); 632 } 633 634 log("Background call: " + mBackgroundCall.toString()); 635 636 l = mBackgroundCall.getConnections(); 637 for (int i = 0, s = l.size(); i < s; i++) { 638 log(l.get(i).toString()); 639 } 640 641 } 642 643 //***** Called from ImsPhone 644 645 /*package*/ void 646 setMute(boolean mute) { 647 mDesiredMute = mute; 648 mForegroundCall.setMute(mute); 649 } 650 651 /*package*/ boolean 652 getMute() { 653 return mDesiredMute; 654 } 655 656 /*package*/ void 657 sendDtmf(char c) { 658 if (DBG) log("sendDtmf"); 659 660 ImsCall imscall = mForegroundCall.getImsCall(); 661 if (imscall != null) { 662 imscall.sendDtmf(c); 663 } 664 } 665 666 //***** Called from ImsPhoneConnection 667 668 /*package*/ void 669 hangup (ImsPhoneConnection conn) throws CallStateException { 670 if (DBG) log("hangup connection"); 671 672 if (conn.getOwner() != this) { 673 throw new CallStateException ("ImsPhoneConnection " + conn 674 + "does not belong to ImsPhoneCallTracker " + this); 675 } 676 677 hangup(conn.getCall()); 678 } 679 680 //***** Called from ImsPhoneCall 681 682 /* package */ void 683 hangup (ImsPhoneCall call) throws CallStateException { 684 if (DBG) log("hangup call"); 685 686 if (call.getConnections().size() == 0) { 687 throw new CallStateException("no connections"); 688 } 689 690 ImsCall imsCall = call.getImsCall(); 691 boolean rejectCall = false; 692 693 if (call == mRingingCall) { 694 if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming"); 695 rejectCall = true; 696 } else if (call == mForegroundCall) { 697 if (call.isDialingOrAlerting()) { 698 if (Phone.DEBUG_PHONE) { 699 log("(foregnd) hangup dialing or alerting..."); 700 } 701 } else { 702 if (Phone.DEBUG_PHONE) { 703 log("(foregnd) hangup foreground"); 704 } 705 //held call will be resumed by onCallTerminated 706 } 707 } else if (call == mBackgroundCall) { 708 if (Phone.DEBUG_PHONE) { 709 log("(backgnd) hangup waiting or background"); 710 } 711 } else { 712 throw new CallStateException ("ImsPhoneCall " + call + 713 "does not belong to ImsPhoneCallTracker " + this); 714 } 715 716 call.onHangupLocal(); 717 718 try { 719 if (imsCall != null) { 720 if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE); 721 else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 722 } else if (mPendingMO != null && call == mForegroundCall) { 723 // is holding a foreground call 724 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED); 725 mPendingMO.onDisconnect(); 726 removeConnection(mPendingMO); 727 mPendingMO = null; 728 updatePhoneState(); 729 removeMessages(EVENT_DIAL_PENDINGMO); 730 } 731 } catch (ImsException e) { 732 throw new CallStateException(e.getMessage()); 733 } 734 735 mPhone.notifyPreciseCallStateChanged(); 736 } 737 738 /* package */ 739 void resumeWaitingOrHolding() throws CallStateException { 740 if (DBG) log("resumeWaitingOrHolding"); 741 742 try { 743 if (mForegroundCall.getState().isAlive()) { 744 //resume foreground call after holding background call 745 //they were switched before holding 746 ImsCall imsCall = mForegroundCall.getImsCall(); 747 if (imsCall != null) imsCall.resume(); 748 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) { 749 //accept waiting call after holding background call 750 ImsCall imsCall = mRingingCall.getImsCall(); 751 if (imsCall != null) imsCall.accept(ImsCallProfile.CALL_TYPE_VOICE); 752 } else { 753 //Just resume background call. 754 //To distinguish resuming call with swapping calls 755 //we do not switch calls.here 756 //ImsPhoneConnection.update will chnage the parent when completed 757 ImsCall imsCall = mBackgroundCall.getImsCall(); 758 if (imsCall != null) imsCall.resume(); 759 } 760 } catch (ImsException e) { 761 throw new CallStateException(e.getMessage()); 762 } 763 } 764 765 /* package */ 766 void sendUSSD (String ussdString, Message response) { 767 if (DBG) log("sendUSSD"); 768 769 try { 770 if (mUssdSession != null) { 771 mUssdSession.sendUssd(ussdString); 772 AsyncResult.forMessage(response, null, null); 773 response.sendToTarget(); 774 return; 775 } 776 777 String[] callees = new String[] { ussdString }; 778 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 779 ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE); 780 profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING, 781 ImsCallProfile.DIALSTRING_USSD); 782 783 mUssdSession = mImsManager.makeCall(mServiceId, profile, 784 callees, mImsUssdListener); 785 } catch (ImsException e) { 786 loge("sendUSSD : " + e); 787 mPhone.sendErrorResponse(response, e); 788 } 789 } 790 791 /* package */ 792 void cancelUSSD() { 793 if (mUssdSession == null) return; 794 795 try { 796 mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 797 } catch (ImsException e) { 798 } 799 800 } 801 802 private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) { 803 for (ImsPhoneConnection conn : mConnections) { 804 if (conn.getImsCall() == imsCall) { 805 return conn; 806 } 807 } 808 return null; 809 } 810 811 private synchronized void removeConnection(ImsPhoneConnection conn) { 812 mConnections.remove(conn); 813 } 814 815 private synchronized void addConnection(ImsPhoneConnection conn) { 816 mConnections.add(conn); 817 } 818 819 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) { 820 if (DBG) log("processCallStateChange state=" + state + " cause=" + cause); 821 822 if (imsCall == null) return; 823 824 boolean changed = false; 825 ImsPhoneConnection conn = findConnection(imsCall); 826 827 if (conn == null) { 828 // TODO : what should be done? 829 return; 830 } 831 832 changed = conn.update(imsCall, state); 833 834 if (state == ImsPhoneCall.State.DISCONNECTED) { 835 changed = conn.onDisconnect(cause) || changed; 836 removeConnection(conn); 837 } 838 839 if (changed) { 840 if (conn.getCall() == mHandoverCall) return; 841 updatePhoneState(); 842 mPhone.notifyPreciseCallStateChanged(); 843 } 844 } 845 846 private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) { 847 int cause = DisconnectCause.ERROR_UNSPECIFIED; 848 849 //int type = reasonInfo.getReasonType(); 850 int code = reasonInfo.getCode(); 851 switch (code) { 852 case ImsReasonInfo.CODE_SIP_BAD_ADDRESS: 853 case ImsReasonInfo.CODE_SIP_NOT_REACHABLE: 854 return DisconnectCause.NUMBER_UNREACHABLE; 855 856 case ImsReasonInfo.CODE_SIP_BUSY: 857 return DisconnectCause.BUSY; 858 859 case ImsReasonInfo.CODE_USER_TERMINATED: 860 return DisconnectCause.LOCAL; 861 862 case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE: 863 return DisconnectCause.NORMAL; 864 865 case ImsReasonInfo.CODE_SIP_REDIRECTED: 866 case ImsReasonInfo.CODE_SIP_BAD_REQUEST: 867 case ImsReasonInfo.CODE_SIP_FORBIDDEN: 868 case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE: 869 case ImsReasonInfo.CODE_SIP_USER_REJECTED: 870 case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR: 871 return DisconnectCause.SERVER_ERROR; 872 873 case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE: 874 case ImsReasonInfo.CODE_SIP_NOT_FOUND: 875 case ImsReasonInfo.CODE_SIP_SERVER_ERROR: 876 return DisconnectCause.SERVER_UNREACHABLE; 877 878 case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING: 879 case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED: 880 case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN: 881 case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE: 882 case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED: 883 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE: 884 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE: 885 case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING: 886 return DisconnectCause.OUT_OF_SERVICE; 887 888 case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT: 889 case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING: 890 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER: 891 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE: 892 return DisconnectCause.TIMED_OUT; 893 894 case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY: 895 case ImsReasonInfo.CODE_LOCAL_POWER_OFF: 896 return DisconnectCause.POWER_OFF; 897 898 default: 899 } 900 901 return cause; 902 } 903 904 /** 905 * Listen to the IMS call state change 906 */ 907 private ImsCall.Listener mImsCallListener = new ImsCall.Listener() { 908 @Override 909 public void onCallProgressing(ImsCall imsCall) { 910 if (DBG) log("onCallProgressing"); 911 912 mPendingMO = null; 913 processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING, 914 DisconnectCause.NOT_DISCONNECTED); 915 } 916 917 @Override 918 public void onCallStarted(ImsCall imsCall) { 919 if (DBG) log("onCallStarted"); 920 921 mPendingMO = null; 922 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 923 DisconnectCause.NOT_DISCONNECTED); 924 } 925 926 /** 927 * onCallStartFailed will be invoked when: 928 * case 1) Dialing fails 929 * case 2) Ringing call is disconnected by local or remote user 930 */ 931 @Override 932 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 933 if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode()); 934 935 if (mPendingMO != null) { 936 // To initiate dialing circuit-switched call 937 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED 938 && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE 939 && mRingingCall.getState() == ImsPhoneCall.State.IDLE) { 940 mForegroundCall.detach(mPendingMO); 941 removeConnection(mPendingMO); 942 mPendingMO.finalize(); 943 mPendingMO = null; 944 mPhone.initiateSilentRedial(); 945 return; 946 } 947 mPendingMO = null; 948 } 949 } 950 951 @Override 952 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 953 if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode()); 954 955 ImsPhoneCall.State oldState = mForegroundCall.getState(); 956 int cause = getDisconnectCauseFromReasonInfo(reasonInfo); 957 ImsPhoneConnection conn = findConnection(imsCall); 958 if (DBG) log("cause = " + cause + " conn = " + conn); 959 960 if (conn != null && conn.isIncoming() && conn.getConnectTime() == 0) { 961 // Missed or rejected call 962 if (cause == DisconnectCause.LOCAL) { 963 cause = DisconnectCause.INCOMING_REJECTED; 964 } else { 965 cause = DisconnectCause.INCOMING_MISSED; 966 } 967 } 968 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 969 970 if (reasonInfo.getCode() == ImsReasonInfo.CODE_USER_TERMINATED) { 971 if ((oldState == ImsPhoneCall.State.DISCONNECTING) 972 && (mForegroundCall.getState() == ImsPhoneCall.State.DISCONNECTED) 973 && (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING)) { 974 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 975 } 976 } 977 } 978 979 @Override 980 public void onCallHeld(ImsCall imsCall) { 981 if (DBG) log("onCallHeld"); 982 983 synchronized (mSyncHold) { 984 ImsPhoneCall.State oldState = mBackgroundCall.getState(); 985 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING, 986 DisconnectCause.NOT_DISCONNECTED); 987 988 if (oldState == ImsPhoneCall.State.ACTIVE) { 989 if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) 990 || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) { 991 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 992 } else { 993 //when multiple connections belong to background call, 994 //only the first callback reaches here 995 //otherwise the oldState is already HOLDING 996 if (mPendingMO != null) { 997 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 998 } 999 } 1000 } 1001 } 1002 } 1003 1004 @Override 1005 public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1006 if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode()); 1007 1008 synchronized (mSyncHold) { 1009 ImsPhoneCall.State bgState = mBackgroundCall.getState(); 1010 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) { 1011 // disconnected while processing hold 1012 if (mPendingMO != null) { 1013 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 1014 } 1015 } else if (bgState == ImsPhoneCall.State.ACTIVE) { 1016 mForegroundCall.switchWith(mBackgroundCall); 1017 1018 if (mPendingMO != null) { 1019 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 1020 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 1021 } 1022 } 1023 } 1024 } 1025 1026 @Override 1027 public void onCallResumed(ImsCall imsCall) { 1028 if (DBG) log("onCallResumed"); 1029 1030 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 1031 DisconnectCause.NOT_DISCONNECTED); 1032 } 1033 1034 @Override 1035 public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1036 // TODO : What should be done? 1037 } 1038 1039 @Override 1040 public void onCallResumeReceived(ImsCall imsCall) { 1041 if (DBG) log("onCallResumeReceived"); 1042 1043 if (mOnHoldToneStarted) { 1044 mPhone.stopOnHoldTone(); 1045 mOnHoldToneStarted = false; 1046 } 1047 } 1048 1049 @Override 1050 public void onCallHoldReceived(ImsCall imsCall) { 1051 if (DBG) log("onCallHoldReceived"); 1052 1053 ImsPhoneConnection conn = findConnection(imsCall); 1054 if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) { 1055 if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) { 1056 mPhone.startOnHoldTone(); 1057 mOnHoldToneStarted = true; 1058 } 1059 } 1060 } 1061 1062 @Override 1063 public void onCallMerged(ImsCall call, ImsCall newCall) { 1064 if (DBG) log("onCallMerged"); 1065 1066 mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState()); 1067 updatePhoneState(); 1068 mPhone.notifyPreciseCallStateChanged(); 1069 } 1070 1071 @Override 1072 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 1073 if (DBG) log("onCallMergeFailed reasonCode=" + reasonInfo.getCode()); 1074 mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE); 1075 } 1076 }; 1077 1078 /** 1079 * Listen to the IMS call state change 1080 */ 1081 private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() { 1082 @Override 1083 public void onCallStarted(ImsCall imsCall) { 1084 if (DBG) log("mImsUssdListener onCallStarted"); 1085 1086 if (imsCall == mUssdSession) { 1087 if (mPendingUssd != null) { 1088 AsyncResult.forMessage(mPendingUssd); 1089 mPendingUssd.sendToTarget(); 1090 mPendingUssd = null; 1091 } 1092 } 1093 } 1094 1095 @Override 1096 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1097 if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode()); 1098 1099 onCallTerminated(imsCall, reasonInfo); 1100 } 1101 1102 @Override 1103 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1104 if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); 1105 1106 if (imsCall == mUssdSession) { 1107 mUssdSession = null; 1108 if (mPendingUssd != null) { 1109 CommandException ex = 1110 new CommandException(CommandException.Error.GENERIC_FAILURE); 1111 AsyncResult.forMessage(mPendingUssd, null, ex); 1112 mPendingUssd.sendToTarget(); 1113 mPendingUssd = null; 1114 } 1115 } 1116 imsCall.close(); 1117 } 1118 1119 @Override 1120 public void onCallUssdMessageReceived(ImsCall call, 1121 int mode, String ussdMessage) { 1122 if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode); 1123 1124 int ussdMode = -1; 1125 1126 switch(mode) { 1127 case ImsCall.USSD_MODE_REQUEST: 1128 ussdMode = CommandsInterface.USSD_MODE_REQUEST; 1129 break; 1130 1131 case ImsCall.USSD_MODE_NOTIFY: 1132 ussdMode = CommandsInterface.USSD_MODE_NOTIFY; 1133 break; 1134 } 1135 1136 mPhone.onIncomingUSSD(ussdMode, ussdMessage); 1137 } 1138 }; 1139 1140 /** 1141 * Listen to the IMS service state change 1142 * 1143 */ 1144 private ImsConnectionStateListener mImsConnectionStateListener = 1145 new ImsConnectionStateListener() { 1146 @Override 1147 public void onImsConnected() { 1148 if (DBG) log("onImsConnected"); 1149 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1150 } 1151 1152 @Override 1153 public void onImsDisconnected() { 1154 if (DBG) log("onImsDisconnected"); 1155 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1156 } 1157 1158 @Override 1159 public void onImsResumed() { 1160 if (DBG) log("onImsResumed"); 1161 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1162 } 1163 1164 @Override 1165 public void onImsSuspended() { 1166 if (DBG) log("onImsSuspended"); 1167 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1168 } 1169 1170 @Override 1171 public void onFeatureCapabilityChanged(int serviceClass, 1172 int[] enabledFeatures, int[] disabledFeatures) { 1173 if (serviceClass == ImsServiceClass.MMTEL) { 1174 if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] == 1175 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) { 1176 mIsVolteEnabled = true; 1177 } 1178 if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] == 1179 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) { 1180 mIsVtEnabled = true; 1181 } 1182 if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] == 1183 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) { 1184 mIsVolteEnabled = false; 1185 } 1186 if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] == 1187 ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) { 1188 mIsVtEnabled = false; 1189 } 1190 } 1191 if (DBG) log("onFeatureCapabilityChanged, mIsVolteEnabled = " + mIsVolteEnabled 1192 + " mIsVtEnabled = " + mIsVtEnabled); 1193 } 1194 }; 1195 1196 /* package */ 1197 ImsUtInterface getUtInterface() throws ImsException { 1198 if (mImsManager == null) { 1199 throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED); 1200 } 1201 1202 ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId); 1203 return ut; 1204 } 1205 1206 /* package */ 1207 void notifySrvccState(Call.SrvccState state) { 1208 if (DBG) log("notifySrvccState state=" + state); 1209 1210 mSrvccState = state; 1211 1212 if (mSrvccState == Call.SrvccState.COMPLETED) { 1213 if (mForegroundCall.getConnections().size() > 0) { 1214 mHandoverCall.switchWith(mForegroundCall); 1215 } else if (mBackgroundCall.getConnections().size() > 0) { 1216 mHandoverCall.switchWith(mBackgroundCall); 1217 } 1218 1219 // release wake lock hold 1220 ImsPhoneConnection con = mHandoverCall.getHandoverConnection(); 1221 if (con != null) { 1222 con.releaseWakeLock(); 1223 } 1224 } 1225 } 1226 1227 //****** Overridden from Handler 1228 1229 @Override 1230 public void 1231 handleMessage (Message msg) { 1232 AsyncResult ar; 1233 if (DBG) log("handleMessage what=" + msg.what); 1234 1235 switch (msg.what) { 1236 case EVENT_HANGUP_PENDINGMO: 1237 if (mPendingMO != null) { 1238 mPendingMO.onDisconnect(); 1239 removeConnection(mPendingMO); 1240 mPendingMO = null; 1241 } 1242 1243 updatePhoneState(); 1244 mPhone.notifyPreciseCallStateChanged(); 1245 break; 1246 case EVENT_RESUME_BACKGROUND: 1247 try { 1248 resumeWaitingOrHolding(); 1249 } catch (CallStateException e) { 1250 if (Phone.DEBUG_PHONE) { 1251 loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e); 1252 } 1253 } 1254 break; 1255 case EVENT_DIAL_PENDINGMO: 1256 dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY); 1257 break; 1258 1259 case EVENT_EXIT_ECM_RESPONSE_CDMA: 1260 // no matter the result, we still do the same here 1261 if (pendingCallInEcm) { 1262 dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState); 1263 pendingCallInEcm = false; 1264 } 1265 mPhone.unsetOnEcbModeExitResponse(this); 1266 break; 1267 } 1268 } 1269 1270 @Override 1271 protected void log(String msg) { 1272 Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 1273 } 1274 1275 protected void loge(String msg) { 1276 Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 1277 } 1278 1279 @Override 1280 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1281 pw.println("ImsPhoneCallTracker extends:"); 1282 super.dump(fd, pw, args); 1283 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1284 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1285 pw.println(" mRingingCall=" + mRingingCall); 1286 pw.println(" mForegroundCall=" + mForegroundCall); 1287 pw.println(" mBackgroundCall=" + mBackgroundCall); 1288 pw.println(" mHandoverCall=" + mHandoverCall); 1289 pw.println(" mPendingMO=" + mPendingMO); 1290 //pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1291 pw.println(" mPhone=" + mPhone); 1292 pw.println(" mDesiredMute=" + mDesiredMute); 1293 pw.println(" mState=" + mState); 1294 } 1295 1296 @Override 1297 protected void handlePollCalls(AsyncResult ar) { 1298 } 1299 1300 /* package */ 1301 ImsEcbm getEcbmInterface() throws ImsException { 1302 if (mImsManager == null) { 1303 throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED); 1304 } 1305 1306 ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId); 1307 return ecbm; 1308 } 1309 1310 public boolean isInEmergencyCall() { 1311 return mIsInEmergencyCall; 1312 } 1313 1314 public boolean isVolteEnabled() { 1315 return mIsVolteEnabled; 1316 } 1317 1318 public boolean isVtEnabled() { 1319 return mIsVtEnabled; 1320 } 1321 } 1322