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