1/* 2 * Copyright (C) 2015 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; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.os.AsyncResult; 24import android.os.Bundle; 25import android.os.Handler; 26import android.os.Message; 27import android.os.Registrant; 28import android.os.RegistrantList; 29import android.os.SystemProperties; 30import android.telephony.CellLocation; 31import android.telephony.DisconnectCause; 32import android.telephony.PhoneNumberUtils; 33import android.telephony.ServiceState; 34import android.telephony.TelephonyManager; 35import android.telephony.cdma.CdmaCellLocation; 36import android.telephony.gsm.GsmCellLocation; 37import android.text.TextUtils; 38import java.util.Iterator; 39import android.telephony.Rlog; 40import android.util.EventLog; 41 42import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; 43import com.android.internal.telephony.metrics.TelephonyMetrics; 44 45import java.io.FileDescriptor; 46import java.io.PrintWriter; 47import java.util.List; 48import java.util.ArrayList; 49 50/** 51 * {@hide} 52 */ 53public class GsmCdmaCallTracker extends CallTracker { 54 private static final String LOG_TAG = "GsmCdmaCallTracker"; 55 private static final boolean REPEAT_POLLING = false; 56 57 private static final boolean DBG_POLL = false; 58 private static final boolean VDBG = false; 59 60 //***** Constants 61 62 public static final int MAX_CONNECTIONS_GSM = 19; //7 allowed in GSM + 12 from IMS for SRVCC 63 private static final int MAX_CONNECTIONS_PER_CALL_GSM = 5; //only 5 connections allowed per call 64 65 private static final int MAX_CONNECTIONS_CDMA = 8; 66 private static final int MAX_CONNECTIONS_PER_CALL_CDMA = 1; //only 1 connection allowed per call 67 68 //***** Instance Variables 69 private GsmCdmaConnection mConnections[]; 70 private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 71 private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 72 73 // connections dropped during last poll 74 private ArrayList<GsmCdmaConnection> mDroppedDuringPoll = 75 new ArrayList<GsmCdmaConnection>(MAX_CONNECTIONS_GSM); 76 77 public GsmCdmaCall mRingingCall = new GsmCdmaCall(this); 78 // A call that is ringing or (call) waiting 79 public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this); 80 public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this); 81 82 private GsmCdmaConnection mPendingMO; 83 private boolean mHangupPendingMO; 84 85 private GsmCdmaPhone mPhone; 86 87 private boolean mDesiredMute = false; // false = mute off 88 89 public PhoneConstants.State mState = PhoneConstants.State.IDLE; 90 91 private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance(); 92 93 // Following member variables are for CDMA only 94 private RegistrantList mCallWaitingRegistrants = new RegistrantList(); 95 private boolean mPendingCallInEcm; 96 private boolean mIsInEmergencyCall; 97 private int mPendingCallClirMode; 98 private boolean mIsEcmTimerCanceled; 99 private int m3WayCallFlashDelay; 100 101 /** 102 * Listens for Emergency Callback Mode state change intents 103 */ 104 private BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() { 105 @Override 106 public void onReceive(Context context, Intent intent) { 107 if (intent.getAction().equals( 108 TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { 109 110 boolean isInEcm = intent.getBooleanExtra(PhoneConstants.PHONE_IN_ECM_STATE, false); 111 log("Received ACTION_EMERGENCY_CALLBACK_MODE_CHANGED isInEcm = " + isInEcm); 112 113 // If we exit ECM mode, notify all connections. 114 if (!isInEcm) { 115 // Although mConnections seems to be the place to look, it is not guaranteed 116 // to have all of the connections we're tracking. THe best place to look is in 117 // the Call objects associated with the tracker. 118 List<Connection> toNotify = new ArrayList<Connection>(); 119 toNotify.addAll(mRingingCall.getConnections()); 120 toNotify.addAll(mForegroundCall.getConnections()); 121 toNotify.addAll(mBackgroundCall.getConnections()); 122 if (mPendingMO != null) { 123 toNotify.add(mPendingMO); 124 } 125 126 // Notify connections that ECM mode exited. 127 for (Connection connection : toNotify) { 128 if (connection != null) { 129 connection.onExitedEcmMode(); 130 } 131 } 132 } 133 } 134 } 135 }; 136 137 //***** Events 138 139 140 //***** Constructors 141 142 public GsmCdmaCallTracker (GsmCdmaPhone phone) { 143 this.mPhone = phone; 144 mCi = phone.mCi; 145 mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); 146 mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null); 147 mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); 148 149 // Register receiver for ECM exit 150 IntentFilter filter = new IntentFilter(); 151 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 152 mPhone.getContext().registerReceiver(mEcmExitReceiver, filter); 153 154 updatePhoneType(true); 155 } 156 157 public void updatePhoneType() { 158 updatePhoneType(false); 159 } 160 161 private void updatePhoneType(boolean duringInit) { 162 if (!duringInit) { 163 reset(); 164 pollCallsWhenSafe(); 165 } 166 if (mPhone.isPhoneTypeGsm()) { 167 mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_GSM]; 168 mCi.unregisterForCallWaitingInfo(this); 169 } else { 170 mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA]; 171 mPendingCallInEcm = false; 172 mIsInEmergencyCall = false; 173 mPendingCallClirMode = CommandsInterface.CLIR_DEFAULT; 174 mIsEcmTimerCanceled = false; 175 m3WayCallFlashDelay = 0; 176 mCi.registerForCallWaitingInfo(this, EVENT_CALL_WAITING_INFO_CDMA, null); 177 } 178 } 179 180 private void reset() { 181 Rlog.d(LOG_TAG, "reset"); 182 183 clearDisconnected(); 184 185 for (GsmCdmaConnection gsmCdmaConnection : mConnections) { 186 if (gsmCdmaConnection != null) { 187 gsmCdmaConnection.dispose(); 188 } 189 } 190 191 if (mPendingMO != null) { 192 mPendingMO.dispose(); 193 } 194 195 mConnections = null; 196 mPendingMO = null; 197 mState = PhoneConstants.State.IDLE; 198 } 199 200 @Override 201 protected void finalize() { 202 Rlog.d(LOG_TAG, "GsmCdmaCallTracker finalized"); 203 } 204 205 //***** Instance Methods 206 207 //***** Public Methods 208 @Override 209 public void registerForVoiceCallStarted(Handler h, int what, Object obj) { 210 Registrant r = new Registrant(h, what, obj); 211 mVoiceCallStartedRegistrants.add(r); 212 // Notify if in call when registering 213 if (mState != PhoneConstants.State.IDLE) { 214 r.notifyRegistrant(new AsyncResult(null, null, null)); 215 } 216 } 217 218 @Override 219 public void unregisterForVoiceCallStarted(Handler h) { 220 mVoiceCallStartedRegistrants.remove(h); 221 } 222 223 @Override 224 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 225 Registrant r = new Registrant(h, what, obj); 226 mVoiceCallEndedRegistrants.add(r); 227 } 228 229 @Override 230 public void unregisterForVoiceCallEnded(Handler h) { 231 mVoiceCallEndedRegistrants.remove(h); 232 } 233 234 public void registerForCallWaiting(Handler h, int what, Object obj) { 235 Registrant r = new Registrant (h, what, obj); 236 mCallWaitingRegistrants.add(r); 237 } 238 239 public void unregisterForCallWaiting(Handler h) { 240 mCallWaitingRegistrants.remove(h); 241 } 242 243 private void fakeHoldForegroundBeforeDial() { 244 List<Connection> connCopy; 245 246 // We need to make a copy here, since fakeHoldBeforeDial() 247 // modifies the lists, and we don't want to reverse the order 248 connCopy = (List<Connection>) mForegroundCall.mConnections.clone(); 249 250 for (int i = 0, s = connCopy.size() ; i < s ; i++) { 251 GsmCdmaConnection conn = (GsmCdmaConnection)connCopy.get(i); 252 253 conn.fakeHoldBeforeDial(); 254 } 255 } 256 257 //GSM 258 /** 259 * clirMode is one of the CLIR_ constants 260 */ 261 public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo, 262 Bundle intentExtras) 263 throws CallStateException { 264 // note that this triggers call state changed notif 265 clearDisconnected(); 266 267 if (!canDial()) { 268 throw new CallStateException("cannot dial in current state"); 269 } 270 271 String origNumber = dialString; 272 dialString = convertNumberIfNecessary(mPhone, dialString); 273 274 // The new call must be assigned to the foreground call. 275 // That call must be idle, so place anything that's 276 // there on hold 277 if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) { 278 // this will probably be done by the radio anyway 279 // but the dial might fail before this happens 280 // and we need to make sure the foreground call is clear 281 // for the newly dialed connection 282 switchWaitingOrHoldingAndActive(); 283 // This is a hack to delay DIAL so that it is sent out to RIL only after 284 // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to 285 // multi-way conference calls due to DIAL being sent out before SWITCH is processed 286 try { 287 Thread.sleep(500); 288 } catch (InterruptedException e) { 289 // do nothing 290 } 291 292 // Fake local state so that 293 // a) foregroundCall is empty for the newly dialed connection 294 // b) hasNonHangupStateChanged remains false in the 295 // next poll, so that we don't clear a failed dialing call 296 fakeHoldForegroundBeforeDial(); 297 } 298 299 if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) { 300 //we should have failed in !canDial() above before we get here 301 throw new CallStateException("cannot dial in current state"); 302 } 303 boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), 304 dialString); 305 mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString), 306 this, mForegroundCall, isEmergencyCall); 307 mHangupPendingMO = false; 308 mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo); 309 310 311 if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0 312 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { 313 // Phone number is invalid 314 mPendingMO.mCause = DisconnectCause.INVALID_NUMBER; 315 316 // handlePollCalls() will notice this call not present 317 // and will mark it as dropped. 318 pollCallsWhenSafe(); 319 } else { 320 // Always unmute when initiating a new call 321 setMute(false); 322 323 mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage()); 324 } 325 326 if (mNumberConverted) { 327 mPendingMO.setConverted(origNumber); 328 mNumberConverted = false; 329 } 330 331 updatePhoneState(); 332 mPhone.notifyPreciseCallStateChanged(); 333 334 return mPendingMO; 335 } 336 337 //CDMA 338 /** 339 * Handle Ecm timer to be canceled or re-started 340 */ 341 private void handleEcmTimer(int action) { 342 mPhone.handleTimerInEmergencyCallbackMode(action); 343 switch(action) { 344 case GsmCdmaPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break; 345 case GsmCdmaPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break; 346 default: 347 Rlog.e(LOG_TAG, "handleEcmTimer, unsupported action " + action); 348 } 349 } 350 351 //CDMA 352 /** 353 * Disable data call when emergency call is connected 354 */ 355 private void disableDataCallInEmergencyCall(String dialString) { 356 if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) { 357 if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall"); 358 setIsInEmergencyCall(); 359 } 360 } 361 362 //CDMA 363 public void setIsInEmergencyCall() { 364 mIsInEmergencyCall = true; 365 mPhone.mDcTracker.setInternalDataEnabled(false); 366 mPhone.notifyEmergencyCallRegistrants(true); 367 mPhone.sendEmergencyCallStateChange(true); 368 } 369 370 //CDMA 371 /** 372 * clirMode is one of the CLIR_ constants 373 */ 374 private Connection dial(String dialString, int clirMode) throws CallStateException { 375 // note that this triggers call state changed notif 376 clearDisconnected(); 377 378 if (!canDial()) { 379 throw new CallStateException("cannot dial in current state"); 380 } 381 382 TelephonyManager tm = 383 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE); 384 String origNumber = dialString; 385 String operatorIsoContry = tm.getNetworkCountryIsoForPhone(mPhone.getPhoneId()); 386 String simIsoContry = tm.getSimCountryIsoForPhone(mPhone.getPhoneId()); 387 boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry) 388 && !TextUtils.isEmpty(simIsoContry) 389 && !simIsoContry.equals(operatorIsoContry); 390 if (internationalRoaming) { 391 if ("us".equals(simIsoContry)) { 392 internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry); 393 } else if ("vi".equals(simIsoContry)) { 394 internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry); 395 } 396 } 397 if (internationalRoaming) { 398 dialString = convertNumberIfNecessary(mPhone, dialString); 399 } 400 401 String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 402 boolean isPhoneInEcmMode = inEcm.equals("true"); 403 boolean isEmergencyCall = 404 PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString); 405 406 // Cancel Ecm timer if a second emergency call is originating in Ecm mode 407 if (isPhoneInEcmMode && isEmergencyCall) { 408 handleEcmTimer(GsmCdmaPhone.CANCEL_ECM_TIMER); 409 } 410 411 // The new call must be assigned to the foreground call. 412 // That call must be idle, so place anything that's 413 // there on hold 414 if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) { 415 return dialThreeWay(dialString); 416 } 417 418 mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString), 419 this, mForegroundCall, isEmergencyCall); 420 mHangupPendingMO = false; 421 422 if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0 423 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) { 424 // Phone number is invalid 425 mPendingMO.mCause = DisconnectCause.INVALID_NUMBER; 426 427 // handlePollCalls() will notice this call not present 428 // and will mark it as dropped. 429 pollCallsWhenSafe(); 430 } else { 431 // Always unmute when initiating a new call 432 setMute(false); 433 434 // Check data call 435 disableDataCallInEmergencyCall(dialString); 436 437 // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit. 438 if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) { 439 mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage()); 440 } else { 441 mPhone.exitEmergencyCallbackMode(); 442 mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null); 443 mPendingCallClirMode=clirMode; 444 mPendingCallInEcm=true; 445 } 446 } 447 448 if (mNumberConverted) { 449 mPendingMO.setConverted(origNumber); 450 mNumberConverted = false; 451 } 452 453 updatePhoneState(); 454 mPhone.notifyPreciseCallStateChanged(); 455 456 return mPendingMO; 457 } 458 459 //CDMA 460 private Connection dialThreeWay(String dialString) { 461 if (!mForegroundCall.isIdle()) { 462 // Check data call and possibly set mIsInEmergencyCall 463 disableDataCallInEmergencyCall(dialString); 464 465 // Attach the new connection to foregroundCall 466 mPendingMO = new GsmCdmaConnection(mPhone, 467 checkForTestEmergencyNumber(dialString), this, mForegroundCall, 468 mIsInEmergencyCall); 469 // Some network need a empty flash before sending the normal one 470 m3WayCallFlashDelay = mPhone.getContext().getResources() 471 .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay); 472 if (m3WayCallFlashDelay > 0) { 473 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH)); 474 } else { 475 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 476 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 477 } 478 return mPendingMO; 479 } 480 return null; 481 } 482 483 public Connection dial(String dialString) throws CallStateException { 484 if (isPhoneTypeGsm()) { 485 return dial(dialString, CommandsInterface.CLIR_DEFAULT, null); 486 } else { 487 return dial(dialString, CommandsInterface.CLIR_DEFAULT); 488 } 489 } 490 491 //GSM 492 public Connection dial(String dialString, UUSInfo uusInfo, Bundle intentExtras) 493 throws CallStateException { 494 return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras); 495 } 496 497 //GSM 498 private Connection dial(String dialString, int clirMode, Bundle intentExtras) 499 throws CallStateException { 500 return dial(dialString, clirMode, null, intentExtras); 501 } 502 503 public void acceptCall() throws CallStateException { 504 // FIXME if SWITCH fails, should retry with ANSWER 505 // in case the active/holding call disappeared and this 506 // is no longer call waiting 507 508 if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) { 509 Rlog.i("phone", "acceptCall: incoming..."); 510 // Always unmute when answering a new call 511 setMute(false); 512 mCi.acceptCall(obtainCompleteMessage()); 513 } else if (mRingingCall.getState() == GsmCdmaCall.State.WAITING) { 514 if (isPhoneTypeGsm()) { 515 setMute(false); 516 } else { 517 GsmCdmaConnection cwConn = (GsmCdmaConnection)(mRingingCall.getLatestConnection()); 518 519 // Since there is no network response for supplimentary 520 // service for CDMA, we assume call waiting is answered. 521 // ringing Call state change to idle is in GsmCdmaCall.detach 522 // triggered by updateParent. 523 cwConn.updateParent(mRingingCall, mForegroundCall); 524 cwConn.onConnectedInOrOut(); 525 updatePhoneState(); 526 } 527 switchWaitingOrHoldingAndActive(); 528 } else { 529 throw new CallStateException("phone not ringing"); 530 } 531 } 532 533 public void rejectCall() throws CallStateException { 534 // AT+CHLD=0 means "release held or UDUB" 535 // so if the phone isn't ringing, this could hang up held 536 if (mRingingCall.getState().isRinging()) { 537 mCi.rejectCall(obtainCompleteMessage()); 538 } else { 539 throw new CallStateException("phone not ringing"); 540 } 541 } 542 543 //CDMA 544 private void flashAndSetGenericTrue() { 545 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); 546 547 mPhone.notifyPreciseCallStateChanged(); 548 } 549 550 public void switchWaitingOrHoldingAndActive() throws CallStateException { 551 // Should we bother with this check? 552 if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) { 553 throw new CallStateException("cannot be in the incoming state"); 554 } else { 555 if (isPhoneTypeGsm()) { 556 mCi.switchWaitingOrHoldingAndActive( 557 obtainCompleteMessage(EVENT_SWITCH_RESULT)); 558 } else { 559 if (mForegroundCall.getConnections().size() > 1) { 560 flashAndSetGenericTrue(); 561 } else { 562 // Send a flash command to CDMA network for putting the other party on hold. 563 // For CDMA networks which do not support this the user would just hear a beep 564 // from the network. For CDMA networks which do support it will put the other 565 // party on hold. 566 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); 567 } 568 } 569 } 570 } 571 572 public void conference() { 573 if (isPhoneTypeGsm()) { 574 mCi.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT)); 575 } else { 576 // Should we be checking state? 577 flashAndSetGenericTrue(); 578 } 579 } 580 581 public void explicitCallTransfer() { 582 mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); 583 } 584 585 public void clearDisconnected() { 586 internalClearDisconnected(); 587 588 updatePhoneState(); 589 mPhone.notifyPreciseCallStateChanged(); 590 } 591 592 public boolean canConference() { 593 return mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE 594 && mBackgroundCall.getState() == GsmCdmaCall.State.HOLDING 595 && !mBackgroundCall.isFull() 596 && !mForegroundCall.isFull(); 597 } 598 599 private boolean canDial() { 600 boolean ret; 601 int serviceState = mPhone.getServiceState().getState(); 602 String disableCall = SystemProperties.get( 603 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 604 605 ret = (serviceState != ServiceState.STATE_POWER_OFF) 606 && mPendingMO == null 607 && !mRingingCall.isRinging() 608 && !disableCall.equals("true") 609 && (!mForegroundCall.getState().isAlive() 610 || !mBackgroundCall.getState().isAlive() 611 || (!isPhoneTypeGsm() 612 && mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE)); 613 614 if (!ret) { 615 log(String.format("canDial is false\n" + 616 "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" + 617 "&& pendingMO == null::=%s\n" + 618 "&& !ringingCall.isRinging()::=%s\n" + 619 "&& !disableCall.equals(\"true\")::=%s\n" + 620 "&& (!foregroundCall.getState().isAlive()::=%s\n" + 621 " || foregroundCall.getState() == GsmCdmaCall.State.ACTIVE::=%s\n" + 622 " ||!backgroundCall.getState().isAlive())::=%s)", 623 serviceState, 624 serviceState != ServiceState.STATE_POWER_OFF, 625 mPendingMO == null, 626 !mRingingCall.isRinging(), 627 !disableCall.equals("true"), 628 !mForegroundCall.getState().isAlive(), 629 mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE, 630 !mBackgroundCall.getState().isAlive())); 631 } 632 633 return ret; 634 } 635 636 public boolean canTransfer() { 637 if (isPhoneTypeGsm()) { 638 return (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE 639 || mForegroundCall.getState() == GsmCdmaCall.State.ALERTING 640 || mForegroundCall.getState() == GsmCdmaCall.State.DIALING) 641 && mBackgroundCall.getState() == GsmCdmaCall.State.HOLDING; 642 } else { 643 Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA"); 644 return false; 645 } 646 } 647 648 //***** Private Instance Methods 649 650 private void internalClearDisconnected() { 651 mRingingCall.clearDisconnected(); 652 mForegroundCall.clearDisconnected(); 653 mBackgroundCall.clearDisconnected(); 654 } 655 656 /** 657 * Obtain a message to use for signalling "invoke getCurrentCalls() when 658 * this operation and all other pending operations are complete 659 */ 660 private Message obtainCompleteMessage() { 661 return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); 662 } 663 664 /** 665 * Obtain a message to use for signalling "invoke getCurrentCalls() when 666 * this operation and all other pending operations are complete 667 */ 668 private Message obtainCompleteMessage(int what) { 669 mPendingOperations++; 670 mLastRelevantPoll = null; 671 mNeedsPoll = true; 672 673 if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + 674 mPendingOperations + ", needsPoll=" + mNeedsPoll); 675 676 return obtainMessage(what); 677 } 678 679 private void operationComplete() { 680 mPendingOperations--; 681 682 if (DBG_POLL) log("operationComplete: pendingOperations=" + 683 mPendingOperations + ", needsPoll=" + mNeedsPoll); 684 685 if (mPendingOperations == 0 && mNeedsPoll) { 686 mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); 687 mCi.getCurrentCalls(mLastRelevantPoll); 688 } else if (mPendingOperations < 0) { 689 // this should never happen 690 Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0"); 691 mPendingOperations = 0; 692 } 693 } 694 695 private void updatePhoneState() { 696 PhoneConstants.State oldState = mState; 697 if (mRingingCall.isRinging()) { 698 mState = PhoneConstants.State.RINGING; 699 } else if (mPendingMO != null || 700 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 701 mState = PhoneConstants.State.OFFHOOK; 702 } else { 703 Phone imsPhone = mPhone.getImsPhone(); 704 if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){ 705 imsPhone.callEndCleanupHandOverCallIfAny(); 706 } 707 mState = PhoneConstants.State.IDLE; 708 } 709 710 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 711 mVoiceCallEndedRegistrants.notifyRegistrants( 712 new AsyncResult(null, null, null)); 713 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 714 mVoiceCallStartedRegistrants.notifyRegistrants ( 715 new AsyncResult(null, null, null)); 716 } 717 if (Phone.DEBUG_PHONE) { 718 log("update phone state, old=" + oldState + " new="+ mState); 719 } 720 if (mState != oldState) { 721 mPhone.notifyPhoneStateChanged(); 722 mMetrics.writePhoneState(mPhone.getPhoneId(), mState); 723 } 724 } 725 726 // ***** Overwritten from CallTracker 727 728 @Override 729 protected synchronized void handlePollCalls(AsyncResult ar) { 730 List polledCalls; 731 732 if (VDBG) log("handlePollCalls"); 733 if (ar.exception == null) { 734 polledCalls = (List)ar.result; 735 } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { 736 // just a dummy empty ArrayList to cause the loop 737 // to hang up all the calls 738 polledCalls = new ArrayList(); 739 } else { 740 // Radio probably wasn't ready--try again in a bit 741 // But don't keep polling if the channel is closed 742 pollCallsAfterDelay(); 743 return; 744 } 745 746 Connection newRinging = null; //or waiting 747 ArrayList<Connection> newUnknownConnectionsGsm = new ArrayList<Connection>(); 748 Connection newUnknownConnectionCdma = null; 749 boolean hasNonHangupStateChanged = false; // Any change besides 750 // a dropped connection 751 boolean hasAnyCallDisconnected = false; 752 boolean needsPollDelay = false; 753 boolean unknownConnectionAppeared = false; 754 int handoverConnectionsSize = mHandoverConnections.size(); 755 756 //CDMA 757 boolean noConnectionExists = true; 758 759 for (int i = 0, curDC = 0, dcSize = polledCalls.size() 760 ; i < mConnections.length; i++) { 761 GsmCdmaConnection conn = mConnections[i]; 762 DriverCall dc = null; 763 764 // polledCall list is sparse 765 if (curDC < dcSize) { 766 dc = (DriverCall) polledCalls.get(curDC); 767 768 if (dc.index == i+1) { 769 curDC++; 770 } else { 771 dc = null; 772 } 773 } 774 775 //CDMA 776 if (conn != null || dc != null) { 777 noConnectionExists = false; 778 } 779 780 if (DBG_POLL) log("poll: conn[i=" + i + "]=" + 781 conn+", dc=" + dc); 782 783 if (conn == null && dc != null) { 784 // Connection appeared in CLCC response that we don't know about 785 if (mPendingMO != null && mPendingMO.compareTo(dc)) { 786 787 if (DBG_POLL) log("poll: pendingMO=" + mPendingMO); 788 789 // It's our pending mobile originating call 790 mConnections[i] = mPendingMO; 791 mPendingMO.mIndex = i; 792 mPendingMO.update(dc); 793 mPendingMO = null; 794 795 // Someone has already asked to hangup this call 796 if (mHangupPendingMO) { 797 mHangupPendingMO = false; 798 799 // Re-start Ecm timer when an uncompleted emergency call ends 800 if (!isPhoneTypeGsm() && mIsEcmTimerCanceled) { 801 handleEcmTimer(GsmCdmaPhone.RESTART_ECM_TIMER); 802 } 803 804 try { 805 if (Phone.DEBUG_PHONE) log( 806 "poll: hangupPendingMO, hangup conn " + i); 807 hangup(mConnections[i]); 808 } catch (CallStateException ex) { 809 Rlog.e(LOG_TAG, "unexpected error on hangup"); 810 } 811 812 // Do not continue processing this poll 813 // Wait for hangup and repoll 814 return; 815 } 816 } else { 817 if (Phone.DEBUG_PHONE) { 818 log("pendingMo=" + mPendingMO + ", dc=" + dc); 819 } 820 821 mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i); 822 823 Connection hoConnection = getHoConnection(dc); 824 if (hoConnection != null) { 825 // Single Radio Voice Call Continuity (SRVCC) completed 826 mConnections[i].migrateFrom(hoConnection); 827 // Updating connect time for silent redial cases (ex: Calls are transferred 828 // from DIALING/ALERTING/INCOMING/WAITING to ACTIVE) 829 if (hoConnection.mPreHandoverState != GsmCdmaCall.State.ACTIVE && 830 hoConnection.mPreHandoverState != GsmCdmaCall.State.HOLDING && 831 dc.state == DriverCall.State.ACTIVE) { 832 mConnections[i].onConnectedInOrOut(); 833 } 834 835 mHandoverConnections.remove(hoConnection); 836 837 if (isPhoneTypeGsm()) { 838 for (Iterator<Connection> it = mHandoverConnections.iterator(); 839 it.hasNext(); ) { 840 Connection c = it.next(); 841 Rlog.i(LOG_TAG, "HO Conn state is " + c.mPreHandoverState); 842 if (c.mPreHandoverState == mConnections[i].getState()) { 843 Rlog.i(LOG_TAG, "Removing HO conn " 844 + hoConnection + c.mPreHandoverState); 845 it.remove(); 846 } 847 } 848 } 849 850 mPhone.notifyHandoverStateChanged(mConnections[i]); 851 } else { 852 // find if the MT call is a new ring or unknown connection 853 newRinging = checkMtFindNewRinging(dc,i); 854 if (newRinging == null) { 855 unknownConnectionAppeared = true; 856 if (isPhoneTypeGsm()) { 857 newUnknownConnectionsGsm.add(mConnections[i]); 858 } else { 859 newUnknownConnectionCdma = mConnections[i]; 860 } 861 } 862 } 863 } 864 hasNonHangupStateChanged = true; 865 } else if (conn != null && dc == null) { 866 if (isPhoneTypeGsm()) { 867 // Connection missing in CLCC response that we were 868 // tracking. 869 mDroppedDuringPoll.add(conn); 870 } else { 871 // This case means the RIL has no more active call anymore and 872 // we need to clean up the foregroundCall and ringingCall. 873 // Loop through foreground call connections as 874 // it contains the known logical connections. 875 int count = mForegroundCall.mConnections.size(); 876 for (int n = 0; n < count; n++) { 877 if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll"); 878 GsmCdmaConnection cn = (GsmCdmaConnection)mForegroundCall.mConnections.get(n); 879 mDroppedDuringPoll.add(cn); 880 } 881 count = mRingingCall.mConnections.size(); 882 // Loop through ringing call connections as 883 // it may contain the known logical connections. 884 for (int n = 0; n < count; n++) { 885 if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll"); 886 GsmCdmaConnection cn = (GsmCdmaConnection)mRingingCall.mConnections.get(n); 887 mDroppedDuringPoll.add(cn); 888 } 889 890 // Re-start Ecm timer when the connected emergency call ends 891 if (mIsEcmTimerCanceled) { 892 handleEcmTimer(GsmCdmaPhone.RESTART_ECM_TIMER); 893 } 894 // If emergency call is not going through while dialing 895 checkAndEnableDataCallAfterEmergencyCallDropped(); 896 } 897 // Dropped connections are removed from the CallTracker 898 // list but kept in the Call list 899 mConnections[i] = null; 900 } else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) { 901 // Connection in CLCC response does not match what 902 // we were tracking. Assume dropped call and new call 903 904 mDroppedDuringPoll.add(conn); 905 mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i); 906 907 if (mConnections[i].getCall() == mRingingCall) { 908 newRinging = mConnections[i]; 909 } // else something strange happened 910 hasNonHangupStateChanged = true; 911 } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ 912 // Call collision case 913 if (!isPhoneTypeGsm() && conn.isIncoming() != dc.isMT) { 914 if (dc.isMT == true) { 915 // Mt call takes precedence than Mo,drops Mo 916 mDroppedDuringPoll.add(conn); 917 // find if the MT call is a new ring or unknown connection 918 newRinging = checkMtFindNewRinging(dc,i); 919 if (newRinging == null) { 920 unknownConnectionAppeared = true; 921 newUnknownConnectionCdma = conn; 922 } 923 checkAndEnableDataCallAfterEmergencyCallDropped(); 924 } else { 925 // Call info stored in conn is not consistent with the call info from dc. 926 // We should follow the rule of MT calls taking precedence over MO calls 927 // when there is conflict, so here we drop the call info from dc and 928 // continue to use the call info from conn, and only take a log. 929 Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc); 930 } 931 } else { 932 boolean changed; 933 changed = conn.update(dc); 934 hasNonHangupStateChanged = hasNonHangupStateChanged || changed; 935 } 936 } 937 938 if (REPEAT_POLLING) { 939 if (dc != null) { 940 // FIXME with RIL, we should not need this anymore 941 if ((dc.state == DriverCall.State.DIALING 942 /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) 943 || (dc.state == DriverCall.State.ALERTING 944 /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) 945 || (dc.state == DriverCall.State.INCOMING 946 /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) 947 || (dc.state == DriverCall.State.WAITING 948 /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)) { 949 // Sometimes there's no unsolicited notification 950 // for state transitions 951 needsPollDelay = true; 952 } 953 } 954 } 955 } 956 957 // Safety check so that obj is not stuck with mIsInEmergencyCall set to true (and data 958 // disabled). This should never happen though. 959 if (!isPhoneTypeGsm() && noConnectionExists) { 960 checkAndEnableDataCallAfterEmergencyCallDropped(); 961 } 962 963 // This is the first poll after an ATD. 964 // We expect the pending call to appear in the list 965 // If it does not, we land here 966 if (mPendingMO != null) { 967 Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:" 968 + mForegroundCall.getState()); 969 970 mDroppedDuringPoll.add(mPendingMO); 971 mPendingMO = null; 972 mHangupPendingMO = false; 973 974 if (!isPhoneTypeGsm()) { 975 if( mPendingCallInEcm) { 976 mPendingCallInEcm = false; 977 } 978 checkAndEnableDataCallAfterEmergencyCallDropped(); 979 } 980 } 981 982 if (newRinging != null) { 983 mPhone.notifyNewRingingConnection(newRinging); 984 } 985 986 // clear the "local hangup" and "missed/rejected call" 987 // cases from the "dropped during poll" list 988 // These cases need no "last call fail" reason 989 ArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>(); 990 for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) { 991 GsmCdmaConnection conn = mDroppedDuringPoll.get(i); 992 //CDMA 993 boolean wasDisconnected = false; 994 995 if (conn.isIncoming() && conn.getConnectTime() == 0) { 996 // Missed or rejected call 997 int cause; 998 if (conn.mCause == DisconnectCause.LOCAL) { 999 cause = DisconnectCause.INCOMING_REJECTED; 1000 } else { 1001 cause = DisconnectCause.INCOMING_MISSED; 1002 } 1003 1004 if (Phone.DEBUG_PHONE) { 1005 log("missed/rejected call, conn.cause=" + conn.mCause); 1006 log("setting cause to " + cause); 1007 } 1008 mDroppedDuringPoll.remove(i); 1009 hasAnyCallDisconnected |= conn.onDisconnect(cause); 1010 wasDisconnected = true; 1011 locallyDisconnectedConnections.add(conn); 1012 } else if (conn.mCause == DisconnectCause.LOCAL 1013 || conn.mCause == DisconnectCause.INVALID_NUMBER) { 1014 mDroppedDuringPoll.remove(i); 1015 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); 1016 wasDisconnected = true; 1017 locallyDisconnectedConnections.add(conn); 1018 } 1019 1020 if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared 1021 && conn == newUnknownConnectionCdma) { 1022 unknownConnectionAppeared = false; 1023 newUnknownConnectionCdma = null; 1024 } 1025 } 1026 if (locallyDisconnectedConnections.size() > 0) { 1027 mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections); 1028 } 1029 1030 /* Disconnect any pending Handover connections */ 1031 for (Iterator<Connection> it = mHandoverConnections.iterator(); 1032 it.hasNext();) { 1033 Connection hoConnection = it.next(); 1034 log("handlePollCalls - disconnect hoConn= " + hoConnection + 1035 " hoConn.State= " + hoConnection.getState()); 1036 if (hoConnection.getState().isRinging()) { 1037 hoConnection.onDisconnect(DisconnectCause.INCOMING_MISSED); 1038 } else { 1039 hoConnection.onDisconnect(DisconnectCause.NOT_VALID); 1040 } 1041 // TODO: Do we need to update these hoConnections in Metrics ? 1042 it.remove(); 1043 } 1044 1045 // Any non-local disconnects: determine cause 1046 if (mDroppedDuringPoll.size() > 0) { 1047 mCi.getLastCallFailCause( 1048 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); 1049 } 1050 1051 if (needsPollDelay) { 1052 pollCallsAfterDelay(); 1053 } 1054 1055 // Cases when we can no longer keep disconnected Connection's 1056 // with their previous calls 1057 // 1) the phone has started to ring 1058 // 2) A Call/Connection object has changed state... 1059 // we may have switched or held or answered (but not hung up) 1060 if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) { 1061 internalClearDisconnected(); 1062 } 1063 1064 if (VDBG) log("handlePollCalls calling updatePhoneState()"); 1065 updatePhoneState(); 1066 1067 if (unknownConnectionAppeared) { 1068 if (isPhoneTypeGsm()) { 1069 for (Connection c : newUnknownConnectionsGsm) { 1070 log("Notify unknown for " + c); 1071 mPhone.notifyUnknownConnection(c); 1072 } 1073 } else { 1074 mPhone.notifyUnknownConnection(newUnknownConnectionCdma); 1075 } 1076 } 1077 1078 if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { 1079 mPhone.notifyPreciseCallStateChanged(); 1080 updateMetrics(mConnections); 1081 } 1082 1083 // If all handover connections are mapped during this poll process clean it up 1084 if (handoverConnectionsSize > 0 && mHandoverConnections.size() == 0) { 1085 Phone imsPhone = mPhone.getImsPhone(); 1086 if (imsPhone != null) { 1087 imsPhone.callEndCleanupHandOverCallIfAny(); 1088 } 1089 } 1090 //dumpState(); 1091 } 1092 1093 private void updateMetrics(GsmCdmaConnection []connections) { 1094 ArrayList<GsmCdmaConnection> activeConnections = new ArrayList<>(); 1095 for(GsmCdmaConnection conn : connections) { 1096 if(conn != null) activeConnections.add(conn); 1097 } 1098 mMetrics.writeRilCallList(mPhone.getPhoneId(), activeConnections); 1099 } 1100 1101 private void handleRadioNotAvailable() { 1102 // handlePollCalls will clear out its 1103 // call list when it gets the CommandException 1104 // error result from this 1105 pollCallsWhenSafe(); 1106 } 1107 1108 private void dumpState() { 1109 List l; 1110 1111 Rlog.i(LOG_TAG,"Phone State:" + mState); 1112 1113 Rlog.i(LOG_TAG,"Ringing call: " + mRingingCall.toString()); 1114 1115 l = mRingingCall.getConnections(); 1116 for (int i = 0, s = l.size(); i < s; i++) { 1117 Rlog.i(LOG_TAG,l.get(i).toString()); 1118 } 1119 1120 Rlog.i(LOG_TAG,"Foreground call: " + mForegroundCall.toString()); 1121 1122 l = mForegroundCall.getConnections(); 1123 for (int i = 0, s = l.size(); i < s; i++) { 1124 Rlog.i(LOG_TAG,l.get(i).toString()); 1125 } 1126 1127 Rlog.i(LOG_TAG,"Background call: " + mBackgroundCall.toString()); 1128 1129 l = mBackgroundCall.getConnections(); 1130 for (int i = 0, s = l.size(); i < s; i++) { 1131 Rlog.i(LOG_TAG,l.get(i).toString()); 1132 } 1133 1134 } 1135 1136 //***** Called from GsmCdmaConnection 1137 1138 public void hangup(GsmCdmaConnection conn) throws CallStateException { 1139 if (conn.mOwner != this) { 1140 throw new CallStateException ("GsmCdmaConnection " + conn 1141 + "does not belong to GsmCdmaCallTracker " + this); 1142 } 1143 1144 if (conn == mPendingMO) { 1145 // We're hanging up an outgoing call that doesn't have it's 1146 // GsmCdma index assigned yet 1147 1148 if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); 1149 mHangupPendingMO = true; 1150 } else if (!isPhoneTypeGsm() 1151 && conn.getCall() == mRingingCall 1152 && mRingingCall.getState() == GsmCdmaCall.State.WAITING) { 1153 // Handle call waiting hang up case. 1154 // 1155 // The ringingCall state will change to IDLE in GsmCdmaCall.detach 1156 // if the ringing call connection size is 0. We don't specifically 1157 // set the ringing call state to IDLE here to avoid a race condition 1158 // where a new call waiting could get a hang up from an old call 1159 // waiting ringingCall. 1160 // 1161 // PhoneApp does the call log itself since only PhoneApp knows 1162 // the hangup reason is user ignoring or timing out. So conn.onDisconnect() 1163 // is not called here. Instead, conn.onLocalDisconnect() is called. 1164 conn.onLocalDisconnect(); 1165 1166 updatePhoneState(); 1167 mPhone.notifyPreciseCallStateChanged(); 1168 return; 1169 } else { 1170 try { 1171 mMetrics.writeRilHangup(mPhone.getPhoneId(), conn, conn.getGsmCdmaIndex()); 1172 mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage()); 1173 } catch (CallStateException ex) { 1174 // Ignore "connection not found" 1175 // Call may have hung up already 1176 Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: hangup() on absent connection " 1177 + conn); 1178 } 1179 } 1180 1181 conn.onHangupLocal(); 1182 } 1183 1184 public void separate(GsmCdmaConnection conn) throws CallStateException { 1185 if (conn.mOwner != this) { 1186 throw new CallStateException ("GsmCdmaConnection " + conn 1187 + "does not belong to GsmCdmaCallTracker " + this); 1188 } 1189 try { 1190 mCi.separateConnection (conn.getGsmCdmaIndex(), 1191 obtainCompleteMessage(EVENT_SEPARATE_RESULT)); 1192 } catch (CallStateException ex) { 1193 // Ignore "connection not found" 1194 // Call may have hung up already 1195 Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: separate() on absent connection " + conn); 1196 } 1197 } 1198 1199 //***** Called from GsmCdmaPhone 1200 1201 public void setMute(boolean mute) { 1202 mDesiredMute = mute; 1203 mCi.setMute(mDesiredMute, null); 1204 } 1205 1206 public boolean getMute() { 1207 return mDesiredMute; 1208 } 1209 1210 1211 //***** Called from GsmCdmaCall 1212 1213 public void hangup(GsmCdmaCall call) throws CallStateException { 1214 if (call.getConnections().size() == 0) { 1215 throw new CallStateException("no connections in call"); 1216 } 1217 1218 if (call == mRingingCall) { 1219 if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); 1220 logHangupEvent(call); 1221 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 1222 } else if (call == mForegroundCall) { 1223 if (call.isDialingOrAlerting()) { 1224 if (Phone.DEBUG_PHONE) { 1225 log("(foregnd) hangup dialing or alerting..."); 1226 } 1227 hangup((GsmCdmaConnection)(call.getConnections().get(0))); 1228 } else if (isPhoneTypeGsm() 1229 && mRingingCall.isRinging()) { 1230 // Do not auto-answer ringing on CHUP, instead just end active calls 1231 log("hangup all conns in active/background call, without affecting ringing call"); 1232 hangupAllConnections(call); 1233 } else { 1234 logHangupEvent(call); 1235 hangupForegroundResumeBackground(); 1236 } 1237 } else if (call == mBackgroundCall) { 1238 if (mRingingCall.isRinging()) { 1239 if (Phone.DEBUG_PHONE) { 1240 log("hangup all conns in background call"); 1241 } 1242 hangupAllConnections(call); 1243 } else { 1244 hangupWaitingOrBackground(); 1245 } 1246 } else { 1247 throw new RuntimeException ("GsmCdmaCall " + call + 1248 "does not belong to GsmCdmaCallTracker " + this); 1249 } 1250 1251 call.onHangupLocal(); 1252 mPhone.notifyPreciseCallStateChanged(); 1253 } 1254 1255 private void logHangupEvent(GsmCdmaCall call) { 1256 int count = call.mConnections.size(); 1257 for (int i = 0; i < count; i++) { 1258 GsmCdmaConnection cn = (GsmCdmaConnection) call.mConnections.get(i); 1259 int call_index; 1260 try { 1261 call_index = cn.getGsmCdmaIndex(); 1262 } catch (CallStateException ex) { 1263 call_index = -1; 1264 } 1265 mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, call_index); 1266 } 1267 if (VDBG) Rlog.v(LOG_TAG, "logHangupEvent logged " + count + " Connections "); 1268 } 1269 1270 public void hangupWaitingOrBackground() { 1271 if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); 1272 logHangupEvent(mBackgroundCall); 1273 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 1274 } 1275 1276 public void hangupForegroundResumeBackground() { 1277 if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); 1278 mCi.hangupForegroundResumeBackground(obtainCompleteMessage()); 1279 } 1280 1281 public void hangupConnectionByIndex(GsmCdmaCall call, int index) 1282 throws CallStateException { 1283 int count = call.mConnections.size(); 1284 for (int i = 0; i < count; i++) { 1285 GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i); 1286 if (cn.getGsmCdmaIndex() == index) { 1287 mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex()); 1288 mCi.hangupConnection(index, obtainCompleteMessage()); 1289 return; 1290 } 1291 } 1292 1293 throw new CallStateException("no GsmCdma index found"); 1294 } 1295 1296 public void hangupAllConnections(GsmCdmaCall call) { 1297 try { 1298 int count = call.mConnections.size(); 1299 for (int i = 0; i < count; i++) { 1300 GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i); 1301 mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex()); 1302 mCi.hangupConnection(cn.getGsmCdmaIndex(), obtainCompleteMessage()); 1303 } 1304 } catch (CallStateException ex) { 1305 Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); 1306 } 1307 } 1308 1309 public GsmCdmaConnection getConnectionByIndex(GsmCdmaCall call, int index) 1310 throws CallStateException { 1311 int count = call.mConnections.size(); 1312 for (int i = 0; i < count; i++) { 1313 GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i); 1314 if (cn.getGsmCdmaIndex() == index) { 1315 return cn; 1316 } 1317 } 1318 1319 return null; 1320 } 1321 1322 //CDMA 1323 private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) { 1324 if (mCallWaitingRegistrants != null) { 1325 mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null)); 1326 } 1327 } 1328 1329 //CDMA 1330 private void handleCallWaitingInfo(CdmaCallWaitingNotification cw) { 1331 // Create a new GsmCdmaConnection which attaches itself to ringingCall. 1332 new GsmCdmaConnection(mPhone.getContext(), cw, this, mRingingCall); 1333 updatePhoneState(); 1334 1335 // Finally notify application 1336 notifyCallWaitingInfo(cw); 1337 } 1338 1339 private Phone.SuppService getFailedService(int what) { 1340 switch (what) { 1341 case EVENT_SWITCH_RESULT: 1342 return Phone.SuppService.SWITCH; 1343 case EVENT_CONFERENCE_RESULT: 1344 return Phone.SuppService.CONFERENCE; 1345 case EVENT_SEPARATE_RESULT: 1346 return Phone.SuppService.SEPARATE; 1347 case EVENT_ECT_RESULT: 1348 return Phone.SuppService.TRANSFER; 1349 } 1350 return Phone.SuppService.UNKNOWN; 1351 } 1352 1353 //****** Overridden from Handler 1354 1355 @Override 1356 public void handleMessage(Message msg) { 1357 AsyncResult ar; 1358 1359 switch (msg.what) { 1360 case EVENT_POLL_CALLS_RESULT: 1361 Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received"); 1362 1363 if (msg == mLastRelevantPoll) { 1364 if (DBG_POLL) log( 1365 "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); 1366 mNeedsPoll = false; 1367 mLastRelevantPoll = null; 1368 handlePollCalls((AsyncResult)msg.obj); 1369 } 1370 break; 1371 1372 case EVENT_OPERATION_COMPLETE: 1373 operationComplete(); 1374 break; 1375 1376 case EVENT_CONFERENCE_RESULT: 1377 if (isPhoneTypeGsm()) { 1378 // The conference merge failed, so notify listeners. Ultimately this bubbles up 1379 // to Telecom, which will inform the InCall UI of the failure. 1380 Connection connection = mForegroundCall.getLatestConnection(); 1381 if (connection != null) { 1382 connection.onConferenceMergeFailed(); 1383 } 1384 } 1385 // fall through 1386 case EVENT_SEPARATE_RESULT: 1387 case EVENT_ECT_RESULT: 1388 case EVENT_SWITCH_RESULT: 1389 if (isPhoneTypeGsm()) { 1390 ar = (AsyncResult) msg.obj; 1391 if (ar.exception != null) { 1392 mPhone.notifySuppServiceFailed(getFailedService(msg.what)); 1393 } 1394 operationComplete(); 1395 } else { 1396 if (msg.what != EVENT_SWITCH_RESULT) { 1397 // EVENT_SWITCH_RESULT in GSM call triggers operationComplete() which gets 1398 // the current call list. But in CDMA there is no list so there is nothing 1399 // to do. Other messages however are not expected in CDMA. 1400 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1401 "phone type " + mPhone.getPhoneType()); 1402 } 1403 } 1404 break; 1405 1406 case EVENT_GET_LAST_CALL_FAIL_CAUSE: 1407 int causeCode; 1408 String vendorCause = null; 1409 ar = (AsyncResult)msg.obj; 1410 1411 operationComplete(); 1412 1413 if (ar.exception != null) { 1414 // An exception occurred...just treat the disconnect 1415 // cause as "normal" 1416 causeCode = CallFailCause.NORMAL_CLEARING; 1417 Rlog.i(LOG_TAG, 1418 "Exception during getLastCallFailCause, assuming normal disconnect"); 1419 } else { 1420 LastCallFailCause failCause = (LastCallFailCause)ar.result; 1421 causeCode = failCause.causeCode; 1422 vendorCause = failCause.vendorCause; 1423 } 1424 // Log the causeCode if its not normal 1425 if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || 1426 causeCode == CallFailCause.TEMPORARY_FAILURE || 1427 causeCode == CallFailCause.SWITCHING_CONGESTION || 1428 causeCode == CallFailCause.CHANNEL_NOT_AVAIL || 1429 causeCode == CallFailCause.QOS_NOT_AVAIL || 1430 causeCode == CallFailCause.BEARER_NOT_AVAIL || 1431 causeCode == CallFailCause.ERROR_UNSPECIFIED) { 1432 1433 CellLocation loc = mPhone.getCellLocation(); 1434 int cid = -1; 1435 if (loc != null) { 1436 if (isPhoneTypeGsm()) { 1437 cid = ((GsmCellLocation)loc).getCid(); 1438 } else { 1439 cid = ((CdmaCellLocation)loc).getBaseStationId(); 1440 } 1441 } 1442 EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, cid, 1443 TelephonyManager.getDefault().getNetworkType()); 1444 } 1445 1446 for (int i = 0, s = mDroppedDuringPoll.size(); i < s ; i++) { 1447 GsmCdmaConnection conn = mDroppedDuringPoll.get(i); 1448 1449 conn.onRemoteDisconnect(causeCode, vendorCause); 1450 } 1451 1452 updatePhoneState(); 1453 1454 mPhone.notifyPreciseCallStateChanged(); 1455 mMetrics.writeRilCallList(mPhone.getPhoneId(), mDroppedDuringPoll); 1456 mDroppedDuringPoll.clear(); 1457 break; 1458 1459 case EVENT_REPOLL_AFTER_DELAY: 1460 case EVENT_CALL_STATE_CHANGE: 1461 pollCallsWhenSafe(); 1462 break; 1463 1464 case EVENT_RADIO_AVAILABLE: 1465 handleRadioAvailable(); 1466 break; 1467 1468 case EVENT_RADIO_NOT_AVAILABLE: 1469 handleRadioNotAvailable(); 1470 break; 1471 1472 case EVENT_EXIT_ECM_RESPONSE_CDMA: 1473 if (!isPhoneTypeGsm()) { 1474 // no matter the result, we still do the same here 1475 if (mPendingCallInEcm) { 1476 mCi.dial(mPendingMO.getAddress(), mPendingCallClirMode, obtainCompleteMessage()); 1477 mPendingCallInEcm = false; 1478 } 1479 mPhone.unsetOnEcbModeExitResponse(this); 1480 } else { 1481 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1482 "phone type " + mPhone.getPhoneType()); 1483 } 1484 break; 1485 1486 case EVENT_CALL_WAITING_INFO_CDMA: 1487 if (!isPhoneTypeGsm()) { 1488 ar = (AsyncResult)msg.obj; 1489 if (ar.exception == null) { 1490 handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); 1491 Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); 1492 } 1493 } else { 1494 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1495 "phone type " + mPhone.getPhoneType()); 1496 } 1497 break; 1498 1499 case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: 1500 if (!isPhoneTypeGsm()) { 1501 ar = (AsyncResult)msg.obj; 1502 if (ar.exception == null) { 1503 // Assume 3 way call is connected 1504 mPendingMO.onConnectedInOrOut(); 1505 mPendingMO = null; 1506 } 1507 } else { 1508 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1509 "phone type " + mPhone.getPhoneType()); 1510 } 1511 break; 1512 1513 case EVENT_THREE_WAY_DIAL_BLANK_FLASH: 1514 if (!isPhoneTypeGsm()) { 1515 ar = (AsyncResult) msg.obj; 1516 if (ar.exception == null) { 1517 postDelayed( 1518 new Runnable() { 1519 public void run() { 1520 if (mPendingMO != null) { 1521 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 1522 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 1523 } 1524 } 1525 }, m3WayCallFlashDelay); 1526 } else { 1527 mPendingMO = null; 1528 Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call"); 1529 } 1530 } else { 1531 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1532 "phone type " + mPhone.getPhoneType()); 1533 } 1534 break; 1535 1536 default:{ 1537 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1538 "phone type " + mPhone.getPhoneType()); 1539 } 1540 } 1541 } 1542 1543 //CDMA 1544 /** 1545 * Check and enable data call after an emergency call is dropped if it's 1546 * not in ECM 1547 */ 1548 private void checkAndEnableDataCallAfterEmergencyCallDropped() { 1549 if (mIsInEmergencyCall) { 1550 mIsInEmergencyCall = false; 1551 String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1552 if (Phone.DEBUG_PHONE) { 1553 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm); 1554 } 1555 if (inEcm.compareTo("false") == 0) { 1556 // Re-initiate data connection 1557 mPhone.mDcTracker.setInternalDataEnabled(true); 1558 mPhone.notifyEmergencyCallRegistrants(false); 1559 } 1560 mPhone.sendEmergencyCallStateChange(false); 1561 } 1562 } 1563 1564 /** 1565 * Check the MT call to see if it's a new ring or 1566 * a unknown connection. 1567 */ 1568 private Connection checkMtFindNewRinging(DriverCall dc, int i) { 1569 1570 Connection newRinging = null; 1571 1572 // it's a ringing call 1573 if (mConnections[i].getCall() == mRingingCall) { 1574 newRinging = mConnections[i]; 1575 if (Phone.DEBUG_PHONE) log("Notify new ring " + dc); 1576 } else { 1577 // Something strange happened: a call which is neither 1578 // a ringing call nor the one we created. It could be the 1579 // call collision result from RIL 1580 Rlog.e(LOG_TAG,"Phantom call appeared " + dc); 1581 // If it's a connected call, set the connect time so that 1582 // it's non-zero. It may not be accurate, but at least 1583 // it won't appear as a Missed Call. 1584 if (dc.state != DriverCall.State.ALERTING 1585 && dc.state != DriverCall.State.DIALING) { 1586 mConnections[i].onConnectedInOrOut(); 1587 if (dc.state == DriverCall.State.HOLDING) { 1588 // We've transitioned into HOLDING 1589 mConnections[i].onStartedHolding(); 1590 } 1591 } 1592 } 1593 return newRinging; 1594 } 1595 1596 //CDMA 1597 /** 1598 * Check if current call is in emergency call 1599 * 1600 * @return true if it is in emergency call 1601 * false if it is not in emergency call 1602 */ 1603 public boolean isInEmergencyCall() { 1604 return mIsInEmergencyCall; 1605 } 1606 1607 private boolean isPhoneTypeGsm() { 1608 return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM; 1609 } 1610 1611 public GsmCdmaPhone getPhone() { 1612 return mPhone; 1613 } 1614 1615 @Override 1616 protected void log(String msg) { 1617 Rlog.d(LOG_TAG, "[GsmCdmaCallTracker] " + msg); 1618 } 1619 1620 @Override 1621 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1622 pw.println("GsmCdmaCallTracker extends:"); 1623 super.dump(fd, pw, args); 1624 pw.println("mConnections: length=" + mConnections.length); 1625 for(int i=0; i < mConnections.length; i++) { 1626 pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]); 1627 } 1628 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1629 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1630 if (!isPhoneTypeGsm()) { 1631 pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants); 1632 } 1633 pw.println(" mDroppedDuringPoll: size=" + mDroppedDuringPoll.size()); 1634 for(int i = 0; i < mDroppedDuringPoll.size(); i++) { 1635 pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i)); 1636 } 1637 pw.println(" mRingingCall=" + mRingingCall); 1638 pw.println(" mForegroundCall=" + mForegroundCall); 1639 pw.println(" mBackgroundCall=" + mBackgroundCall); 1640 pw.println(" mPendingMO=" + mPendingMO); 1641 pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1642 pw.println(" mPhone=" + mPhone); 1643 pw.println(" mDesiredMute=" + mDesiredMute); 1644 pw.println(" mState=" + mState); 1645 if (!isPhoneTypeGsm()) { 1646 pw.println(" mPendingCallInEcm=" + mPendingCallInEcm); 1647 pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall); 1648 pw.println(" mPendingCallClirMode=" + mPendingCallClirMode); 1649 pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled); 1650 } 1651 1652 } 1653 1654 @Override 1655 public PhoneConstants.State getState() { 1656 return mState; 1657 } 1658 1659 public int getMaxConnectionsPerCall() { 1660 return mPhone.isPhoneTypeGsm() ? 1661 MAX_CONNECTIONS_PER_CALL_GSM : 1662 MAX_CONNECTIONS_PER_CALL_CDMA; 1663 } 1664} 1665