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