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