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