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