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