ImsPhoneCallTracker.java revision 0ba5c4a978fba0c15c8539c790a3ae1793bd99e3
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[] mImsFeatureEnabled = {false, false, false, false}; 78 79 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 80 @Override 81 public void onReceive(Context context, Intent intent) { 82 if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) { 83 if (DBG) log("onReceive : incoming call intent"); 84 85 if (mImsManager == null) return; 86 87 if (mServiceId < 0) return; 88 89 try { 90 // Network initiated USSD will be treated by mImsUssdListener 91 boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false); 92 if (isUssd) { 93 if (DBG) log("onReceive : USSD"); 94 mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener); 95 if (mUssdSession != null) { 96 mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE); 97 } 98 return; 99 } 100 101 // Normal MT call 102 ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener); 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 private boolean mSwitchingFgAndBgCalls = false; 179 private ImsCall mCallExpectedToResume = null; 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.getPhoneId()); 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(mPhone.getContext(), 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 // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls. 495 // If hold or resume later fails, we will swap them back. 496 mSwitchingFgAndBgCalls = true; 497 mCallExpectedToResume = mBackgroundCall.getImsCall(); 498 mForegroundCall.switchWith(mBackgroundCall); 499 500 // Hold the foreground call; once the foreground call is held, the background call will 501 // be resumed. 502 try { 503 imsCall.hold(); 504 } catch (ImsException e) { 505 mForegroundCall.switchWith(mBackgroundCall); 506 throw new CallStateException(e.getMessage()); 507 } 508 } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 509 resumeWaitingOrHolding(); 510 } 511 } 512 513 void 514 conference() { 515 if (DBG) log("conference"); 516 517 ImsCall fgImsCall = mForegroundCall.getImsCall(); 518 if (fgImsCall == null) { 519 log("conference no foreground ims call"); 520 return; 521 } 522 523 ImsCall bgImsCall = mBackgroundCall.getImsCall(); 524 if (bgImsCall == null) { 525 log("conference no background ims call"); 526 return; 527 } 528 529 // Keep track of the connect time of the earliest call so that it can be set on the 530 // {@code ImsConference} when it is created. 531 long conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(), 532 mBackgroundCall.getEarliestConnectTime()); 533 ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection(); 534 if (foregroundConnection != null) { 535 foregroundConnection.setConferenceConnectTime(conferenceConnectTime); 536 } 537 538 try { 539 fgImsCall.merge(bgImsCall); 540 } catch (ImsException e) { 541 log("conference " + e.getMessage()); 542 } 543 } 544 545 void 546 explicitCallTransfer() { 547 //TODO : implement 548 } 549 550 void 551 clearDisconnected() { 552 if (DBG) log("clearDisconnected"); 553 554 internalClearDisconnected(); 555 556 updatePhoneState(); 557 mPhone.notifyPreciseCallStateChanged(); 558 } 559 560 boolean 561 canConference() { 562 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 563 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING 564 && !mBackgroundCall.isFull() 565 && !mForegroundCall.isFull(); 566 } 567 568 boolean 569 canDial() { 570 boolean ret; 571 int serviceState = mPhone.getServiceState().getState(); 572 String disableCall = SystemProperties.get( 573 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 574 575 ret = (serviceState != ServiceState.STATE_POWER_OFF) 576 && mPendingMO == null 577 && !mRingingCall.isRinging() 578 && !disableCall.equals("true") 579 && (!mForegroundCall.getState().isAlive() 580 || !mBackgroundCall.getState().isAlive()); 581 582 return ret; 583 } 584 585 boolean 586 canTransfer() { 587 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 588 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING; 589 } 590 591 //***** Private Instance Methods 592 593 private void 594 internalClearDisconnected() { 595 mRingingCall.clearDisconnected(); 596 mForegroundCall.clearDisconnected(); 597 mBackgroundCall.clearDisconnected(); 598 mHandoverCall.clearDisconnected(); 599 } 600 601 private void 602 updatePhoneState() { 603 PhoneConstants.State oldState = mState; 604 605 if (mRingingCall.isRinging()) { 606 mState = PhoneConstants.State.RINGING; 607 } else if (mPendingMO != null || 608 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 609 mState = PhoneConstants.State.OFFHOOK; 610 } else { 611 mState = PhoneConstants.State.IDLE; 612 } 613 614 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 615 mVoiceCallEndedRegistrants.notifyRegistrants( 616 new AsyncResult(null, null, null)); 617 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 618 mVoiceCallStartedRegistrants.notifyRegistrants ( 619 new AsyncResult(null, null, null)); 620 } 621 622 if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState); 623 624 if (mState != oldState) { 625 mPhone.notifyPhoneStateChanged(); 626 } 627 } 628 629 private void 630 handleRadioNotAvailable() { 631 // handlePollCalls will clear out its 632 // call list when it gets the CommandException 633 // error result from this 634 pollCallsWhenSafe(); 635 } 636 637 private void 638 dumpState() { 639 List l; 640 641 log("Phone State:" + mState); 642 643 log("Ringing call: " + mRingingCall.toString()); 644 645 l = mRingingCall.getConnections(); 646 for (int i = 0, s = l.size(); i < s; i++) { 647 log(l.get(i).toString()); 648 } 649 650 log("Foreground call: " + mForegroundCall.toString()); 651 652 l = mForegroundCall.getConnections(); 653 for (int i = 0, s = l.size(); i < s; i++) { 654 log(l.get(i).toString()); 655 } 656 657 log("Background call: " + mBackgroundCall.toString()); 658 659 l = mBackgroundCall.getConnections(); 660 for (int i = 0, s = l.size(); i < s; i++) { 661 log(l.get(i).toString()); 662 } 663 664 } 665 666 //***** Called from ImsPhone 667 668 void setUiTTYMode(int uiTtyMode, Message onComplete) { 669 try { 670 mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, uiTtyMode, onComplete); 671 } catch (ImsException e) { 672 loge("setTTYMode : " + e); 673 mPhone.sendErrorResponse(onComplete, e); 674 } 675 } 676 677 /*package*/ void setMute(boolean mute) { 678 mDesiredMute = mute; 679 mForegroundCall.setMute(mute); 680 } 681 682 /*package*/ boolean getMute() { 683 return mDesiredMute; 684 } 685 686 /* package */ void sendDtmf(char c, Message result) { 687 if (DBG) log("sendDtmf"); 688 689 ImsCall imscall = mForegroundCall.getImsCall(); 690 if (imscall != null) { 691 imscall.sendDtmf(c, result); 692 } 693 } 694 695 /*package*/ void 696 startDtmf(char c) { 697 if (DBG) log("startDtmf"); 698 699 ImsCall imscall = mForegroundCall.getImsCall(); 700 if (imscall != null) { 701 imscall.startDtmf(c); 702 } else { 703 loge("startDtmf : no foreground call"); 704 } 705 } 706 707 /*package*/ void 708 stopDtmf() { 709 if (DBG) log("stopDtmf"); 710 711 ImsCall imscall = mForegroundCall.getImsCall(); 712 if (imscall != null) { 713 imscall.stopDtmf(); 714 } else { 715 loge("stopDtmf : no foreground call"); 716 } 717 } 718 719 //***** Called from ImsPhoneConnection 720 721 /*package*/ void 722 hangup (ImsPhoneConnection conn) throws CallStateException { 723 if (DBG) log("hangup connection"); 724 725 if (conn.getOwner() != this) { 726 throw new CallStateException ("ImsPhoneConnection " + conn 727 + "does not belong to ImsPhoneCallTracker " + this); 728 } 729 730 hangup(conn.getCall()); 731 } 732 733 //***** Called from ImsPhoneCall 734 735 /* package */ void 736 hangup (ImsPhoneCall call) throws CallStateException { 737 if (DBG) log("hangup call"); 738 739 if (call.getConnections().size() == 0) { 740 throw new CallStateException("no connections"); 741 } 742 743 ImsCall imsCall = call.getImsCall(); 744 boolean rejectCall = false; 745 746 if (call == mRingingCall) { 747 if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming"); 748 rejectCall = true; 749 } else if (call == mForegroundCall) { 750 if (call.isDialingOrAlerting()) { 751 if (Phone.DEBUG_PHONE) { 752 log("(foregnd) hangup dialing or alerting..."); 753 } 754 } else { 755 if (Phone.DEBUG_PHONE) { 756 log("(foregnd) hangup foreground"); 757 } 758 //held call will be resumed by onCallTerminated 759 } 760 } else if (call == mBackgroundCall) { 761 if (Phone.DEBUG_PHONE) { 762 log("(backgnd) hangup waiting or background"); 763 } 764 } else { 765 throw new CallStateException ("ImsPhoneCall " + call + 766 "does not belong to ImsPhoneCallTracker " + this); 767 } 768 769 call.onHangupLocal(); 770 771 try { 772 if (imsCall != null) { 773 if (rejectCall) imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE); 774 else imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 775 } else if (mPendingMO != null && call == mForegroundCall) { 776 // is holding a foreground call 777 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED); 778 mPendingMO.onDisconnect(); 779 removeConnection(mPendingMO); 780 mPendingMO = null; 781 updatePhoneState(); 782 removeMessages(EVENT_DIAL_PENDINGMO); 783 } 784 } catch (ImsException e) { 785 throw new CallStateException(e.getMessage()); 786 } 787 788 mPhone.notifyPreciseCallStateChanged(); 789 } 790 791 void callEndCleanupHandOverCallIfAny() { 792 if (mHandoverCall.mConnections.size() > 0) { 793 if (DBG) log("callEndCleanupHandOverCallIfAny, mHandoverCall.mConnections=" 794 + mHandoverCall.mConnections); 795 mHandoverCall.mConnections.clear(); 796 mState = PhoneConstants.State.IDLE; 797 } 798 } 799 800 /* package */ 801 void resumeWaitingOrHolding() throws CallStateException { 802 if (DBG) log("resumeWaitingOrHolding"); 803 804 try { 805 if (mForegroundCall.getState().isAlive()) { 806 //resume foreground call after holding background call 807 //they were switched before holding 808 ImsCall imsCall = mForegroundCall.getImsCall(); 809 if (imsCall != null) imsCall.resume(); 810 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) { 811 //accept waiting call after holding background call 812 ImsCall imsCall = mRingingCall.getImsCall(); 813 if (imsCall != null) imsCall.accept(ImsCallProfile.CALL_TYPE_VOICE); 814 } else { 815 //Just resume background call. 816 //To distinguish resuming call with swapping calls 817 //we do not switch calls.here 818 //ImsPhoneConnection.update will chnage the parent when completed 819 ImsCall imsCall = mBackgroundCall.getImsCall(); 820 if (imsCall != null) imsCall.resume(); 821 } 822 } catch (ImsException e) { 823 throw new CallStateException(e.getMessage()); 824 } 825 } 826 827 /* package */ 828 void sendUSSD (String ussdString, Message response) { 829 if (DBG) log("sendUSSD"); 830 831 try { 832 if (mUssdSession != null) { 833 mUssdSession.sendUssd(ussdString); 834 AsyncResult.forMessage(response, null, null); 835 response.sendToTarget(); 836 return; 837 } 838 839 String[] callees = new String[] { ussdString }; 840 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 841 ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE); 842 profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING, 843 ImsCallProfile.DIALSTRING_USSD); 844 845 mUssdSession = mImsManager.makeCall(mServiceId, profile, 846 callees, mImsUssdListener); 847 } catch (ImsException e) { 848 loge("sendUSSD : " + e); 849 mPhone.sendErrorResponse(response, e); 850 } 851 } 852 853 /* package */ 854 void cancelUSSD() { 855 if (mUssdSession == null) return; 856 857 try { 858 mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 859 } catch (ImsException e) { 860 } 861 862 } 863 864 private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) { 865 for (ImsPhoneConnection conn : mConnections) { 866 if (conn.getImsCall() == imsCall) { 867 return conn; 868 } 869 } 870 return null; 871 } 872 873 private synchronized void removeConnection(ImsPhoneConnection conn) { 874 mConnections.remove(conn); 875 } 876 877 private synchronized void addConnection(ImsPhoneConnection conn) { 878 mConnections.add(conn); 879 } 880 881 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) { 882 if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause); 883 884 if (imsCall == null) return; 885 886 boolean changed = false; 887 ImsPhoneConnection conn = findConnection(imsCall); 888 889 if (conn == null) { 890 // TODO : what should be done? 891 return; 892 } 893 894 changed = conn.update(imsCall, state); 895 896 if (state == ImsPhoneCall.State.DISCONNECTED) { 897 changed = conn.onDisconnect(cause) || changed; 898 //detach the disconnected connections 899 conn.getCall().detach(conn); 900 removeConnection(conn); 901 } 902 903 if (changed) { 904 if (conn.getCall() == mHandoverCall) return; 905 updatePhoneState(); 906 mPhone.notifyPreciseCallStateChanged(); 907 } 908 } 909 910 private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) { 911 int cause = DisconnectCause.ERROR_UNSPECIFIED; 912 913 //int type = reasonInfo.getReasonType(); 914 int code = reasonInfo.getCode(); 915 switch (code) { 916 case ImsReasonInfo.CODE_SIP_BAD_ADDRESS: 917 case ImsReasonInfo.CODE_SIP_NOT_REACHABLE: 918 return DisconnectCause.NUMBER_UNREACHABLE; 919 920 case ImsReasonInfo.CODE_SIP_BUSY: 921 return DisconnectCause.BUSY; 922 923 case ImsReasonInfo.CODE_USER_TERMINATED: 924 return DisconnectCause.LOCAL; 925 926 case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE: 927 return DisconnectCause.INCOMING_REJECTED; 928 929 case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE: 930 return DisconnectCause.NORMAL; 931 932 case ImsReasonInfo.CODE_SIP_REDIRECTED: 933 case ImsReasonInfo.CODE_SIP_BAD_REQUEST: 934 case ImsReasonInfo.CODE_SIP_FORBIDDEN: 935 case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE: 936 case ImsReasonInfo.CODE_SIP_USER_REJECTED: 937 case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR: 938 return DisconnectCause.SERVER_ERROR; 939 940 case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE: 941 case ImsReasonInfo.CODE_SIP_NOT_FOUND: 942 case ImsReasonInfo.CODE_SIP_SERVER_ERROR: 943 return DisconnectCause.SERVER_UNREACHABLE; 944 945 case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING: 946 case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED: 947 case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN: 948 case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE: 949 case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED: 950 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE: 951 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE: 952 case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING: 953 return DisconnectCause.OUT_OF_SERVICE; 954 955 case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT: 956 case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING: 957 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER: 958 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE: 959 return DisconnectCause.TIMED_OUT; 960 961 case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY: 962 case ImsReasonInfo.CODE_LOCAL_POWER_OFF: 963 return DisconnectCause.POWER_OFF; 964 965 default: 966 } 967 968 return cause; 969 } 970 971 /** 972 * Listen to the IMS call state change 973 */ 974 private ImsCall.Listener mImsCallListener = new ImsCall.Listener() { 975 @Override 976 public void onCallProgressing(ImsCall imsCall) { 977 if (DBG) log("onCallProgressing"); 978 979 mPendingMO = null; 980 processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING, 981 DisconnectCause.NOT_DISCONNECTED); 982 } 983 984 @Override 985 public void onCallStarted(ImsCall imsCall) { 986 if (DBG) log("onCallStarted"); 987 988 mPendingMO = null; 989 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 990 DisconnectCause.NOT_DISCONNECTED); 991 } 992 993 /** 994 * onCallStartFailed will be invoked when: 995 * case 1) Dialing fails 996 * case 2) Ringing call is disconnected by local or remote user 997 */ 998 @Override 999 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1000 if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode()); 1001 1002 if (mPendingMO != null) { 1003 // To initiate dialing circuit-switched call 1004 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED 1005 && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE 1006 && mRingingCall.getState() == ImsPhoneCall.State.IDLE) { 1007 mForegroundCall.detach(mPendingMO); 1008 removeConnection(mPendingMO); 1009 mPendingMO.finalize(); 1010 mPendingMO = null; 1011 mPhone.initiateSilentRedial(); 1012 return; 1013 } else { 1014 int cause = getDisconnectCauseFromReasonInfo(reasonInfo); 1015 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 1016 } 1017 mPendingMO = null; 1018 } 1019 } 1020 1021 @Override 1022 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1023 if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode()); 1024 1025 ImsPhoneCall.State oldState = mForegroundCall.getState(); 1026 int cause = getDisconnectCauseFromReasonInfo(reasonInfo); 1027 ImsPhoneConnection conn = findConnection(imsCall); 1028 if (DBG) log("cause = " + cause + " conn = " + conn); 1029 1030 if (conn != null && conn.isIncoming() && conn.getConnectTime() == 0) { 1031 // Missed 1032 if (cause == DisconnectCause.NORMAL) { 1033 cause = DisconnectCause.INCOMING_MISSED; 1034 } 1035 if (DBG) log("Incoming connection of 0 connect time detected - translated cause = " 1036 + cause); 1037 1038 } 1039 1040 if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) { 1041 // Call was terminated while it is merged instead of a remote disconnect. 1042 cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY; 1043 } 1044 1045 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 1046 } 1047 1048 @Override 1049 public void onCallHeld(ImsCall imsCall) { 1050 if (DBG) log("onCallHeld"); 1051 1052 synchronized (mSyncHold) { 1053 ImsPhoneCall.State oldState = mBackgroundCall.getState(); 1054 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING, 1055 DisconnectCause.NOT_DISCONNECTED); 1056 1057 // Note: If we're performing a switchWaitingOrHoldingAndActive, the call to 1058 // processCallStateChange above may have caused the mBackgroundCall and 1059 // mForegroundCall references below to change meaning. Watch out for this if you 1060 // are reading through this code. 1061 if (oldState == ImsPhoneCall.State.ACTIVE) { 1062 // Note: This case comes up when we have just held a call in response to a 1063 // switchWaitingOrHoldingAndActive. We now need to resume the background call. 1064 // The EVENT_RESUME_BACKGROUND causes resumeWaitingOrHolding to be called. 1065 if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) 1066 || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) { 1067 1068 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 1069 } else { 1070 //when multiple connections belong to background call, 1071 //only the first callback reaches here 1072 //otherwise the oldState is already HOLDING 1073 if (mPendingMO != null) { 1074 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 1075 } 1076 1077 // In this case there will be no call resumed, so we can assume that we 1078 // are done switching fg and bg calls now. 1079 // This may happen if there is no BG call and we are holding a call so that 1080 // we can dial another one. 1081 mSwitchingFgAndBgCalls = false; 1082 } 1083 } 1084 } 1085 } 1086 1087 @Override 1088 public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1089 if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode()); 1090 1091 synchronized (mSyncHold) { 1092 ImsPhoneCall.State bgState = mBackgroundCall.getState(); 1093 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) { 1094 // disconnected while processing hold 1095 if (mPendingMO != null) { 1096 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 1097 } 1098 } else if (bgState == ImsPhoneCall.State.ACTIVE) { 1099 mForegroundCall.switchWith(mBackgroundCall); 1100 1101 if (mPendingMO != null) { 1102 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 1103 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 1104 } 1105 } 1106 } 1107 } 1108 1109 @Override 1110 public void onCallResumed(ImsCall imsCall) { 1111 if (DBG) log("onCallResumed"); 1112 1113 // If we are the in midst of swapping FG and BG calls and the call we end up resuming 1114 // is not the one we expected, we likely had a resume failure and we need to swap the 1115 // FG and BG calls back. 1116 if (mSwitchingFgAndBgCalls && imsCall != mCallExpectedToResume) { 1117 mForegroundCall.switchWith(mBackgroundCall); 1118 mSwitchingFgAndBgCalls = false; 1119 mCallExpectedToResume = null; 1120 } 1121 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 1122 DisconnectCause.NOT_DISCONNECTED); 1123 } 1124 1125 @Override 1126 public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1127 // TODO : What should be done? 1128 // If we are in the midst of swapping the FG and BG calls and we got a resume fail, we 1129 // need to swap back the FG and BG calls. 1130 if (mSwitchingFgAndBgCalls && imsCall == mCallExpectedToResume) { 1131 mForegroundCall.switchWith(mBackgroundCall); 1132 mCallExpectedToResume = null; 1133 mSwitchingFgAndBgCalls = false; 1134 } 1135 mPhone.notifySuppServiceFailed(Phone.SuppService.RESUME); 1136 } 1137 1138 @Override 1139 public void onCallResumeReceived(ImsCall imsCall) { 1140 if (DBG) log("onCallResumeReceived"); 1141 1142 if (mOnHoldToneStarted) { 1143 mPhone.stopOnHoldTone(); 1144 mOnHoldToneStarted = false; 1145 } 1146 } 1147 1148 @Override 1149 public void onCallHoldReceived(ImsCall imsCall) { 1150 if (DBG) log("onCallHoldReceived"); 1151 1152 ImsPhoneConnection conn = findConnection(imsCall); 1153 if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) { 1154 if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) { 1155 mPhone.startOnHoldTone(); 1156 mOnHoldToneStarted = true; 1157 } 1158 } 1159 } 1160 1161 @Override 1162 public void onCallMerged(ImsCall call, boolean swapCalls) { 1163 if (DBG) log("onCallMerged"); 1164 1165 mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState()); 1166 if (swapCalls) { 1167 try { 1168 switchWaitingOrHoldingAndActive(); 1169 } catch (CallStateException e) { 1170 if (Phone.DEBUG_PHONE) { 1171 loge("Failed swap fg and bg calls on merge exception=" + e); 1172 } 1173 } 1174 } 1175 updatePhoneState(); 1176 mPhone.notifyPreciseCallStateChanged(); 1177 } 1178 1179 @Override 1180 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 1181 if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo); 1182 mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE); 1183 } 1184 1185 /** 1186 * Called when the state of IMS conference participant(s) has changed. 1187 * 1188 * @param call the call object that carries out the IMS call. 1189 * @param participants the participant(s) and their new state information. 1190 */ 1191 @Override 1192 public void onConferenceParticipantsStateChanged(ImsCall call, 1193 List<ConferenceParticipant> participants) { 1194 if (DBG) log("onConferenceParticipantsStateChanged"); 1195 1196 ImsPhoneConnection conn = findConnection(call); 1197 if (conn != null) { 1198 conn.updateConferenceParticipants(participants); 1199 } 1200 } 1201 1202 @Override 1203 public void onCallSessionTtyModeReceived(ImsCall call, int mode) { 1204 mPhone.onTtyModeReceived(mode); 1205 } 1206 1207 @Override 1208 public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 1209 ImsReasonInfo reasonInfo) { 1210 if (DBG) { 1211 log("onCallHandover :: srcAccessTech=" + srcAccessTech + ", targetAccessTech=" + 1212 targetAccessTech + ", reasonInfo=" + reasonInfo); 1213 } 1214 } 1215 1216 @Override 1217 public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 1218 ImsReasonInfo reasonInfo) { 1219 if (DBG) { 1220 log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech + 1221 ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo); 1222 } 1223 } 1224 }; 1225 1226 /** 1227 * Listen to the IMS call state change 1228 */ 1229 private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() { 1230 @Override 1231 public void onCallStarted(ImsCall imsCall) { 1232 if (DBG) log("mImsUssdListener onCallStarted"); 1233 1234 if (imsCall == mUssdSession) { 1235 if (mPendingUssd != null) { 1236 AsyncResult.forMessage(mPendingUssd); 1237 mPendingUssd.sendToTarget(); 1238 mPendingUssd = null; 1239 } 1240 } 1241 } 1242 1243 @Override 1244 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1245 if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode()); 1246 1247 onCallTerminated(imsCall, reasonInfo); 1248 } 1249 1250 @Override 1251 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1252 if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); 1253 1254 if (imsCall == mUssdSession) { 1255 mUssdSession = null; 1256 if (mPendingUssd != null) { 1257 CommandException ex = 1258 new CommandException(CommandException.Error.GENERIC_FAILURE); 1259 AsyncResult.forMessage(mPendingUssd, null, ex); 1260 mPendingUssd.sendToTarget(); 1261 mPendingUssd = null; 1262 } 1263 } 1264 imsCall.close(); 1265 } 1266 1267 @Override 1268 public void onCallUssdMessageReceived(ImsCall call, 1269 int mode, String ussdMessage) { 1270 if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode); 1271 1272 int ussdMode = -1; 1273 1274 switch(mode) { 1275 case ImsCall.USSD_MODE_REQUEST: 1276 ussdMode = CommandsInterface.USSD_MODE_REQUEST; 1277 break; 1278 1279 case ImsCall.USSD_MODE_NOTIFY: 1280 ussdMode = CommandsInterface.USSD_MODE_NOTIFY; 1281 break; 1282 } 1283 1284 mPhone.onIncomingUSSD(ussdMode, ussdMessage); 1285 } 1286 }; 1287 1288 /** 1289 * Listen to the IMS service state change 1290 * 1291 */ 1292 private ImsConnectionStateListener mImsConnectionStateListener = 1293 new ImsConnectionStateListener() { 1294 @Override 1295 public void onImsConnected() { 1296 if (DBG) log("onImsConnected"); 1297 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1298 mPhone.setImsRegistered(true); 1299 } 1300 1301 @Override 1302 public void onImsDisconnected(ImsReasonInfo imsReasonInfo) { 1303 if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo); 1304 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1305 mPhone.setImsRegistered(false); 1306 } 1307 1308 @Override 1309 public void onImsProgressing() { 1310 if (DBG) log("onImsProgressing"); 1311 } 1312 1313 @Override 1314 public void onImsResumed() { 1315 if (DBG) log("onImsResumed"); 1316 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1317 } 1318 1319 @Override 1320 public void onImsSuspended() { 1321 if (DBG) log("onImsSuspended"); 1322 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1323 } 1324 1325 @Override 1326 public void onFeatureCapabilityChanged(int serviceClass, 1327 int[] enabledFeatures, int[] disabledFeatures) { 1328 if (serviceClass == ImsServiceClass.MMTEL) { 1329 // Check enabledFeatures to determine capabilities. We ignore disabledFeatures. 1330 for (int i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE; 1331 i <= ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI; i++) { 1332 if (enabledFeatures[i] == i) { 1333 // If the feature is set to its own integer value it is enabled. 1334 if (DBG) log("onFeatureCapabilityChanged: i=" + i + ", value=true"); 1335 mImsFeatureEnabled[i] = true; 1336 } else if (enabledFeatures[i] 1337 == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) { 1338 // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled. 1339 if (DBG) log("onFeatureCapabilityChanged: i=" + i + ", value=false"); 1340 mImsFeatureEnabled[i] = false; 1341 } else { 1342 // Feature has unknown state; it is not its own value or -1. 1343 if (DBG) { 1344 loge("onFeatureCapabilityChanged: i=" + i + ", unexpectedValue=" 1345 + enabledFeatures[i]); 1346 } 1347 } 1348 } 1349 1350 // TODO: Use the ImsCallSession or ImsCallProfile to tell the initial Wifi state and 1351 // {@link ImsCallSession.Listener#callSessionHandover} to listen for changes to 1352 // wifi capability caused by a handover. 1353 if (DBG) log("onFeatureCapabilityChanged: isVowifiEnabled=" + isVowifiEnabled()); 1354 for (ImsPhoneConnection connection : mConnections) { 1355 connection.updateWifiState(); 1356 } 1357 } 1358 1359 if (DBG) log("onFeatureCapabilityChanged: mImsFeatureEnabled=" + mImsFeatureEnabled); 1360 } 1361 }; 1362 1363 /* package */ 1364 ImsUtInterface getUtInterface() throws ImsException { 1365 if (mImsManager == null) { 1366 throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED); 1367 } 1368 1369 ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId); 1370 return ut; 1371 } 1372 1373 private void transferHandoverConnections(ImsPhoneCall call) { 1374 if (call.mConnections != null) { 1375 for (Connection c : call.mConnections) { 1376 c.mPreHandoverState = call.mState; 1377 log ("Connection state before handover is " + c.getStateBeforeHandover()); 1378 } 1379 } 1380 if (mHandoverCall.mConnections == null ) { 1381 mHandoverCall.mConnections = call.mConnections; 1382 } else { // Multi-call SRVCC 1383 mHandoverCall.mConnections.addAll(call.mConnections); 1384 } 1385 if (mHandoverCall.mConnections != null) { 1386 if (call.getImsCall() != null) { 1387 call.getImsCall().close(); 1388 } 1389 for (Connection c : mHandoverCall.mConnections) { 1390 ((ImsPhoneConnection)c).changeParent(mHandoverCall); 1391 ((ImsPhoneConnection)c).releaseWakeLock(); 1392 } 1393 } 1394 if (call.getState().isAlive()) { 1395 log ("Call is alive and state is " + call.mState); 1396 mHandoverCall.mState = call.mState; 1397 } 1398 call.mConnections.clear(); 1399 call.mState = ImsPhoneCall.State.IDLE; 1400 } 1401 1402 /* package */ 1403 void notifySrvccState(Call.SrvccState state) { 1404 if (DBG) log("notifySrvccState state=" + state); 1405 1406 mSrvccState = state; 1407 1408 if (mSrvccState == Call.SrvccState.COMPLETED) { 1409 transferHandoverConnections(mForegroundCall); 1410 transferHandoverConnections(mBackgroundCall); 1411 transferHandoverConnections(mRingingCall); 1412 } 1413 } 1414 1415 //****** Overridden from Handler 1416 1417 @Override 1418 public void 1419 handleMessage (Message msg) { 1420 AsyncResult ar; 1421 if (DBG) log("handleMessage what=" + msg.what); 1422 1423 switch (msg.what) { 1424 case EVENT_HANGUP_PENDINGMO: 1425 if (mPendingMO != null) { 1426 mPendingMO.onDisconnect(); 1427 removeConnection(mPendingMO); 1428 mPendingMO = null; 1429 } 1430 1431 updatePhoneState(); 1432 mPhone.notifyPreciseCallStateChanged(); 1433 break; 1434 case EVENT_RESUME_BACKGROUND: 1435 try { 1436 resumeWaitingOrHolding(); 1437 } catch (CallStateException e) { 1438 if (Phone.DEBUG_PHONE) { 1439 loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e); 1440 } 1441 } 1442 break; 1443 case EVENT_DIAL_PENDINGMO: 1444 dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY); 1445 break; 1446 1447 case EVENT_EXIT_ECM_RESPONSE_CDMA: 1448 // no matter the result, we still do the same here 1449 if (pendingCallInEcm) { 1450 dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState); 1451 pendingCallInEcm = false; 1452 } 1453 mPhone.unsetOnEcbModeExitResponse(this); 1454 break; 1455 } 1456 } 1457 1458 @Override 1459 protected void log(String msg) { 1460 Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 1461 } 1462 1463 protected void loge(String msg) { 1464 Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 1465 } 1466 1467 @Override 1468 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1469 pw.println("ImsPhoneCallTracker extends:"); 1470 super.dump(fd, pw, args); 1471 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1472 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1473 pw.println(" mRingingCall=" + mRingingCall); 1474 pw.println(" mForegroundCall=" + mForegroundCall); 1475 pw.println(" mBackgroundCall=" + mBackgroundCall); 1476 pw.println(" mHandoverCall=" + mHandoverCall); 1477 pw.println(" mPendingMO=" + mPendingMO); 1478 //pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1479 pw.println(" mPhone=" + mPhone); 1480 pw.println(" mDesiredMute=" + mDesiredMute); 1481 pw.println(" mState=" + mState); 1482 } 1483 1484 @Override 1485 protected void handlePollCalls(AsyncResult ar) { 1486 } 1487 1488 /* package */ 1489 ImsEcbm getEcbmInterface() throws ImsException { 1490 if (mImsManager == null) { 1491 throw new ImsException("no ims manager", ImsReasonInfo.CODE_UNSPECIFIED); 1492 } 1493 1494 ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId); 1495 return ecbm; 1496 } 1497 1498 public boolean isInEmergencyCall() { 1499 return mIsInEmergencyCall; 1500 } 1501 1502 public boolean isVolteEnabled() { 1503 return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE]; 1504 } 1505 1506 public boolean isVowifiEnabled() { 1507 return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI]; 1508 } 1509 1510 public boolean isVtEnabled() { 1511 return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] 1512 || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]); 1513 } 1514 @Override 1515 public PhoneConstants.State getState() { 1516 return mState; 1517 } 1518} 1519