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