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