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