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