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