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 17 package com.android.internal.telephony.cdma; 18 19 import android.os.AsyncResult; 20 import android.os.Handler; 21 import android.os.Message; 22 import android.os.Registrant; 23 import android.os.RegistrantList; 24 import android.telephony.DisconnectCause; 25 import android.telephony.PhoneNumberUtils; 26 import android.telephony.ServiceState; 27 import android.telephony.Rlog; 28 import android.os.SystemProperties; 29 import android.text.TextUtils; 30 31 import com.android.internal.telephony.CallStateException; 32 import com.android.internal.telephony.CallTracker; 33 import com.android.internal.telephony.CommandsInterface; 34 import com.android.internal.telephony.Connection; 35 import com.android.internal.telephony.DriverCall; 36 import com.android.internal.telephony.Phone; 37 import com.android.internal.telephony.PhoneBase; 38 import com.android.internal.telephony.PhoneConstants; 39 import com.android.internal.telephony.TelephonyProperties; 40 41 import java.io.FileDescriptor; 42 import java.io.PrintWriter; 43 import java.util.ArrayList; 44 import java.util.List; 45 46 47 /** 48 * {@hide} 49 */ 50 public 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 Connection newUnknown = null; 509 boolean hasNonHangupStateChanged = false; // Any change besides 510 // a dropped connection 511 boolean hasAnyCallDisconnected = false; 512 boolean needsPollDelay = false; 513 boolean unknownConnectionAppeared = false; 514 515 for (int i = 0, curDC = 0, dcSize = polledCalls.size() 516 ; i < mConnections.length; i++) { 517 CdmaConnection conn = mConnections[i]; 518 DriverCall dc = null; 519 520 // polledCall list is sparse 521 if (curDC < dcSize) { 522 dc = (DriverCall) polledCalls.get(curDC); 523 524 if (dc.index == i+1) { 525 curDC++; 526 } else { 527 dc = null; 528 } 529 } 530 531 if (DBG_POLL) log("poll: conn[i=" + i + "]=" + 532 conn+", dc=" + dc); 533 534 if (conn == null && dc != null) { 535 // Connection appeared in CLCC response that we don't know about 536 if (mPendingMO != null && mPendingMO.compareTo(dc)) { 537 538 if (DBG_POLL) log("poll: pendingMO=" + mPendingMO); 539 540 // It's our pending mobile originating call 541 mConnections[i] = mPendingMO; 542 mPendingMO.mIndex = i; 543 mPendingMO.update(dc); 544 mPendingMO = null; 545 546 // Someone has already asked to hangup this call 547 if (mHangupPendingMO) { 548 mHangupPendingMO = false; 549 // Re-start Ecm timer when an uncompleted emergency call ends 550 if (mIsEcmTimerCanceled) { 551 handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER); 552 } 553 554 try { 555 if (Phone.DEBUG_PHONE) log( 556 "poll: hangupPendingMO, hangup conn " + i); 557 hangup(mConnections[i]); 558 } catch (CallStateException ex) { 559 Rlog.e(LOG_TAG, "unexpected error on hangup"); 560 } 561 562 // Do not continue processing this poll 563 // Wait for hangup and repoll 564 return; 565 } 566 } else { 567 if (Phone.DEBUG_PHONE) { 568 log("pendingMo=" + mPendingMO + ", dc=" + dc); 569 } 570 mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i); 571 572 if (mHandoverConnection != null) { 573 // Single Radio Voice Call Continuity (SRVCC) completed 574 mPhone.migrateFrom((PhoneBase) mPhone.getImsPhone()); 575 mConnections[i].migrateFrom(mHandoverConnection); 576 mPhone.notifyHandoverStateChanged(mConnections[i]); 577 mHandoverConnection = null; 578 } else { 579 // find if the MT call is a new ring or unknown connection 580 newRinging = checkMtFindNewRinging(dc,i); 581 if (newRinging == null) { 582 unknownConnectionAppeared = true; 583 } 584 } 585 checkAndEnableDataCallAfterEmergencyCallDropped(); 586 } 587 hasNonHangupStateChanged = true; 588 } else if (conn != null && dc == null) { 589 // This case means the RIL has no more active call anymore and 590 // we need to clean up the foregroundCall and ringingCall. 591 // Loop through foreground call connections as 592 // it contains the known logical connections. 593 int count = mForegroundCall.mConnections.size(); 594 for (int n = 0; n < count; n++) { 595 if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll"); 596 CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n); 597 mDroppedDuringPoll.add(cn); 598 } 599 count = mRingingCall.mConnections.size(); 600 // Loop through ringing call connections as 601 // it may contain the known logical connections. 602 for (int n = 0; n < count; n++) { 603 if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll"); 604 CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n); 605 mDroppedDuringPoll.add(cn); 606 } 607 mForegroundCall.setGeneric(false); 608 mRingingCall.setGeneric(false); 609 610 // Re-start Ecm timer when the connected emergency call ends 611 if (mIsEcmTimerCanceled) { 612 handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER); 613 } 614 // If emergency call is not going through while dialing 615 checkAndEnableDataCallAfterEmergencyCallDropped(); 616 617 // Dropped connections are removed from the CallTracker 618 // list but kept in the Call list 619 mConnections[i] = null; 620 } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ 621 // Call collision case 622 if (conn.isIncoming() != dc.isMT) { 623 if (dc.isMT == true){ 624 // Mt call takes precedence than Mo,drops Mo 625 mDroppedDuringPoll.add(conn); 626 // find if the MT call is a new ring or unknown connection 627 newRinging = checkMtFindNewRinging(dc,i); 628 if (newRinging == null) { 629 unknownConnectionAppeared = true; 630 newUnknown = conn; 631 } 632 checkAndEnableDataCallAfterEmergencyCallDropped(); 633 } else { 634 // Call info stored in conn is not consistent with the call info from dc. 635 // We should follow the rule of MT calls taking precedence over MO calls 636 // when there is conflict, so here we drop the call info from dc and 637 // continue to use the call info from conn, and only take a log. 638 Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc); 639 } 640 } else { 641 boolean changed; 642 changed = conn.update(dc); 643 hasNonHangupStateChanged = hasNonHangupStateChanged || changed; 644 } 645 } 646 647 if (REPEAT_POLLING) { 648 if (dc != null) { 649 // FIXME with RIL, we should not need this anymore 650 if ((dc.state == DriverCall.State.DIALING 651 /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) 652 || (dc.state == DriverCall.State.ALERTING 653 /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) 654 || (dc.state == DriverCall.State.INCOMING 655 /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) 656 || (dc.state == DriverCall.State.WAITING 657 /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/) 658 ) { 659 // Sometimes there's no unsolicited notification 660 // for state transitions 661 needsPollDelay = true; 662 } 663 } 664 } 665 } 666 667 // This is the first poll after an ATD. 668 // We expect the pending call to appear in the list 669 // If it does not, we land here 670 if (mPendingMO != null) { 671 Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:" 672 + mForegroundCall.getState()); 673 674 mDroppedDuringPoll.add(mPendingMO); 675 mPendingMO = null; 676 mHangupPendingMO = false; 677 if( mPendingCallInEcm) { 678 mPendingCallInEcm = false; 679 } 680 } 681 682 if (newRinging != null) { 683 mPhone.notifyNewRingingConnection(newRinging); 684 } 685 686 // clear the "local hangup" and "missed/rejected call" 687 // cases from the "dropped during poll" list 688 // These cases need no "last call fail" reason 689 for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) { 690 CdmaConnection conn = mDroppedDuringPoll.get(i); 691 692 if (conn.isIncoming() && conn.getConnectTime() == 0) { 693 // Missed or rejected call 694 int cause; 695 if (conn.mCause == DisconnectCause.LOCAL) { 696 cause = DisconnectCause.INCOMING_REJECTED; 697 } else { 698 cause = DisconnectCause.INCOMING_MISSED; 699 } 700 701 if (Phone.DEBUG_PHONE) { 702 log("missed/rejected call, conn.cause=" + conn.mCause); 703 log("setting cause to " + cause); 704 } 705 mDroppedDuringPoll.remove(i); 706 hasAnyCallDisconnected |= conn.onDisconnect(cause); 707 } else if (conn.mCause == DisconnectCause.LOCAL 708 || conn.mCause == DisconnectCause.INVALID_NUMBER) { 709 mDroppedDuringPoll.remove(i); 710 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); 711 } 712 } 713 714 // Any non-local disconnects: determine cause 715 if (mDroppedDuringPoll.size() > 0) { 716 mCi.getLastCallFailCause( 717 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); 718 } 719 720 if (needsPollDelay) { 721 pollCallsAfterDelay(); 722 } 723 724 // Cases when we can no longer keep disconnected Connection's 725 // with their previous calls 726 // 1) the phone has started to ring 727 // 2) A Call/Connection object has changed state... 728 // we may have switched or held or answered (but not hung up) 729 if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) { 730 internalClearDisconnected(); 731 } 732 733 updatePhoneState(); 734 735 if (unknownConnectionAppeared) { 736 mPhone.notifyUnknownConnection(newUnknown); 737 } 738 739 if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { 740 mPhone.notifyPreciseCallStateChanged(); 741 } 742 743 //dumpState(); 744 } 745 746 //***** Called from CdmaConnection 747 /*package*/ void 748 hangup (CdmaConnection conn) throws CallStateException { 749 if (conn.mOwner != this) { 750 throw new CallStateException ("CdmaConnection " + conn 751 + "does not belong to CdmaCallTracker " + this); 752 } 753 754 if (conn == mPendingMO) { 755 // We're hanging up an outgoing call that doesn't have it's 756 // GSM index assigned yet 757 758 if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); 759 mHangupPendingMO = true; 760 } else if ((conn.getCall() == mRingingCall) 761 && (mRingingCall.getState() == CdmaCall.State.WAITING)) { 762 // Handle call waiting hang up case. 763 // 764 // The ringingCall state will change to IDLE in CdmaCall.detach 765 // if the ringing call connection size is 0. We don't specifically 766 // set the ringing call state to IDLE here to avoid a race condition 767 // where a new call waiting could get a hang up from an old call 768 // waiting ringingCall. 769 // 770 // PhoneApp does the call log itself since only PhoneApp knows 771 // the hangup reason is user ignoring or timing out. So conn.onDisconnect() 772 // is not called here. Instead, conn.onLocalDisconnect() is called. 773 conn.onLocalDisconnect(); 774 updatePhoneState(); 775 mPhone.notifyPreciseCallStateChanged(); 776 return; 777 } else { 778 try { 779 mCi.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage()); 780 } catch (CallStateException ex) { 781 // Ignore "connection not found" 782 // Call may have hung up already 783 Rlog.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection " 784 + conn); 785 } 786 } 787 788 conn.onHangupLocal(); 789 } 790 791 /*package*/ void 792 separate (CdmaConnection conn) throws CallStateException { 793 if (conn.mOwner != this) { 794 throw new CallStateException ("CdmaConnection " + conn 795 + "does not belong to CdmaCallTracker " + this); 796 } 797 try { 798 mCi.separateConnection (conn.getCDMAIndex(), 799 obtainCompleteMessage(EVENT_SEPARATE_RESULT)); 800 } catch (CallStateException ex) { 801 // Ignore "connection not found" 802 // Call may have hung up already 803 Rlog.w(LOG_TAG,"CdmaCallTracker WARN: separate() on absent connection " 804 + conn); 805 } 806 } 807 808 //***** Called from CDMAPhone 809 810 /*package*/ void 811 setMute(boolean mute) { 812 mDesiredMute = mute; 813 mCi.setMute(mDesiredMute, null); 814 } 815 816 /*package*/ boolean 817 getMute() { 818 return mDesiredMute; 819 } 820 821 822 //***** Called from CdmaCall 823 824 /* package */ void 825 hangup (CdmaCall call) throws CallStateException { 826 if (call.getConnections().size() == 0) { 827 throw new CallStateException("no connections in call"); 828 } 829 830 if (call == mRingingCall) { 831 if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); 832 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 833 } else if (call == mForegroundCall) { 834 if (call.isDialingOrAlerting()) { 835 if (Phone.DEBUG_PHONE) { 836 log("(foregnd) hangup dialing or alerting..."); 837 } 838 hangup((CdmaConnection)(call.getConnections().get(0))); 839 } else { 840 hangupForegroundResumeBackground(); 841 } 842 } else if (call == mBackgroundCall) { 843 if (mRingingCall.isRinging()) { 844 if (Phone.DEBUG_PHONE) { 845 log("hangup all conns in background call"); 846 } 847 hangupAllConnections(call); 848 } else { 849 hangupWaitingOrBackground(); 850 } 851 } else { 852 throw new RuntimeException ("CdmaCall " + call + 853 "does not belong to CdmaCallTracker " + this); 854 } 855 856 call.onHangupLocal(); 857 mPhone.notifyPreciseCallStateChanged(); 858 } 859 860 /* package */ 861 void hangupWaitingOrBackground() { 862 if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); 863 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 864 } 865 866 /* package */ 867 void hangupForegroundResumeBackground() { 868 if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); 869 mCi.hangupForegroundResumeBackground(obtainCompleteMessage()); 870 } 871 872 void hangupConnectionByIndex(CdmaCall call, int index) 873 throws CallStateException { 874 int count = call.mConnections.size(); 875 for (int i = 0; i < count; i++) { 876 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i); 877 if (cn.getCDMAIndex() == index) { 878 mCi.hangupConnection(index, obtainCompleteMessage()); 879 return; 880 } 881 } 882 883 throw new CallStateException("no gsm index found"); 884 } 885 886 void hangupAllConnections(CdmaCall call) { 887 try { 888 int count = call.mConnections.size(); 889 for (int i = 0; i < count; i++) { 890 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i); 891 mCi.hangupConnection(cn.getCDMAIndex(), obtainCompleteMessage()); 892 } 893 } catch (CallStateException ex) { 894 Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); 895 } 896 } 897 898 /* package */ 899 CdmaConnection getConnectionByIndex(CdmaCall call, int index) 900 throws CallStateException { 901 int count = call.mConnections.size(); 902 for (int i = 0; i < count; i++) { 903 CdmaConnection cn = (CdmaConnection)call.mConnections.get(i); 904 if (cn.getCDMAIndex() == index) { 905 return cn; 906 } 907 } 908 909 return null; 910 } 911 912 private void flashAndSetGenericTrue() { 913 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); 914 915 // Set generic to true because in CDMA it is not known what 916 // the status of the call is after a call waiting is answered, 917 // 3 way call merged or a switch between calls. 918 mForegroundCall.setGeneric(true); 919 mPhone.notifyPreciseCallStateChanged(); 920 } 921 922 private void handleRadioNotAvailable() { 923 // handlePollCalls will clear out its 924 // call list when it gets the CommandException 925 // error result from this 926 pollCallsWhenSafe(); 927 } 928 929 private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) { 930 if (mCallWaitingRegistrants != null) { 931 mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null)); 932 } 933 } 934 935 private void handleCallWaitingInfo (CdmaCallWaitingNotification cw) { 936 // Check how many connections in foregroundCall. 937 // If the connection in foregroundCall is more 938 // than one, then the connection information is 939 // not reliable anymore since it means either 940 // call waiting is connected or 3 way call is 941 // dialed before, so set generic. 942 if (mForegroundCall.mConnections.size() > 1 ) { 943 mForegroundCall.setGeneric(true); 944 } 945 946 // Create a new CdmaConnection which attaches itself to ringingCall. 947 mRingingCall.setGeneric(false); 948 new CdmaConnection(mPhone.getContext(), cw, this, mRingingCall); 949 updatePhoneState(); 950 951 // Finally notify application 952 notifyCallWaitingInfo(cw); 953 } 954 //****** Overridden from Handler 955 956 @Override 957 public void 958 handleMessage (Message msg) { 959 AsyncResult ar; 960 961 if (!mPhone.mIsTheCurrentActivePhone) { 962 Rlog.w(LOG_TAG, "Ignoring events received on inactive CdmaPhone"); 963 return; 964 } 965 switch (msg.what) { 966 case EVENT_POLL_CALLS_RESULT:{ 967 Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received"); 968 ar = (AsyncResult)msg.obj; 969 970 if(msg == mLastRelevantPoll) { 971 if(DBG_POLL) log( 972 "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); 973 mNeedsPoll = false; 974 mLastRelevantPoll = null; 975 handlePollCalls((AsyncResult)msg.obj); 976 } 977 } 978 break; 979 980 case EVENT_OPERATION_COMPLETE: 981 operationComplete(); 982 break; 983 984 case EVENT_SWITCH_RESULT: 985 // In GSM call operationComplete() here which gets the 986 // current call list. But in CDMA there is no list so 987 // there is nothing to do. 988 break; 989 990 case EVENT_GET_LAST_CALL_FAIL_CAUSE: 991 int causeCode; 992 ar = (AsyncResult)msg.obj; 993 994 operationComplete(); 995 996 if (ar.exception != null) { 997 // An exception occurred...just treat the disconnect 998 // cause as "normal" 999 causeCode = CallFailCause.NORMAL_CLEARING; 1000 Rlog.i(LOG_TAG, 1001 "Exception during getLastCallFailCause, assuming normal disconnect"); 1002 } else { 1003 causeCode = ((int[])ar.result)[0]; 1004 } 1005 1006 for (int i = 0, s = mDroppedDuringPoll.size() 1007 ; i < s ; i++ 1008 ) { 1009 CdmaConnection conn = mDroppedDuringPoll.get(i); 1010 1011 conn.onRemoteDisconnect(causeCode); 1012 } 1013 1014 updatePhoneState(); 1015 1016 mPhone.notifyPreciseCallStateChanged(); 1017 mDroppedDuringPoll.clear(); 1018 break; 1019 1020 case EVENT_REPOLL_AFTER_DELAY: 1021 case EVENT_CALL_STATE_CHANGE: 1022 pollCallsWhenSafe(); 1023 break; 1024 1025 case EVENT_RADIO_AVAILABLE: 1026 handleRadioAvailable(); 1027 break; 1028 1029 case EVENT_RADIO_NOT_AVAILABLE: 1030 handleRadioNotAvailable(); 1031 break; 1032 1033 case EVENT_EXIT_ECM_RESPONSE_CDMA: 1034 // no matter the result, we still do the same here 1035 if (mPendingCallInEcm) { 1036 mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage()); 1037 mPendingCallInEcm = false; 1038 } 1039 mPhone.unsetOnEcbModeExitResponse(this); 1040 break; 1041 1042 case EVENT_CALL_WAITING_INFO_CDMA: 1043 ar = (AsyncResult)msg.obj; 1044 if (ar.exception == null) { 1045 handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); 1046 Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); 1047 } 1048 break; 1049 1050 case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: 1051 ar = (AsyncResult)msg.obj; 1052 if (ar.exception == null) { 1053 // Assume 3 way call is connected 1054 mPendingMO.onConnectedInOrOut(); 1055 mPendingMO = null; 1056 } 1057 break; 1058 1059 case EVENT_THREE_WAY_DIAL_BLANK_FLASH: 1060 ar = (AsyncResult) msg.obj; 1061 if (ar.exception == null) { 1062 postDelayed( 1063 new Runnable() { 1064 public void run() { 1065 if (mPendingMO != null) { 1066 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 1067 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 1068 } 1069 } 1070 }, m3WayCallFlashDelay); 1071 } else { 1072 mPendingMO = null; 1073 Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call"); 1074 } 1075 break; 1076 1077 default:{ 1078 throw new RuntimeException("unexpected event not handled"); 1079 } 1080 } 1081 } 1082 1083 /** 1084 * Handle Ecm timer to be canceled or re-started 1085 */ 1086 private void handleEcmTimer(int action) { 1087 mPhone.handleTimerInEmergencyCallbackMode(action); 1088 switch(action) { 1089 case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break; 1090 case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break; 1091 default: 1092 Rlog.e(LOG_TAG, "handleEcmTimer, unsupported action " + action); 1093 } 1094 } 1095 1096 /** 1097 * Disable data call when emergency call is connected 1098 */ 1099 private void disableDataCallInEmergencyCall(String dialString) { 1100 if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) { 1101 if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall"); 1102 mIsInEmergencyCall = true; 1103 mPhone.mDcTracker.setInternalDataEnabled(false); 1104 } 1105 } 1106 1107 /** 1108 * Check and enable data call after an emergency call is dropped if it's 1109 * not in ECM 1110 */ 1111 private void checkAndEnableDataCallAfterEmergencyCallDropped() { 1112 if (mIsInEmergencyCall) { 1113 mIsInEmergencyCall = false; 1114 String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1115 if (Phone.DEBUG_PHONE) { 1116 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm); 1117 } 1118 if (inEcm.compareTo("false") == 0) { 1119 // Re-initiate data connection 1120 mPhone.mDcTracker.setInternalDataEnabled(true); 1121 } 1122 } 1123 } 1124 1125 /** 1126 * Check the MT call to see if it's a new ring or 1127 * a unknown connection. 1128 */ 1129 private Connection checkMtFindNewRinging(DriverCall dc, int i) { 1130 1131 Connection newRinging = null; 1132 1133 // it's a ringing call 1134 if (mConnections[i].getCall() == mRingingCall) { 1135 newRinging = mConnections[i]; 1136 if (Phone.DEBUG_PHONE) log("Notify new ring " + dc); 1137 } else { 1138 // Something strange happened: a call which is neither 1139 // a ringing call nor the one we created. It could be the 1140 // call collision result from RIL 1141 Rlog.e(LOG_TAG,"Phantom call appeared " + dc); 1142 // If it's a connected call, set the connect time so that 1143 // it's non-zero. It may not be accurate, but at least 1144 // it won't appear as a Missed Call. 1145 if (dc.state != DriverCall.State.ALERTING 1146 && dc.state != DriverCall.State.DIALING) { 1147 mConnections[i].onConnectedInOrOut(); 1148 if (dc.state == DriverCall.State.HOLDING) { 1149 // We've transitioned into HOLDING 1150 mConnections[i].onStartedHolding(); 1151 } 1152 } 1153 } 1154 return newRinging; 1155 } 1156 1157 /** 1158 * Check if current call is in emergency call 1159 * 1160 * @return true if it is in emergency call 1161 * false if it is not in emergency call 1162 */ 1163 boolean isInEmergencyCall() { 1164 return mIsInEmergencyCall; 1165 } 1166 1167 @Override 1168 protected void log(String msg) { 1169 Rlog.d(LOG_TAG, "[CdmaCallTracker] " + msg); 1170 } 1171 1172 @Override 1173 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1174 pw.println("GsmCallTracker extends:"); 1175 super.dump(fd, pw, args); 1176 pw.println("droppedDuringPoll: length=" + mConnections.length); 1177 for(int i=0; i < mConnections.length; i++) { 1178 pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]); 1179 } 1180 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1181 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1182 pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants); 1183 pw.println("droppedDuringPoll: size=" + mDroppedDuringPoll.size()); 1184 for(int i = 0; i < mDroppedDuringPoll.size(); i++) { 1185 pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i)); 1186 } 1187 pw.println(" mRingingCall=" + mRingingCall); 1188 pw.println(" mForegroundCall=" + mForegroundCall); 1189 pw.println(" mBackgroundCall=" + mBackgroundCall); 1190 pw.println(" mPendingMO=" + mPendingMO); 1191 pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1192 pw.println(" mPendingCallInEcm=" + mPendingCallInEcm); 1193 pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall); 1194 pw.println(" mPhone=" + mPhone); 1195 pw.println(" mDesiredMute=" + mDesiredMute); 1196 pw.println(" mPendingCallClirMode=" + mPendingCallClirMode); 1197 pw.println(" mState=" + mState); 1198 pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled); 1199 } 1200 } 1201