CdmaCallTracker.java revision bb097a7ba08252c516e578ce30cdb658b63d7c9b
1/* 2 * Copyright (C) 2006 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.cdma; 18 19import android.os.AsyncResult; 20import android.os.Handler; 21import android.os.Message; 22import android.os.Registrant; 23import android.os.RegistrantList; 24import android.telephony.DisconnectCause; 25import android.telephony.PhoneNumberUtils; 26import android.telephony.ServiceState; 27import android.telephony.Rlog; 28import android.os.SystemProperties; 29 30import com.android.internal.telephony.CallStateException; 31import com.android.internal.telephony.CallTracker; 32import com.android.internal.telephony.CommandsInterface; 33import com.android.internal.telephony.Connection; 34import com.android.internal.telephony.DriverCall; 35import com.android.internal.telephony.Phone; 36import com.android.internal.telephony.PhoneBase; 37import com.android.internal.telephony.PhoneConstants; 38import com.android.internal.telephony.TelephonyProperties; 39 40import java.io.FileDescriptor; 41import java.io.PrintWriter; 42import java.util.ArrayList; 43import java.util.List; 44 45 46/** 47 * {@hide} 48 */ 49public final class CdmaCallTracker extends CallTracker { 50 static final String LOG_TAG = "CdmaCallTracker"; 51 52 private static final boolean REPEAT_POLLING = false; 53 54 private static final boolean DBG_POLL = false; 55 56 //***** Constants 57 58 static final int MAX_CONNECTIONS = 8; 59 static final int MAX_CONNECTIONS_PER_CALL = 1; // only 1 connection allowed per call 60 61 //***** Instance Variables 62 63 CdmaConnection mConnections[] = new CdmaConnection[MAX_CONNECTIONS]; 64 RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 65 RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 66 RegistrantList mCallWaitingRegistrants = new RegistrantList(); 67 68 69 // connections dropped during last poll 70 ArrayList<CdmaConnection> mDroppedDuringPoll 71 = new ArrayList<CdmaConnection>(MAX_CONNECTIONS); 72 73 CdmaCall mRingingCall = new CdmaCall(this); 74 // A call that is ringing or (call) waiting 75 CdmaCall mForegroundCall = new CdmaCall(this); 76 CdmaCall mBackgroundCall = new CdmaCall(this); 77 78 CdmaConnection mPendingMO; 79 boolean mHangupPendingMO; 80 boolean mPendingCallInEcm=false; 81 boolean mIsInEmergencyCall = false; 82 CDMAPhone mPhone; 83 84 boolean mDesiredMute = false; // false = mute off 85 86 int mPendingCallClirMode; 87 PhoneConstants.State mState = PhoneConstants.State.IDLE; 88 89 private boolean mIsEcmTimerCanceled = false; 90 91 private int m3WayCallFlashDelay = 0; 92// boolean needsPoll; 93 94 95 96 //***** Events 97 98 //***** Constructors 99 CdmaCallTracker(CDMAPhone phone) { 100 mPhone = phone; 101 mCi = phone.mCi; 102 mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); 103 mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null); 104 mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); 105 mCi.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null); 106 mForegroundCall.setGeneric(false); 107 } 108 109 public void dispose() { 110 Rlog.d(LOG_TAG, "CdmaCallTracker dispose"); 111 mCi.unregisterForLineControlInfo(this); 112 mCi.unregisterForCallStateChanged(this); 113 mCi.unregisterForOn(this); 114 mCi.unregisterForNotAvailable(this); 115 mCi.unregisterForCallWaitingInfo(this); 116 117 clearDisconnected(); 118 119 } 120 121 @Override 122 protected void finalize() { 123 Rlog.d(LOG_TAG, "CdmaCallTracker finalized"); 124 } 125 126 //***** Instance Methods 127 128 //***** Public Methods 129 @Override 130 public void registerForVoiceCallStarted(Handler h, int what, Object obj) { 131 Registrant r = new Registrant(h, what, obj); 132 mVoiceCallStartedRegistrants.add(r); 133 // Notify if in call when registering 134 if (mState != PhoneConstants.State.IDLE) { 135 r.notifyRegistrant(new AsyncResult(null, null, null)); 136 } 137 } 138 @Override 139 public void unregisterForVoiceCallStarted(Handler h) { 140 mVoiceCallStartedRegistrants.remove(h); 141 } 142 143 @Override 144 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 145 Registrant r = new Registrant(h, what, obj); 146 mVoiceCallEndedRegistrants.add(r); 147 } 148 149 @Override 150 public void unregisterForVoiceCallEnded(Handler h) { 151 mVoiceCallEndedRegistrants.remove(h); 152 } 153 154 public void registerForCallWaiting(Handler h, int what, Object obj) { 155 Registrant r = new Registrant (h, what, obj); 156 mCallWaitingRegistrants.add(r); 157 } 158 159 public void unregisterForCallWaiting(Handler h) { 160 mCallWaitingRegistrants.remove(h); 161 } 162 163 /** 164 * clirMode is one of the CLIR_ constants 165 */ 166 Connection 167 dial (String dialString, int clirMode) throws CallStateException { 168 // note that this triggers call state changed notif 169 clearDisconnected(); 170 171 if (!canDial()) { 172 throw new CallStateException("cannot dial in current state"); 173 } 174 175 String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 176 boolean isPhoneInEcmMode = inEcm.equals("true"); 177 boolean isEmergencyCall = 178 PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString); 179 180 // Cancel Ecm timer if a second emergency call is originating in Ecm mode 181 if (isPhoneInEcmMode && isEmergencyCall) { 182 handleEcmTimer(CDMAPhone.CANCEL_ECM_TIMER); 183 } 184 185 // We are initiating a call therefore even if we previously 186 // didn't know the state (i.e. Generic was true) we now know 187 // and therefore can set Generic to false. 188 mForegroundCall.setGeneric(false); 189 190 // The new call must be assigned to the foreground call. 191 // That call must be idle, so place anything that's 192 // there on hold 193 if (mForegroundCall.getState() == CdmaCall.State.ACTIVE) { 194 return dialThreeWay(dialString); 195 } 196 197 mPendingMO = new CdmaConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString), 198 this, mForegroundCall); 199 mHangupPendingMO = false; 200 201 if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0 202 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) { 203 // Phone number is invalid 204 mPendingMO.mCause = DisconnectCause.INVALID_NUMBER; 205 206 // handlePollCalls() will notice this call not present 207 // and will mark it as dropped. 208 pollCallsWhenSafe(); 209 } else { 210 // Always unmute when initiating a new call 211 setMute(false); 212 213 // Check data call 214 disableDataCallInEmergencyCall(dialString); 215 216 // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit. 217 if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) { 218 mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage()); 219 } else { 220 mPhone.exitEmergencyCallbackMode(); 221 mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null); 222 mPendingCallClirMode=clirMode; 223 mPendingCallInEcm=true; 224 } 225 } 226 227 updatePhoneState(); 228 mPhone.notifyPreciseCallStateChanged(); 229 230 return mPendingMO; 231 } 232 233 234 Connection 235 dial (String dialString) throws CallStateException { 236 return dial(dialString, CommandsInterface.CLIR_DEFAULT); 237 } 238 239 private Connection 240 dialThreeWay (String dialString) { 241 if (!mForegroundCall.isIdle()) { 242 // Check data call 243 disableDataCallInEmergencyCall(dialString); 244 245 // Attach the new connection to foregroundCall 246 mPendingMO = new CdmaConnection(mPhone.getContext(), 247 checkForTestEmergencyNumber(dialString), this, mForegroundCall); 248 // Some network need a empty flash before sending the normal one 249 m3WayCallFlashDelay = mPhone.getContext().getResources() 250 .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay); 251 if (m3WayCallFlashDelay > 0) { 252 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH)); 253 } else { 254 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 255 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 256 } 257 return mPendingMO; 258 } 259 return null; 260 } 261 262 void 263 acceptCall() throws CallStateException { 264 if (mRingingCall.getState() == CdmaCall.State.INCOMING) { 265 Rlog.i("phone", "acceptCall: incoming..."); 266 // Always unmute when answering a new call 267 setMute(false); 268 mCi.acceptCall(obtainCompleteMessage()); 269 } else if (mRingingCall.getState() == CdmaCall.State.WAITING) { 270 CdmaConnection cwConn = (CdmaConnection)(mRingingCall.getLatestConnection()); 271 272 // Since there is no network response for supplimentary 273 // service for CDMA, we assume call waiting is answered. 274 // ringing Call state change to idle is in CdmaCall.detach 275 // triggered by updateParent. 276 cwConn.updateParent(mRingingCall, mForegroundCall); 277 cwConn.onConnectedInOrOut(); 278 updatePhoneState(); 279 switchWaitingOrHoldingAndActive(); 280 } else { 281 throw new CallStateException("phone not ringing"); 282 } 283 } 284 285 void 286 rejectCall () throws CallStateException { 287 // AT+CHLD=0 means "release held or UDUB" 288 // so if the phone isn't ringing, this could hang up held 289 if (mRingingCall.getState().isRinging()) { 290 mCi.rejectCall(obtainCompleteMessage()); 291 } else { 292 throw new CallStateException("phone not ringing"); 293 } 294 } 295 296 void 297 switchWaitingOrHoldingAndActive() throws CallStateException { 298 // Should we bother with this check? 299 if (mRingingCall.getState() == CdmaCall.State.INCOMING) { 300 throw new CallStateException("cannot be in the incoming state"); 301 } else if (mForegroundCall.getConnections().size() > 1) { 302 flashAndSetGenericTrue(); 303 } else { 304 // Send a flash command to CDMA network for putting the other party on hold. 305 // For CDMA networks which do not support this the user would just hear a beep 306 // from the network. For CDMA networks which do support it will put the other 307 // party on hold. 308 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); 309 } 310 } 311 312 void 313 conference() { 314 // Should we be checking state? 315 flashAndSetGenericTrue(); 316 } 317 318 void 319 explicitCallTransfer() { 320 mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); 321 } 322 323 void 324 clearDisconnected() { 325 internalClearDisconnected(); 326 327 updatePhoneState(); 328 mPhone.notifyPreciseCallStateChanged(); 329 } 330 331 boolean 332 canConference() { 333 return mForegroundCall.getState() == CdmaCall.State.ACTIVE 334 && mBackgroundCall.getState() == CdmaCall.State.HOLDING 335 && !mBackgroundCall.isFull() 336 && !mForegroundCall.isFull(); 337 } 338 339 boolean 340 canDial() { 341 boolean ret; 342 int serviceState = mPhone.getServiceState().getState(); 343 String disableCall = SystemProperties.get( 344 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 345 346 ret = (serviceState != ServiceState.STATE_POWER_OFF) 347 && mPendingMO == null 348 && !mRingingCall.isRinging() 349 && !disableCall.equals("true") 350 && (!mForegroundCall.getState().isAlive() 351 || (mForegroundCall.getState() == CdmaCall.State.ACTIVE) 352 || !mBackgroundCall.getState().isAlive()); 353 354 if (!ret) { 355 log(String.format("canDial is false\n" + 356 "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" + 357 "&& pendingMO == null::=%s\n" + 358 "&& !ringingCall.isRinging()::=%s\n" + 359 "&& !disableCall.equals(\"true\")::=%s\n" + 360 "&& (!foregroundCall.getState().isAlive()::=%s\n" + 361 " || foregroundCall.getState() == CdmaCall.State.ACTIVE::=%s\n" + 362 " ||!backgroundCall.getState().isAlive())::=%s)", 363 serviceState, 364 serviceState != ServiceState.STATE_POWER_OFF, 365 mPendingMO == null, 366 !mRingingCall.isRinging(), 367 !disableCall.equals("true"), 368 !mForegroundCall.getState().isAlive(), 369 mForegroundCall.getState() == CdmaCall.State.ACTIVE, 370 !mBackgroundCall.getState().isAlive())); 371 } 372 return ret; 373 } 374 375 boolean 376 canTransfer() { 377 Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA"); 378 return false; 379 } 380 381 //***** Private Instance Methods 382 383 private void 384 internalClearDisconnected() { 385 mRingingCall.clearDisconnected(); 386 mForegroundCall.clearDisconnected(); 387 mBackgroundCall.clearDisconnected(); 388 } 389 390 /** 391 * Obtain a message to use for signalling "invoke getCurrentCalls() when 392 * this operation and all other pending operations are complete 393 */ 394 private Message 395 obtainCompleteMessage() { 396 return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); 397 } 398 399 /** 400 * Obtain a message to use for signalling "invoke getCurrentCalls() when 401 * this operation and all other pending operations are complete 402 */ 403 private Message 404 obtainCompleteMessage(int what) { 405 mPendingOperations++; 406 mLastRelevantPoll = null; 407 mNeedsPoll = true; 408 409 if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + 410 mPendingOperations + ", needsPoll=" + mNeedsPoll); 411 412 return obtainMessage(what); 413 } 414 415 private void 416 operationComplete() { 417 mPendingOperations--; 418 419 if (DBG_POLL) log("operationComplete: pendingOperations=" + 420 mPendingOperations + ", needsPoll=" + mNeedsPoll); 421 422 if (mPendingOperations == 0 && mNeedsPoll) { 423 mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); 424 mCi.getCurrentCalls(mLastRelevantPoll); 425 } else if (mPendingOperations < 0) { 426 // this should never happen 427 Rlog.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0"); 428 mPendingOperations = 0; 429 } 430 } 431 432 433 434 private void 435 updatePhoneState() { 436 PhoneConstants.State oldState = mState; 437 438 if (mRingingCall.isRinging()) { 439 mState = PhoneConstants.State.RINGING; 440 } else if (mPendingMO != null || 441 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 442 mState = PhoneConstants.State.OFFHOOK; 443 } else { 444 mState = PhoneConstants.State.IDLE; 445 } 446 447 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 448 mVoiceCallEndedRegistrants.notifyRegistrants( 449 new AsyncResult(null, null, null)); 450 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 451 mVoiceCallStartedRegistrants.notifyRegistrants ( 452 new AsyncResult(null, null, null)); 453 } 454 if (Phone.DEBUG_PHONE) { 455 log("update phone state, old=" + oldState + " new="+ mState); 456 } 457 if (mState != oldState) { 458 mPhone.notifyPhoneStateChanged(); 459 } 460 } 461 462 // ***** Overwritten from CallTracker 463 464 @Override 465 protected void 466 handlePollCalls(AsyncResult ar) { 467 List polledCalls; 468 469 if (ar.exception == null) { 470 polledCalls = (List)ar.result; 471 } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { 472 // just a dummy empty ArrayList to cause the loop 473 // to hang up all the calls 474 polledCalls = new ArrayList(); 475 } else { 476 // Radio probably wasn't ready--try again in a bit 477 // But don't keep polling if the channel is closed 478 pollCallsAfterDelay(); 479 return; 480 } 481 482 Connection newRinging = null; //or waiting 483 boolean hasNonHangupStateChanged = false; // Any change besides 484 // a dropped connection 485 boolean hasAnyCallDisconnected = false; 486 boolean needsPollDelay = false; 487 boolean unknownConnectionAppeared = false; 488 489 for (int i = 0, curDC = 0, dcSize = polledCalls.size() 490 ; i < mConnections.length; i++) { 491 CdmaConnection conn = mConnections[i]; 492 DriverCall dc = null; 493 494 // polledCall list is sparse 495 if (curDC < dcSize) { 496 dc = (DriverCall) polledCalls.get(curDC); 497 498 if (dc.index == i+1) { 499 curDC++; 500 } else { 501 dc = null; 502 } 503 } 504 505 if (DBG_POLL) log("poll: conn[i=" + i + "]=" + 506 conn+", dc=" + dc); 507 508 if (conn == null && dc != null) { 509 // Connection appeared in CLCC response that we don't know about 510 if (mPendingMO != null && mPendingMO.compareTo(dc)) { 511 512 if (DBG_POLL) log("poll: pendingMO=" + mPendingMO); 513 514 // It's our pending mobile originating call 515 mConnections[i] = mPendingMO; 516 mPendingMO.mIndex = i; 517 mPendingMO.update(dc); 518 mPendingMO = null; 519 520 // Someone has already asked to hangup this call 521 if (mHangupPendingMO) { 522 mHangupPendingMO = false; 523 // Re-start Ecm timer when an uncompleted emergency call ends 524 if (mIsEcmTimerCanceled) { 525 handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER); 526 } 527 528 try { 529 if (Phone.DEBUG_PHONE) log( 530 "poll: hangupPendingMO, hangup conn " + i); 531 hangup(mConnections[i]); 532 } catch (CallStateException ex) { 533 Rlog.e(LOG_TAG, "unexpected error on hangup"); 534 } 535 536 // Do not continue processing this poll 537 // Wait for hangup and repoll 538 return; 539 } 540 } else { 541 if (Phone.DEBUG_PHONE) { 542 log("pendingMo=" + mPendingMO + ", dc=" + dc); 543 } 544 mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i); 545 546 if (mHandoverConnection != null) { 547 // Single Radio Voice Call Continuity (SRVCC) completed 548 mPhone.migrateFrom((PhoneBase) mPhone.getImsPhone()); 549 mConnections[i].migrateFrom(mHandoverConnection); 550 mPhone.notifyHandoverStateChanged(mConnections[i]); 551 mHandoverConnection = null; 552 } else { 553 // find if the MT call is a new ring or unknown connection 554 newRinging = checkMtFindNewRinging(dc,i); 555 if (newRinging == null) { 556 unknownConnectionAppeared = true; 557 } 558 } 559 checkAndEnableDataCallAfterEmergencyCallDropped(); 560 } 561 hasNonHangupStateChanged = true; 562 } else if (conn != null && dc == null) { 563 // This case means the RIL has no more active call anymore and 564 // we need to clean up the foregroundCall and ringingCall. 565 // Loop through foreground call connections as 566 // it contains the known logical connections. 567 int count = mForegroundCall.mConnections.size(); 568 for (int n = 0; n < count; n++) { 569 if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll"); 570 CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n); 571 mDroppedDuringPoll.add(cn); 572 } 573 count = mRingingCall.mConnections.size(); 574 // Loop through ringing call connections as 575 // it may contain the known logical connections. 576 for (int n = 0; n < count; n++) { 577 if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll"); 578 CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n); 579 mDroppedDuringPoll.add(cn); 580 } 581 mForegroundCall.setGeneric(false); 582 mRingingCall.setGeneric(false); 583 584 // Re-start Ecm timer when the connected emergency call ends 585 if (mIsEcmTimerCanceled) { 586 handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER); 587 } 588 // If emergency call is not going through while dialing 589 checkAndEnableDataCallAfterEmergencyCallDropped(); 590 591 // Dropped connections are removed from the CallTracker 592 // list but kept in the Call list 593 mConnections[i] = null; 594 } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ 595 // Call collision case 596 if (conn.isIncoming() != dc.isMT) { 597 if (dc.isMT == true){ 598 // Mt call takes precedence than Mo,drops Mo 599 mDroppedDuringPoll.add(conn); 600 // find if the MT call is a new ring or unknown connection 601 newRinging = checkMtFindNewRinging(dc,i); 602 if (newRinging == null) { 603 unknownConnectionAppeared = true; 604 } 605 checkAndEnableDataCallAfterEmergencyCallDropped(); 606 } else { 607 // Call info stored in conn is not consistent with the call info from dc. 608 // We should follow the rule of MT calls taking precedence over MO calls 609 // when there is conflict, so here we drop the call info from dc and 610 // continue to use the call info from conn, and only take a log. 611 Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc); 612 } 613 } else { 614 boolean changed; 615 changed = conn.update(dc); 616 hasNonHangupStateChanged = hasNonHangupStateChanged || changed; 617 } 618 } 619 620 if (REPEAT_POLLING) { 621 if (dc != null) { 622 // FIXME with RIL, we should not need this anymore 623 if ((dc.state == DriverCall.State.DIALING 624 /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) 625 || (dc.state == DriverCall.State.ALERTING 626 /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) 627 || (dc.state == DriverCall.State.INCOMING 628 /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) 629 || (dc.state == DriverCall.State.WAITING 630 /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/) 631 ) { 632 // Sometimes there's no unsolicited notification 633 // for state transitions 634 needsPollDelay = true; 635 } 636 } 637 } 638 } 639 640 // This is the first poll after an ATD. 641 // We expect the pending call to appear in the list 642 // If it does not, we land here 643 if (mPendingMO != null) { 644 Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:" 645 + mForegroundCall.getState()); 646 647 mDroppedDuringPoll.add(mPendingMO); 648 mPendingMO = null; 649 mHangupPendingMO = false; 650 if( mPendingCallInEcm) { 651 mPendingCallInEcm = false; 652 } 653 } 654 655 if (newRinging != null) { 656 mPhone.notifyNewRingingConnection(newRinging); 657 } 658 659 // clear the "local hangup" and "missed/rejected call" 660 // cases from the "dropped during poll" list 661 // These cases need no "last call fail" reason 662 for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) { 663 CdmaConnection conn = mDroppedDuringPoll.get(i); 664 665 if (conn.isIncoming() && conn.getConnectTime() == 0) { 666 // Missed or rejected call 667 int cause; 668 if (conn.mCause == DisconnectCause.LOCAL) { 669 cause = DisconnectCause.INCOMING_REJECTED; 670 } else { 671 cause = DisconnectCause.INCOMING_MISSED; 672 } 673 674 if (Phone.DEBUG_PHONE) { 675 log("missed/rejected call, conn.cause=" + conn.mCause); 676 log("setting cause to " + cause); 677 } 678 mDroppedDuringPoll.remove(i); 679 hasAnyCallDisconnected |= conn.onDisconnect(cause); 680 } else if (conn.mCause == DisconnectCause.LOCAL 681 || conn.mCause == DisconnectCause.INVALID_NUMBER) { 682 mDroppedDuringPoll.remove(i); 683 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); 684 } 685 } 686 687 // Any non-local disconnects: determine cause 688 if (mDroppedDuringPoll.size() > 0) { 689 mCi.getLastCallFailCause( 690 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); 691 } 692 693 if (needsPollDelay) { 694 pollCallsAfterDelay(); 695 } 696 697 // Cases when we can no longer keep disconnected Connection's 698 // with their previous calls 699 // 1) the phone has started to ring 700 // 2) A Call/Connection object has changed state... 701 // we may have switched or held or answered (but not hung up) 702 if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) { 703 internalClearDisconnected(); 704 } 705 706 updatePhoneState(); 707 708 if (unknownConnectionAppeared) { 709 mPhone.notifyUnknownConnection(); 710 } 711 712 if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { 713 mPhone.notifyPreciseCallStateChanged(); 714 } 715 716 //dumpState(); 717 } 718 719 //***** Called from CdmaConnection 720 /*package*/ void 721 hangup (CdmaConnection conn) throws CallStateException { 722 if (conn.mOwner != this) { 723 throw new CallStateException ("CdmaConnection " + conn 724 + "does not belong to CdmaCallTracker " + this); 725 } 726 727 if (conn == mPendingMO) { 728 // We're hanging up an outgoing call that doesn't have it's 729 // GSM index assigned yet 730 731 if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); 732 mHangupPendingMO = true; 733 } else if ((conn.getCall() == mRingingCall) 734 && (mRingingCall.getState() == CdmaCall.State.WAITING)) { 735 // Handle call waiting hang up case. 736 // 737 // The ringingCall state will change to IDLE in CdmaCall.detach 738 // if the ringing call connection size is 0. We don't specifically 739 // set the ringing call state to IDLE here to avoid a race condition 740 // where a new call waiting could get a hang up from an old call 741 // waiting ringingCall. 742 // 743 // PhoneApp does the call log itself since only PhoneApp knows 744 // the hangup reason is user ignoring or timing out. So conn.onDisconnect() 745 // is not called here. Instead, conn.onLocalDisconnect() is called. 746 conn.onLocalDisconnect(); 747 updatePhoneState(); 748 mPhone.notifyPreciseCallStateChanged(); 749 return; 750 } else { 751 try { 752 mCi.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage()); 753 } catch (CallStateException ex) { 754 // Ignore "connection not found" 755 // Call may have hung up already 756 Rlog.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection " 757 + conn); 758 } 759 } 760 761 conn.onHangupLocal(); 762 } 763 764 /*package*/ void 765 separate (CdmaConnection conn) throws CallStateException { 766 if (conn.mOwner != this) { 767 throw new CallStateException ("CdmaConnection " + conn 768 + "does not belong to CdmaCallTracker " + this); 769 } 770 try { 771 mCi.separateConnection (conn.getCDMAIndex(), 772 obtainCompleteMessage(EVENT_SEPARATE_RESULT)); 773 } catch (CallStateException ex) { 774 // Ignore "connection not found" 775 // Call may have hung up already 776 Rlog.w(LOG_TAG,"CdmaCallTracker WARN: separate() on absent connection " 777 + conn); 778 } 779 } 780 781 //***** Called from CDMAPhone 782 783 /*package*/ void 784 setMute(boolean mute) { 785 mDesiredMute = mute; 786 mCi.setMute(mDesiredMute, null); 787 } 788 789 /*package*/ boolean 790 getMute() { 791 return mDesiredMute; 792 } 793 794 795 //***** Called from CdmaCall 796 797 /* package */ void 798 hangup (CdmaCall call) throws CallStateException { 799 if (call.getConnections().size() == 0) { 800 throw new CallStateException("no connections in call"); 801 } 802 803 if (call == mRingingCall) { 804 if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); 805 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 806 } else if (call == mForegroundCall) { 807 if (call.isDialingOrAlerting()) { 808 if (Phone.DEBUG_PHONE) { 809 log("(foregnd) hangup dialing or alerting..."); 810 } 811 hangup((CdmaConnection)(call.getConnections().get(0))); 812 } else { 813 hangupForegroundResumeBackground(); 814 } 815 } else if (call == mBackgroundCall) { 816 if (mRingingCall.isRinging()) { 817 if (Phone.DEBUG_PHONE) { 818 log("hangup all conns in background call"); 819 } 820 hangupAllConnections(call); 821 } else { 822 hangupWaitingOrBackground(); 823 } 824 } else { 825 throw new RuntimeException ("CdmaCall " + call + 826 "does not belong to CdmaCallTracker " + this); 827 } 828 829 call.onHangupLocal(); 830 mPhone.notifyPreciseCallStateChanged(); 831 } 832 833 /* package */ 834 void hangupWaitingOrBackground() { 835 if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); 836 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 837 } 838 839 /* package */ 840 void hangupForegroundResumeBackground() { 841 if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); 842 mCi.hangupForegroundResumeBackground(obtainCompleteMessage()); 843 } 844 845 void hangupConnectionByIndex(CdmaCall call, int index) 846 throws CallStateException { 847 int count = call.mConnections.size(); 848 for (int i = 0; i < count; i++) { 849 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i); 850 if (cn.getCDMAIndex() == index) { 851 mCi.hangupConnection(index, obtainCompleteMessage()); 852 return; 853 } 854 } 855 856 throw new CallStateException("no gsm index found"); 857 } 858 859 void hangupAllConnections(CdmaCall call) { 860 try { 861 int count = call.mConnections.size(); 862 for (int i = 0; i < count; i++) { 863 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i); 864 mCi.hangupConnection(cn.getCDMAIndex(), obtainCompleteMessage()); 865 } 866 } catch (CallStateException ex) { 867 Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); 868 } 869 } 870 871 /* package */ 872 CdmaConnection getConnectionByIndex(CdmaCall call, int index) 873 throws CallStateException { 874 int count = call.mConnections.size(); 875 for (int i = 0; i < count; i++) { 876 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i); 877 if (cn.getCDMAIndex() == index) { 878 return cn; 879 } 880 } 881 882 return null; 883 } 884 885 private void flashAndSetGenericTrue() { 886 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); 887 888 // Set generic to true because in CDMA it is not known what 889 // the status of the call is after a call waiting is answered, 890 // 3 way call merged or a switch between calls. 891 mForegroundCall.setGeneric(true); 892 mPhone.notifyPreciseCallStateChanged(); 893 } 894 895 private void handleRadioNotAvailable() { 896 // handlePollCalls will clear out its 897 // call list when it gets the CommandException 898 // error result from this 899 pollCallsWhenSafe(); 900 } 901 902 private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) { 903 if (mCallWaitingRegistrants != null) { 904 mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null)); 905 } 906 } 907 908 private void handleCallWaitingInfo (CdmaCallWaitingNotification cw) { 909 // Check how many connections in foregroundCall. 910 // If the connection in foregroundCall is more 911 // than one, then the connection information is 912 // not reliable anymore since it means either 913 // call waiting is connected or 3 way call is 914 // dialed before, so set generic. 915 if (mForegroundCall.mConnections.size() > 1 ) { 916 mForegroundCall.setGeneric(true); 917 } 918 919 // Create a new CdmaConnection which attaches itself to ringingCall. 920 mRingingCall.setGeneric(false); 921 new CdmaConnection(mPhone.getContext(), cw, this, mRingingCall); 922 updatePhoneState(); 923 924 // Finally notify application 925 notifyCallWaitingInfo(cw); 926 } 927 //****** Overridden from Handler 928 929 @Override 930 public void 931 handleMessage (Message msg) { 932 AsyncResult ar; 933 934 if (!mPhone.mIsTheCurrentActivePhone) { 935 Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone"); 936 return; 937 } 938 switch (msg.what) { 939 case EVENT_POLL_CALLS_RESULT:{ 940 Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received"); 941 ar = (AsyncResult)msg.obj; 942 943 if(msg == mLastRelevantPoll) { 944 if(DBG_POLL) log( 945 "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); 946 mNeedsPoll = false; 947 mLastRelevantPoll = null; 948 handlePollCalls((AsyncResult)msg.obj); 949 } 950 } 951 break; 952 953 case EVENT_OPERATION_COMPLETE: 954 operationComplete(); 955 break; 956 957 case EVENT_SWITCH_RESULT: 958 // In GSM call operationComplete() here which gets the 959 // current call list. But in CDMA there is no list so 960 // there is nothing to do. 961 break; 962 963 case EVENT_GET_LAST_CALL_FAIL_CAUSE: 964 int causeCode; 965 ar = (AsyncResult)msg.obj; 966 967 operationComplete(); 968 969 if (ar.exception != null) { 970 // An exception occurred...just treat the disconnect 971 // cause as "normal" 972 causeCode = CallFailCause.NORMAL_CLEARING; 973 Rlog.i(LOG_TAG, 974 "Exception during getLastCallFailCause, assuming normal disconnect"); 975 } else { 976 causeCode = ((int[])ar.result)[0]; 977 } 978 979 for (int i = 0, s = mDroppedDuringPoll.size() 980 ; i < s ; i++ 981 ) { 982 CdmaConnection conn = mDroppedDuringPoll.get(i); 983 984 conn.onRemoteDisconnect(causeCode); 985 } 986 987 updatePhoneState(); 988 989 mPhone.notifyPreciseCallStateChanged(); 990 mDroppedDuringPoll.clear(); 991 break; 992 993 case EVENT_REPOLL_AFTER_DELAY: 994 case EVENT_CALL_STATE_CHANGE: 995 pollCallsWhenSafe(); 996 break; 997 998 case EVENT_RADIO_AVAILABLE: 999 handleRadioAvailable(); 1000 break; 1001 1002 case EVENT_RADIO_NOT_AVAILABLE: 1003 handleRadioNotAvailable(); 1004 break; 1005 1006 case EVENT_EXIT_ECM_RESPONSE_CDMA: 1007 // no matter the result, we still do the same here 1008 if (mPendingCallInEcm) { 1009 mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage()); 1010 mPendingCallInEcm = false; 1011 } 1012 mPhone.unsetOnEcbModeExitResponse(this); 1013 break; 1014 1015 case EVENT_CALL_WAITING_INFO_CDMA: 1016 ar = (AsyncResult)msg.obj; 1017 if (ar.exception == null) { 1018 handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); 1019 Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); 1020 } 1021 break; 1022 1023 case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: 1024 ar = (AsyncResult)msg.obj; 1025 if (ar.exception == null) { 1026 // Assume 3 way call is connected 1027 mPendingMO.onConnectedInOrOut(); 1028 mPendingMO = null; 1029 } 1030 break; 1031 1032 case EVENT_THREE_WAY_DIAL_BLANK_FLASH: 1033 ar = (AsyncResult) msg.obj; 1034 if (ar.exception == null) { 1035 postDelayed( 1036 new Runnable() { 1037 public void run() { 1038 if (mPendingMO != null) { 1039 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 1040 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 1041 } 1042 } 1043 }, m3WayCallFlashDelay); 1044 } else { 1045 mPendingMO = null; 1046 Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call"); 1047 } 1048 break; 1049 1050 default:{ 1051 throw new RuntimeException("unexpected event not handled"); 1052 } 1053 } 1054 } 1055 1056 /** 1057 * Handle Ecm timer to be canceled or re-started 1058 */ 1059 private void handleEcmTimer(int action) { 1060 mPhone.handleTimerInEmergencyCallbackMode(action); 1061 switch(action) { 1062 case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break; 1063 case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break; 1064 default: 1065 Rlog.e(LOG_TAG, "handleEcmTimer, unsupported action " + action); 1066 } 1067 } 1068 1069 /** 1070 * Disable data call when emergency call is connected 1071 */ 1072 private void disableDataCallInEmergencyCall(String dialString) { 1073 if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) { 1074 if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall"); 1075 mIsInEmergencyCall = true; 1076 mPhone.mDcTracker.setInternalDataEnabled(false); 1077 } 1078 } 1079 1080 /** 1081 * Check and enable data call after an emergency call is dropped if it's 1082 * not in ECM 1083 */ 1084 private void checkAndEnableDataCallAfterEmergencyCallDropped() { 1085 if (mIsInEmergencyCall) { 1086 mIsInEmergencyCall = false; 1087 String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1088 if (Phone.DEBUG_PHONE) { 1089 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm); 1090 } 1091 if (inEcm.compareTo("false") == 0) { 1092 // Re-initiate data connection 1093 mPhone.mDcTracker.setInternalDataEnabled(true); 1094 } 1095 } 1096 } 1097 1098 /** 1099 * Check the MT call to see if it's a new ring or 1100 * a unknown connection. 1101 */ 1102 private Connection checkMtFindNewRinging(DriverCall dc, int i) { 1103 1104 Connection newRinging = null; 1105 1106 // it's a ringing call 1107 if (mConnections[i].getCall() == mRingingCall) { 1108 newRinging = mConnections[i]; 1109 if (Phone.DEBUG_PHONE) log("Notify new ring " + dc); 1110 } else { 1111 // Something strange happened: a call which is neither 1112 // a ringing call nor the one we created. It could be the 1113 // call collision result from RIL 1114 Rlog.e(LOG_TAG,"Phantom call appeared " + dc); 1115 // If it's a connected call, set the connect time so that 1116 // it's non-zero. It may not be accurate, but at least 1117 // it won't appear as a Missed Call. 1118 if (dc.state != DriverCall.State.ALERTING 1119 && dc.state != DriverCall.State.DIALING) { 1120 mConnections[i].onConnectedInOrOut(); 1121 if (dc.state == DriverCall.State.HOLDING) { 1122 // We've transitioned into HOLDING 1123 mConnections[i].onStartedHolding(); 1124 } 1125 } 1126 } 1127 return newRinging; 1128 } 1129 1130 /** 1131 * Check if current call is in emergency call 1132 * 1133 * @return true if it is in emergency call 1134 * false if it is not in emergency call 1135 */ 1136 boolean isInEmergencyCall() { 1137 return mIsInEmergencyCall; 1138 } 1139 1140 @Override 1141 protected void log(String msg) { 1142 Rlog.d(LOG_TAG, "[CdmaCallTracker] " + msg); 1143 } 1144 1145 @Override 1146 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1147 pw.println("GsmCallTracker extends:"); 1148 super.dump(fd, pw, args); 1149 pw.println("droppedDuringPoll: length=" + mConnections.length); 1150 for(int i=0; i < mConnections.length; i++) { 1151 pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]); 1152 } 1153 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1154 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1155 pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants); 1156 pw.println("droppedDuringPoll: size=" + mDroppedDuringPoll.size()); 1157 for(int i = 0; i < mDroppedDuringPoll.size(); i++) { 1158 pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i)); 1159 } 1160 pw.println(" mRingingCall=" + mRingingCall); 1161 pw.println(" mForegroundCall=" + mForegroundCall); 1162 pw.println(" mBackgroundCall=" + mBackgroundCall); 1163 pw.println(" mPendingMO=" + mPendingMO); 1164 pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1165 pw.println(" mPendingCallInEcm=" + mPendingCallInEcm); 1166 pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall); 1167 pw.println(" mPhone=" + mPhone); 1168 pw.println(" mDesiredMute=" + mDesiredMute); 1169 pw.println(" mPendingCallClirMode=" + mPendingCallClirMode); 1170 pw.println(" mState=" + mState); 1171 pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled); 1172 } 1173} 1174