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