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