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