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