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