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