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