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.gsm; 18 19import android.os.AsyncResult; 20import android.os.Handler; 21import android.os.Message; 22import android.os.Registrant; 23import android.os.RegistrantList; 24import android.os.SystemProperties; 25import android.telephony.DisconnectCause; 26import android.telephony.PhoneNumberUtils; 27import android.telephony.ServiceState; 28import android.telephony.TelephonyManager; 29import android.telephony.gsm.GsmCellLocation; 30import android.util.EventLog; 31import android.telephony.Rlog; 32 33import com.android.internal.telephony.Call; 34import com.android.internal.telephony.CallStateException; 35import com.android.internal.telephony.CallTracker; 36import com.android.internal.telephony.CommandsInterface; 37import com.android.internal.telephony.Connection; 38import com.android.internal.telephony.DriverCall; 39import com.android.internal.telephony.EventLogTags; 40import com.android.internal.telephony.Phone; 41import com.android.internal.telephony.PhoneBase; 42import com.android.internal.telephony.PhoneConstants; 43import com.android.internal.telephony.TelephonyProperties; 44import com.android.internal.telephony.UUSInfo; 45import com.android.internal.telephony.gsm.CallFailCause; 46import com.android.internal.telephony.gsm.GSMPhone; 47import com.android.internal.telephony.gsm.GsmCall; 48import com.android.internal.telephony.gsm.GsmConnection; 49import com.android.internal.telephony.imsphone.ImsPhone; 50import com.android.internal.telephony.imsphone.ImsPhoneConnection; 51 52import java.io.FileDescriptor; 53import java.io.PrintWriter; 54import java.util.List; 55import java.util.ArrayList; 56 57/** 58 * {@hide} 59 */ 60public final class GsmCallTracker extends CallTracker { 61 static final String LOG_TAG = "GsmCallTracker"; 62 private static final boolean REPEAT_POLLING = false; 63 64 private static final boolean DBG_POLL = false; 65 66 //***** Constants 67 68 static final int MAX_CONNECTIONS = 7; // only 7 connections allowed in GSM 69 static final int MAX_CONNECTIONS_PER_CALL = 5; // only 5 connections allowed per call 70 71 //***** Instance Variables 72 GsmConnection mConnections[] = new GsmConnection[MAX_CONNECTIONS]; 73 RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 74 RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 75 76 77 // connections dropped during last poll 78 ArrayList<GsmConnection> mDroppedDuringPoll 79 = new ArrayList<GsmConnection>(MAX_CONNECTIONS); 80 81 GsmCall mRingingCall = new GsmCall(this); 82 // A call that is ringing or (call) waiting 83 GsmCall mForegroundCall = new GsmCall(this); 84 GsmCall mBackgroundCall = new GsmCall(this); 85 86 GsmConnection mPendingMO; 87 boolean mHangupPendingMO; 88 89 GSMPhone mPhone; 90 91 boolean mDesiredMute = false; // false = mute off 92 93 PhoneConstants.State mState = PhoneConstants.State.IDLE; 94 95 Call.SrvccState mSrvccState = Call.SrvccState.NONE; 96 97 //***** Events 98 99 100 //***** Constructors 101 102 GsmCallTracker (GSMPhone phone) { 103 this.mPhone = phone; 104 mCi = phone.mCi; 105 106 mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); 107 108 mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null); 109 mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); 110 } 111 112 public void dispose() { 113 Rlog.d(LOG_TAG, "GsmCallTracker dispose"); 114 //Unregister for all events 115 mCi.unregisterForCallStateChanged(this); 116 mCi.unregisterForOn(this); 117 mCi.unregisterForNotAvailable(this); 118 119 120 clearDisconnected(); 121 } 122 123 @Override 124 protected void finalize() { 125 Rlog.d(LOG_TAG, "GsmCallTracker 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 } 136 137 @Override 138 public void unregisterForVoiceCallStarted(Handler h) { 139 mVoiceCallStartedRegistrants.remove(h); 140 } 141 142 @Override 143 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 144 Registrant r = new Registrant(h, what, obj); 145 mVoiceCallEndedRegistrants.add(r); 146 } 147 148 @Override 149 public void unregisterForVoiceCallEnded(Handler h) { 150 mVoiceCallEndedRegistrants.remove(h); 151 } 152 153 private void 154 fakeHoldForegroundBeforeDial() { 155 List<Connection> connCopy; 156 157 // We need to make a copy here, since fakeHoldBeforeDial() 158 // modifies the lists, and we don't want to reverse the order 159 connCopy = (List<Connection>) mForegroundCall.mConnections.clone(); 160 161 for (int i = 0, s = connCopy.size() ; i < s ; i++) { 162 GsmConnection conn = (GsmConnection)connCopy.get(i); 163 164 conn.fakeHoldBeforeDial(); 165 } 166 } 167 168 /** 169 * clirMode is one of the CLIR_ constants 170 */ 171 synchronized Connection 172 dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException { 173 // note that this triggers call state changed notif 174 clearDisconnected(); 175 176 if (!canDial()) { 177 throw new CallStateException("cannot dial in current state"); 178 } 179 180 String origNumber = dialString; 181 dialString = convertNumberIfNecessary(mPhone, dialString); 182 183 // The new call must be assigned to the foreground call. 184 // That call must be idle, so place anything that's 185 // there on hold 186 if (mForegroundCall.getState() == GsmCall.State.ACTIVE) { 187 // this will probably be done by the radio anyway 188 // but the dial might fail before this happens 189 // and we need to make sure the foreground call is clear 190 // for the newly dialed connection 191 switchWaitingOrHoldingAndActive(); 192 193 // Fake local state so that 194 // a) foregroundCall is empty for the newly dialed connection 195 // b) hasNonHangupStateChanged remains false in the 196 // next poll, so that we don't clear a failed dialing call 197 fakeHoldForegroundBeforeDial(); 198 } 199 200 if (mForegroundCall.getState() != GsmCall.State.IDLE) { 201 //we should have failed in !canDial() above before we get here 202 throw new CallStateException("cannot dial in current state"); 203 } 204 205 mPendingMO = new GsmConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString), 206 this, mForegroundCall); 207 mHangupPendingMO = false; 208 209 if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0 210 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 211 ) { 212 // Phone number is invalid 213 mPendingMO.mCause = DisconnectCause.INVALID_NUMBER; 214 215 // handlePollCalls() will notice this call not present 216 // and will mark it as dropped. 217 pollCallsWhenSafe(); 218 } else { 219 // Always unmute when initiating a new call 220 setMute(false); 221 222 mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage()); 223 } 224 225 if (mNumberConverted) { 226 mPendingMO.setConverted(origNumber); 227 mNumberConverted = false; 228 } 229 230 updatePhoneState(); 231 mPhone.notifyPreciseCallStateChanged(); 232 233 return mPendingMO; 234 } 235 236 Connection 237 dial(String dialString) throws CallStateException { 238 return dial(dialString, CommandsInterface.CLIR_DEFAULT, null); 239 } 240 241 Connection 242 dial(String dialString, UUSInfo uusInfo) throws CallStateException { 243 return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo); 244 } 245 246 Connection 247 dial(String dialString, int clirMode) throws CallStateException { 248 return dial(dialString, clirMode, null); 249 } 250 251 void 252 acceptCall () throws CallStateException { 253 // FIXME if SWITCH fails, should retry with ANSWER 254 // in case the active/holding call disappeared and this 255 // is no longer call waiting 256 257 if (mRingingCall.getState() == GsmCall.State.INCOMING) { 258 Rlog.i("phone", "acceptCall: incoming..."); 259 // Always unmute when answering a new call 260 setMute(false); 261 mCi.acceptCall(obtainCompleteMessage()); 262 } else if (mRingingCall.getState() == GsmCall.State.WAITING) { 263 setMute(false); 264 switchWaitingOrHoldingAndActive(); 265 } else { 266 throw new CallStateException("phone not ringing"); 267 } 268 } 269 270 void 271 rejectCall () throws CallStateException { 272 // AT+CHLD=0 means "release held or UDUB" 273 // so if the phone isn't ringing, this could hang up held 274 if (mRingingCall.getState().isRinging()) { 275 mCi.rejectCall(obtainCompleteMessage()); 276 } else { 277 throw new CallStateException("phone not ringing"); 278 } 279 } 280 281 void 282 switchWaitingOrHoldingAndActive() throws CallStateException { 283 // Should we bother with this check? 284 if (mRingingCall.getState() == GsmCall.State.INCOMING) { 285 throw new CallStateException("cannot be in the incoming state"); 286 } else { 287 mCi.switchWaitingOrHoldingAndActive( 288 obtainCompleteMessage(EVENT_SWITCH_RESULT)); 289 } 290 } 291 292 void 293 conference() { 294 mCi.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT)); 295 } 296 297 void 298 explicitCallTransfer() { 299 mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); 300 } 301 302 void 303 clearDisconnected() { 304 internalClearDisconnected(); 305 306 updatePhoneState(); 307 mPhone.notifyPreciseCallStateChanged(); 308 } 309 310 boolean 311 canConference() { 312 return mForegroundCall.getState() == GsmCall.State.ACTIVE 313 && mBackgroundCall.getState() == GsmCall.State.HOLDING 314 && !mBackgroundCall.isFull() 315 && !mForegroundCall.isFull(); 316 } 317 318 boolean 319 canDial() { 320 boolean ret; 321 int serviceState = mPhone.getServiceState().getState(); 322 String disableCall = SystemProperties.get( 323 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 324 325 ret = (serviceState != ServiceState.STATE_POWER_OFF) 326 && mPendingMO == null 327 && !mRingingCall.isRinging() 328 && !disableCall.equals("true") 329 && (!mForegroundCall.getState().isAlive() 330 || !mBackgroundCall.getState().isAlive()); 331 332 return ret; 333 } 334 335 boolean 336 canTransfer() { 337 return (mForegroundCall.getState() == GsmCall.State.ACTIVE 338 || mForegroundCall.getState() == GsmCall.State.ALERTING 339 || mForegroundCall.getState() == GsmCall.State.DIALING) 340 && mBackgroundCall.getState() == GsmCall.State.HOLDING; 341 } 342 343 //***** Private Instance Methods 344 345 private void 346 internalClearDisconnected() { 347 mRingingCall.clearDisconnected(); 348 mForegroundCall.clearDisconnected(); 349 mBackgroundCall.clearDisconnected(); 350 } 351 352 /** 353 * Obtain a message to use for signalling "invoke getCurrentCalls() when 354 * this operation and all other pending operations are complete 355 */ 356 private Message 357 obtainCompleteMessage() { 358 return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); 359 } 360 361 /** 362 * Obtain a message to use for signalling "invoke getCurrentCalls() when 363 * this operation and all other pending operations are complete 364 */ 365 private Message 366 obtainCompleteMessage(int what) { 367 mPendingOperations++; 368 mLastRelevantPoll = null; 369 mNeedsPoll = true; 370 371 if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + 372 mPendingOperations + ", needsPoll=" + mNeedsPoll); 373 374 return obtainMessage(what); 375 } 376 377 private void 378 operationComplete() { 379 mPendingOperations--; 380 381 if (DBG_POLL) log("operationComplete: pendingOperations=" + 382 mPendingOperations + ", needsPoll=" + mNeedsPoll); 383 384 if (mPendingOperations == 0 && mNeedsPoll) { 385 mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); 386 mCi.getCurrentCalls(mLastRelevantPoll); 387 } else if (mPendingOperations < 0) { 388 // this should never happen 389 Rlog.e(LOG_TAG,"GsmCallTracker.pendingOperations < 0"); 390 mPendingOperations = 0; 391 } 392 } 393 394 private void 395 updatePhoneState() { 396 PhoneConstants.State oldState = mState; 397 if (mRingingCall.isRinging()) { 398 mState = PhoneConstants.State.RINGING; 399 } else if (mPendingMO != null || 400 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 401 mState = PhoneConstants.State.OFFHOOK; 402 } else { 403 ImsPhone imsPhone = (ImsPhone)mPhone.getImsPhone(); 404 if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){ 405 imsPhone.callEndCleanupHandOverCallIfAny(); 406 } 407 mState = PhoneConstants.State.IDLE; 408 } 409 410 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 411 mVoiceCallEndedRegistrants.notifyRegistrants( 412 new AsyncResult(null, null, null)); 413 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 414 mVoiceCallStartedRegistrants.notifyRegistrants ( 415 new AsyncResult(null, null, null)); 416 } 417 418 if (mState != oldState) { 419 mPhone.notifyPhoneStateChanged(); 420 } 421 } 422 423 @Override 424 protected synchronized void 425 handlePollCalls(AsyncResult ar) { 426 List polledCalls; 427 428 if (ar.exception == null) { 429 polledCalls = (List)ar.result; 430 } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { 431 // just a dummy empty ArrayList to cause the loop 432 // to hang up all the calls 433 polledCalls = new ArrayList(); 434 } else { 435 // Radio probably wasn't ready--try again in a bit 436 // But don't keep polling if the channel is closed 437 pollCallsAfterDelay(); 438 return; 439 } 440 441 Connection newRinging = null; //or waiting 442 Connection newUnknown = null; 443 boolean hasNonHangupStateChanged = false; // Any change besides 444 // a dropped connection 445 boolean hasAnyCallDisconnected = false; 446 boolean needsPollDelay = false; 447 boolean unknownConnectionAppeared = false; 448 449 for (int i = 0, curDC = 0, dcSize = polledCalls.size() 450 ; i < mConnections.length; i++) { 451 GsmConnection conn = mConnections[i]; 452 DriverCall dc = null; 453 454 // polledCall list is sparse 455 if (curDC < dcSize) { 456 dc = (DriverCall) polledCalls.get(curDC); 457 458 if (dc.index == i+1) { 459 curDC++; 460 } else { 461 dc = null; 462 } 463 } 464 465 if (DBG_POLL) log("poll: conn[i=" + i + "]=" + 466 conn+", dc=" + dc); 467 468 if (conn == null && dc != null) { 469 // Connection appeared in CLCC response that we don't know about 470 if (mPendingMO != null && mPendingMO.compareTo(dc)) { 471 472 if (DBG_POLL) log("poll: pendingMO=" + mPendingMO); 473 474 // It's our pending mobile originating call 475 mConnections[i] = mPendingMO; 476 mPendingMO.mIndex = i; 477 mPendingMO.update(dc); 478 mPendingMO = null; 479 480 // Someone has already asked to hangup this call 481 if (mHangupPendingMO) { 482 mHangupPendingMO = false; 483 try { 484 if (Phone.DEBUG_PHONE) log( 485 "poll: hangupPendingMO, hangup conn " + i); 486 hangup(mConnections[i]); 487 } catch (CallStateException ex) { 488 Rlog.e(LOG_TAG, "unexpected error on hangup"); 489 } 490 491 // Do not continue processing this poll 492 // Wait for hangup and repoll 493 return; 494 } 495 } else { 496 mConnections[i] = new GsmConnection(mPhone.getContext(), dc, this, i); 497 498 Connection hoConnection = getHoConnection(dc); 499 if (hoConnection != null) { 500 // Single Radio Voice Call Continuity (SRVCC) completed 501 mConnections[i].migrateFrom(hoConnection); 502 if (!hoConnection.isMultiparty()) { 503 // Remove only if it is not multiparty 504 mHandoverConnections.remove(hoConnection); 505 } 506 mPhone.notifyHandoverStateChanged(mConnections[i]); 507 } else if ( mConnections[i].getCall() == mRingingCall ) { // it's a ringing call 508 newRinging = mConnections[i]; 509 } else { 510 // Something strange happened: a call appeared 511 // which is neither a ringing call or one we created. 512 // Either we've crashed and re-attached to an existing 513 // call, or something else (eg, SIM) initiated the call. 514 515 Rlog.i(LOG_TAG,"Phantom call appeared " + dc); 516 517 // If it's a connected call, set the connect time so that 518 // it's non-zero. It may not be accurate, but at least 519 // it won't appear as a Missed Call. 520 if (dc.state != DriverCall.State.ALERTING 521 && dc.state != DriverCall.State.DIALING) { 522 mConnections[i].onConnectedInOrOut(); 523 if (dc.state == DriverCall.State.HOLDING) { 524 // We've transitioned into HOLDING 525 mConnections[i].onStartedHolding(); 526 } 527 } 528 529 newUnknown = mConnections[i]; 530 531 unknownConnectionAppeared = true; 532 } 533 } 534 hasNonHangupStateChanged = true; 535 } else if (conn != null && dc == null) { 536 // Connection missing in CLCC response that we were 537 // tracking. 538 mDroppedDuringPoll.add(conn); 539 // Dropped connections are removed from the CallTracker 540 // list but kept in the GsmCall list 541 mConnections[i] = null; 542 } else if (conn != null && dc != null && !conn.compareTo(dc)) { 543 // Connection in CLCC response does not match what 544 // we were tracking. Assume dropped call and new call 545 546 mDroppedDuringPoll.add(conn); 547 mConnections[i] = new GsmConnection (mPhone.getContext(), dc, this, i); 548 549 if (mConnections[i].getCall() == mRingingCall) { 550 newRinging = mConnections[i]; 551 } // else something strange happened 552 hasNonHangupStateChanged = true; 553 } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ 554 boolean changed; 555 changed = conn.update(dc); 556 hasNonHangupStateChanged = hasNonHangupStateChanged || changed; 557 } 558 559 if (REPEAT_POLLING) { 560 if (dc != null) { 561 // FIXME with RIL, we should not need this anymore 562 if ((dc.state == DriverCall.State.DIALING 563 /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) 564 || (dc.state == DriverCall.State.ALERTING 565 /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) 566 || (dc.state == DriverCall.State.INCOMING 567 /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) 568 || (dc.state == DriverCall.State.WAITING 569 /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/) 570 ) { 571 // Sometimes there's no unsolicited notification 572 // for state transitions 573 needsPollDelay = true; 574 } 575 } 576 } 577 } 578 579 // This is the first poll after an ATD. 580 // We expect the pending call to appear in the list 581 // If it does not, we land here 582 if (mPendingMO != null) { 583 Rlog.d(LOG_TAG,"Pending MO dropped before poll fg state:" 584 + mForegroundCall.getState()); 585 586 mDroppedDuringPoll.add(mPendingMO); 587 mPendingMO = null; 588 mHangupPendingMO = false; 589 } 590 591 if (newRinging != null) { 592 mPhone.notifyNewRingingConnection(newRinging); 593 } 594 595 // clear the "local hangup" and "missed/rejected call" 596 // cases from the "dropped during poll" list 597 // These cases need no "last call fail" reason 598 for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) { 599 GsmConnection conn = mDroppedDuringPoll.get(i); 600 601 if (conn.isIncoming() && conn.getConnectTime() == 0) { 602 // Missed or rejected call 603 int cause; 604 if (conn.mCause == DisconnectCause.LOCAL) { 605 cause = DisconnectCause.INCOMING_REJECTED; 606 } else { 607 cause = DisconnectCause.INCOMING_MISSED; 608 } 609 610 if (Phone.DEBUG_PHONE) { 611 log("missed/rejected call, conn.cause=" + conn.mCause); 612 log("setting cause to " + cause); 613 } 614 mDroppedDuringPoll.remove(i); 615 hasAnyCallDisconnected |= conn.onDisconnect(cause); 616 } else if (conn.mCause == DisconnectCause.LOCAL 617 || conn.mCause == DisconnectCause.INVALID_NUMBER) { 618 mDroppedDuringPoll.remove(i); 619 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); 620 } 621 } 622 623 /* Disconnect any pending Handover connections */ 624 for (Connection hoConnection : mHandoverConnections) { 625 log("handlePollCalls - disconnect hoConn= " + hoConnection.toString()); 626 ((ImsPhoneConnection)hoConnection).onDisconnect(DisconnectCause.NOT_VALID); 627 mHandoverConnections.remove(hoConnection); 628 } 629 630 // Any non-local disconnects: determine cause 631 if (mDroppedDuringPoll.size() > 0) { 632 mCi.getLastCallFailCause( 633 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); 634 } 635 636 if (needsPollDelay) { 637 pollCallsAfterDelay(); 638 } 639 640 // Cases when we can no longer keep disconnected Connection's 641 // with their previous calls 642 // 1) the phone has started to ring 643 // 2) A Call/Connection object has changed state... 644 // we may have switched or held or answered (but not hung up) 645 if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) { 646 internalClearDisconnected(); 647 } 648 649 updatePhoneState(); 650 651 if (unknownConnectionAppeared) { 652 mPhone.notifyUnknownConnection(newUnknown); 653 } 654 655 if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { 656 mPhone.notifyPreciseCallStateChanged(); 657 } 658 659 //dumpState(); 660 } 661 662 private void 663 handleRadioNotAvailable() { 664 // handlePollCalls will clear out its 665 // call list when it gets the CommandException 666 // error result from this 667 pollCallsWhenSafe(); 668 } 669 670 private void 671 dumpState() { 672 List l; 673 674 Rlog.i(LOG_TAG,"Phone State:" + mState); 675 676 Rlog.i(LOG_TAG,"Ringing call: " + mRingingCall.toString()); 677 678 l = mRingingCall.getConnections(); 679 for (int i = 0, s = l.size(); i < s; i++) { 680 Rlog.i(LOG_TAG,l.get(i).toString()); 681 } 682 683 Rlog.i(LOG_TAG,"Foreground call: " + mForegroundCall.toString()); 684 685 l = mForegroundCall.getConnections(); 686 for (int i = 0, s = l.size(); i < s; i++) { 687 Rlog.i(LOG_TAG,l.get(i).toString()); 688 } 689 690 Rlog.i(LOG_TAG,"Background call: " + mBackgroundCall.toString()); 691 692 l = mBackgroundCall.getConnections(); 693 for (int i = 0, s = l.size(); i < s; i++) { 694 Rlog.i(LOG_TAG,l.get(i).toString()); 695 } 696 697 } 698 699 //***** Called from GsmConnection 700 701 /*package*/ void 702 hangup (GsmConnection conn) throws CallStateException { 703 if (conn.mOwner != this) { 704 throw new CallStateException ("GsmConnection " + conn 705 + "does not belong to GsmCallTracker " + this); 706 } 707 708 if (conn == mPendingMO) { 709 // We're hanging up an outgoing call that doesn't have it's 710 // GSM index assigned yet 711 712 if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); 713 mHangupPendingMO = true; 714 } else { 715 try { 716 mCi.hangupConnection (conn.getGSMIndex(), obtainCompleteMessage()); 717 } catch (CallStateException ex) { 718 // Ignore "connection not found" 719 // Call may have hung up already 720 Rlog.w(LOG_TAG,"GsmCallTracker WARN: hangup() on absent connection " 721 + conn); 722 } 723 } 724 725 conn.onHangupLocal(); 726 } 727 728 /*package*/ void 729 separate (GsmConnection conn) throws CallStateException { 730 if (conn.mOwner != this) { 731 throw new CallStateException ("GsmConnection " + conn 732 + "does not belong to GsmCallTracker " + this); 733 } 734 try { 735 mCi.separateConnection (conn.getGSMIndex(), 736 obtainCompleteMessage(EVENT_SEPARATE_RESULT)); 737 } catch (CallStateException ex) { 738 // Ignore "connection not found" 739 // Call may have hung up already 740 Rlog.w(LOG_TAG,"GsmCallTracker WARN: separate() on absent connection " 741 + conn); 742 } 743 } 744 745 //***** Called from GSMPhone 746 747 /*package*/ void 748 setMute(boolean mute) { 749 mDesiredMute = mute; 750 mCi.setMute(mDesiredMute, null); 751 } 752 753 /*package*/ boolean 754 getMute() { 755 return mDesiredMute; 756 } 757 758 759 //***** Called from GsmCall 760 761 /* package */ void 762 hangup (GsmCall call) throws CallStateException { 763 if (call.getConnections().size() == 0) { 764 throw new CallStateException("no connections in call"); 765 } 766 767 if (call == mRingingCall) { 768 if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); 769 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 770 } else if (call == mForegroundCall) { 771 if (call.isDialingOrAlerting()) { 772 if (Phone.DEBUG_PHONE) { 773 log("(foregnd) hangup dialing or alerting..."); 774 } 775 hangup((GsmConnection)(call.getConnections().get(0))); 776 } else if (mRingingCall.isRinging()) { 777 // Do not auto-answer ringing on CHUP, instead just end active calls 778 log("hangup all conns in active/background call, without affecting ringing call"); 779 hangupAllConnections(call); 780 } else { 781 hangupForegroundResumeBackground(); 782 } 783 } else if (call == mBackgroundCall) { 784 if (mRingingCall.isRinging()) { 785 if (Phone.DEBUG_PHONE) { 786 log("hangup all conns in background call"); 787 } 788 hangupAllConnections(call); 789 } else { 790 hangupWaitingOrBackground(); 791 } 792 } else { 793 throw new RuntimeException ("GsmCall " + call + 794 "does not belong to GsmCallTracker " + this); 795 } 796 797 call.onHangupLocal(); 798 mPhone.notifyPreciseCallStateChanged(); 799 } 800 801 /* package */ 802 void hangupWaitingOrBackground() { 803 if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); 804 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 805 } 806 807 /* package */ 808 void hangupForegroundResumeBackground() { 809 if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); 810 mCi.hangupForegroundResumeBackground(obtainCompleteMessage()); 811 } 812 813 void hangupConnectionByIndex(GsmCall call, int index) 814 throws CallStateException { 815 int count = call.mConnections.size(); 816 for (int i = 0; i < count; i++) { 817 GsmConnection cn = (GsmConnection)call.mConnections.get(i); 818 if (cn.getGSMIndex() == index) { 819 mCi.hangupConnection(index, obtainCompleteMessage()); 820 return; 821 } 822 } 823 824 throw new CallStateException("no gsm index found"); 825 } 826 827 void hangupAllConnections(GsmCall call) { 828 try { 829 int count = call.mConnections.size(); 830 for (int i = 0; i < count; i++) { 831 GsmConnection cn = (GsmConnection)call.mConnections.get(i); 832 mCi.hangupConnection(cn.getGSMIndex(), obtainCompleteMessage()); 833 } 834 } catch (CallStateException ex) { 835 Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); 836 } 837 } 838 839 /* package */ 840 GsmConnection getConnectionByIndex(GsmCall call, int index) 841 throws CallStateException { 842 int count = call.mConnections.size(); 843 for (int i = 0; i < count; i++) { 844 GsmConnection cn = (GsmConnection)call.mConnections.get(i); 845 if (cn.getGSMIndex() == index) { 846 return cn; 847 } 848 } 849 850 return null; 851 } 852 853 private Phone.SuppService getFailedService(int what) { 854 switch (what) { 855 case EVENT_SWITCH_RESULT: 856 return Phone.SuppService.SWITCH; 857 case EVENT_CONFERENCE_RESULT: 858 return Phone.SuppService.CONFERENCE; 859 case EVENT_SEPARATE_RESULT: 860 return Phone.SuppService.SEPARATE; 861 case EVENT_ECT_RESULT: 862 return Phone.SuppService.TRANSFER; 863 } 864 return Phone.SuppService.UNKNOWN; 865 } 866 867 //****** Overridden from Handler 868 869 @Override 870 public void 871 handleMessage (Message msg) { 872 AsyncResult ar; 873 874 if (!mPhone.mIsTheCurrentActivePhone) { 875 Rlog.e(LOG_TAG, "Received message " + msg + 876 "[" + msg.what + "] while being destroyed. Ignoring."); 877 return; 878 } 879 switch (msg.what) { 880 case EVENT_POLL_CALLS_RESULT: 881 ar = (AsyncResult)msg.obj; 882 883 if (msg == mLastRelevantPoll) { 884 if (DBG_POLL) log( 885 "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); 886 mNeedsPoll = false; 887 mLastRelevantPoll = null; 888 handlePollCalls((AsyncResult)msg.obj); 889 } 890 break; 891 892 case EVENT_OPERATION_COMPLETE: 893 ar = (AsyncResult)msg.obj; 894 operationComplete(); 895 break; 896 897 case EVENT_SWITCH_RESULT: 898 case EVENT_CONFERENCE_RESULT: 899 case EVENT_SEPARATE_RESULT: 900 case EVENT_ECT_RESULT: 901 ar = (AsyncResult)msg.obj; 902 if (ar.exception != null) { 903 mPhone.notifySuppServiceFailed(getFailedService(msg.what)); 904 } 905 operationComplete(); 906 break; 907 908 case EVENT_GET_LAST_CALL_FAIL_CAUSE: 909 int causeCode; 910 ar = (AsyncResult)msg.obj; 911 912 operationComplete(); 913 914 if (ar.exception != null) { 915 // An exception occurred...just treat the disconnect 916 // cause as "normal" 917 causeCode = CallFailCause.NORMAL_CLEARING; 918 Rlog.i(LOG_TAG, 919 "Exception during getLastCallFailCause, assuming normal disconnect"); 920 } else { 921 causeCode = ((int[])ar.result)[0]; 922 } 923 // Log the causeCode if its not normal 924 if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || 925 causeCode == CallFailCause.TEMPORARY_FAILURE || 926 causeCode == CallFailCause.SWITCHING_CONGESTION || 927 causeCode == CallFailCause.CHANNEL_NOT_AVAIL || 928 causeCode == CallFailCause.QOS_NOT_AVAIL || 929 causeCode == CallFailCause.BEARER_NOT_AVAIL || 930 causeCode == CallFailCause.ERROR_UNSPECIFIED) { 931 GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation()); 932 EventLog.writeEvent(EventLogTags.CALL_DROP, 933 causeCode, loc != null ? loc.getCid() : -1, 934 TelephonyManager.getDefault().getNetworkType()); 935 } 936 937 for (int i = 0, s = mDroppedDuringPoll.size() 938 ; i < s ; i++ 939 ) { 940 GsmConnection conn = mDroppedDuringPoll.get(i); 941 942 conn.onRemoteDisconnect(causeCode); 943 } 944 945 updatePhoneState(); 946 947 mPhone.notifyPreciseCallStateChanged(); 948 mDroppedDuringPoll.clear(); 949 break; 950 951 case EVENT_REPOLL_AFTER_DELAY: 952 case EVENT_CALL_STATE_CHANGE: 953 pollCallsWhenSafe(); 954 break; 955 956 case EVENT_RADIO_AVAILABLE: 957 handleRadioAvailable(); 958 break; 959 960 case EVENT_RADIO_NOT_AVAILABLE: 961 handleRadioNotAvailable(); 962 break; 963 } 964 } 965 966 @Override 967 protected void log(String msg) { 968 Rlog.d(LOG_TAG, "[GsmCallTracker] " + msg); 969 } 970 971 @Override 972 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 973 pw.println("GsmCallTracker extends:"); 974 super.dump(fd, pw, args); 975 pw.println("mConnections: length=" + mConnections.length); 976 for(int i=0; i < mConnections.length; i++) { 977 pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]); 978 } 979 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 980 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 981 pw.println(" mDroppedDuringPoll: size=" + mDroppedDuringPoll.size()); 982 for(int i = 0; i < mDroppedDuringPoll.size(); i++) { 983 pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i)); 984 } 985 pw.println(" mRingingCall=" + mRingingCall); 986 pw.println(" mForegroundCall=" + mForegroundCall); 987 pw.println(" mBackgroundCall=" + mBackgroundCall); 988 pw.println(" mPendingMO=" + mPendingMO); 989 pw.println(" mHangupPendingMO=" + mHangupPendingMO); 990 pw.println(" mPhone=" + mPhone); 991 pw.println(" mDesiredMute=" + mDesiredMute); 992 pw.println(" mState=" + mState); 993 } 994 @Override 995 public PhoneConstants.State getState() { 996 return mState; 997 } 998} 999