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