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