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