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