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 dummy 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 881 Connection hoConnection = getHoConnection(dc); 882 if (hoConnection != null) { 883 // Single Radio Voice Call Continuity (SRVCC) completed 884 mConnections[i].migrateFrom(hoConnection); 885 // Updating connect time for silent redial cases (ex: Calls are transferred 886 // from DIALING/ALERTING/INCOMING/WAITING to ACTIVE) 887 if (hoConnection.mPreHandoverState != GsmCdmaCall.State.ACTIVE && 888 hoConnection.mPreHandoverState != GsmCdmaCall.State.HOLDING && 889 dc.state == DriverCall.State.ACTIVE) { 890 mConnections[i].onConnectedInOrOut(); 891 } else { 892 mConnections[i].onConnectedConnectionMigrated(); 893 } 894 895 mHandoverConnections.remove(hoConnection); 896 897 if (isPhoneTypeGsm()) { 898 for (Iterator<Connection> it = mHandoverConnections.iterator(); 899 it.hasNext(); ) { 900 Connection c = it.next(); 901 Rlog.i(LOG_TAG, "HO Conn state is " + c.mPreHandoverState); 902 if (c.mPreHandoverState == mConnections[i].getState()) { 903 Rlog.i(LOG_TAG, "Removing HO conn " 904 + hoConnection + c.mPreHandoverState); 905 it.remove(); 906 } 907 } 908 } 909 910 mPhone.notifyHandoverStateChanged(mConnections[i]); 911 } else { 912 // find if the MT call is a new ring or unknown connection 913 newRinging = checkMtFindNewRinging(dc,i); 914 if (newRinging == null) { 915 unknownConnectionAppeared = true; 916 if (isPhoneTypeGsm()) { 917 newUnknownConnectionsGsm.add(mConnections[i]); 918 } else { 919 newUnknownConnectionCdma = mConnections[i]; 920 } 921 } 922 } 923 } 924 hasNonHangupStateChanged = true; 925 } else if (conn != null && dc == null) { 926 if (isPhoneTypeGsm()) { 927 // Connection missing in CLCC response that we were 928 // tracking. 929 mDroppedDuringPoll.add(conn); 930 } else { 931 // This case means the RIL has no more active call anymore and 932 // we need to clean up the foregroundCall and ringingCall. 933 // Loop through foreground call connections as 934 // it contains the known logical connections. 935 ArrayList<Connection> connections = mForegroundCall.getConnections(); 936 for (Connection cn : connections) { 937 if (Phone.DEBUG_PHONE) { 938 log("adding fgCall cn " + cn + "to droppedDuringPoll"); 939 } 940 mDroppedDuringPoll.add((GsmCdmaConnection) cn); 941 } 942 943 connections = mRingingCall.getConnections(); 944 // Loop through ringing call connections as 945 // it may contain the known logical connections. 946 for (Connection cn : connections) { 947 if (Phone.DEBUG_PHONE) { 948 log("adding rgCall cn " + cn + "to droppedDuringPoll"); 949 } 950 mDroppedDuringPoll.add((GsmCdmaConnection) cn); 951 } 952 953 // Re-start Ecm timer when the connected emergency call ends 954 if (mPhone.isEcmCanceledForEmergency()) { 955 mPhone.handleTimerInEmergencyCallbackMode(GsmCdmaPhone.RESTART_ECM_TIMER); 956 } 957 // If emergency call is not going through while dialing 958 checkAndEnableDataCallAfterEmergencyCallDropped(); 959 } 960 // Dropped connections are removed from the CallTracker 961 // list but kept in the Call list 962 mConnections[i] = null; 963 } else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) { 964 // Connection in CLCC response does not match what 965 // we were tracking. Assume dropped call and new call 966 967 mDroppedDuringPoll.add(conn); 968 mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i); 969 970 if (mConnections[i].getCall() == mRingingCall) { 971 newRinging = mConnections[i]; 972 } // else something strange happened 973 hasNonHangupStateChanged = true; 974 } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ 975 // Call collision case 976 if (!isPhoneTypeGsm() && conn.isIncoming() != dc.isMT) { 977 if (dc.isMT == true) { 978 // Mt call takes precedence than Mo,drops Mo 979 mDroppedDuringPoll.add(conn); 980 // find if the MT call is a new ring or unknown connection 981 newRinging = checkMtFindNewRinging(dc,i); 982 if (newRinging == null) { 983 unknownConnectionAppeared = true; 984 newUnknownConnectionCdma = conn; 985 } 986 checkAndEnableDataCallAfterEmergencyCallDropped(); 987 } else { 988 // Call info stored in conn is not consistent with the call info from dc. 989 // We should follow the rule of MT calls taking precedence over MO calls 990 // when there is conflict, so here we drop the call info from dc and 991 // continue to use the call info from conn, and only take a log. 992 Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc); 993 } 994 } else { 995 boolean changed; 996 changed = conn.update(dc); 997 hasNonHangupStateChanged = hasNonHangupStateChanged || changed; 998 } 999 } 1000 1001 if (REPEAT_POLLING) { 1002 if (dc != null) { 1003 // FIXME with RIL, we should not need this anymore 1004 if ((dc.state == DriverCall.State.DIALING 1005 /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) 1006 || (dc.state == DriverCall.State.ALERTING 1007 /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) 1008 || (dc.state == DriverCall.State.INCOMING 1009 /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) 1010 || (dc.state == DriverCall.State.WAITING 1011 /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)) { 1012 // Sometimes there's no unsolicited notification 1013 // for state transitions 1014 needsPollDelay = true; 1015 } 1016 } 1017 } 1018 } 1019 1020 // Safety check so that obj is not stuck with mIsInEmergencyCall set to true (and data 1021 // disabled). This should never happen though. 1022 if (!isPhoneTypeGsm() && noConnectionExists) { 1023 checkAndEnableDataCallAfterEmergencyCallDropped(); 1024 } 1025 1026 // This is the first poll after an ATD. 1027 // We expect the pending call to appear in the list 1028 // If it does not, we land here 1029 if (mPendingMO != null) { 1030 Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:" 1031 + mForegroundCall.getState()); 1032 1033 mDroppedDuringPoll.add(mPendingMO); 1034 mPendingMO = null; 1035 mHangupPendingMO = false; 1036 1037 if (!isPhoneTypeGsm()) { 1038 if( mPendingCallInEcm) { 1039 mPendingCallInEcm = false; 1040 } 1041 checkAndEnableDataCallAfterEmergencyCallDropped(); 1042 } 1043 } 1044 1045 if (newRinging != null) { 1046 mPhone.notifyNewRingingConnection(newRinging); 1047 } 1048 1049 // clear the "local hangup" and "missed/rejected call" 1050 // cases from the "dropped during poll" list 1051 // These cases need no "last call fail" reason 1052 ArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>(); 1053 for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) { 1054 GsmCdmaConnection conn = mDroppedDuringPoll.get(i); 1055 //CDMA 1056 boolean wasDisconnected = false; 1057 1058 if (conn.isIncoming() && conn.getConnectTime() == 0) { 1059 // Missed or rejected call 1060 int cause; 1061 if (conn.mCause == DisconnectCause.LOCAL) { 1062 cause = DisconnectCause.INCOMING_REJECTED; 1063 } else { 1064 cause = DisconnectCause.INCOMING_MISSED; 1065 } 1066 1067 if (Phone.DEBUG_PHONE) { 1068 log("missed/rejected call, conn.cause=" + conn.mCause); 1069 log("setting cause to " + cause); 1070 } 1071 mDroppedDuringPoll.remove(i); 1072 hasAnyCallDisconnected |= conn.onDisconnect(cause); 1073 wasDisconnected = true; 1074 locallyDisconnectedConnections.add(conn); 1075 } else if (conn.mCause == DisconnectCause.LOCAL 1076 || conn.mCause == DisconnectCause.INVALID_NUMBER) { 1077 mDroppedDuringPoll.remove(i); 1078 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); 1079 wasDisconnected = true; 1080 locallyDisconnectedConnections.add(conn); 1081 } 1082 1083 if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared 1084 && conn == newUnknownConnectionCdma) { 1085 unknownConnectionAppeared = false; 1086 newUnknownConnectionCdma = null; 1087 } 1088 } 1089 1090 if (locallyDisconnectedConnections.size() > 0) { 1091 mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections, 1092 getNetworkCountryIso()); 1093 mPhone.getVoiceCallSessionStats().onRilCallListChanged(locallyDisconnectedConnections); 1094 } 1095 1096 /* Disconnect any pending Handover connections */ 1097 for (Iterator<Connection> it = mHandoverConnections.iterator(); 1098 it.hasNext();) { 1099 Connection hoConnection = it.next(); 1100 log("handlePollCalls - disconnect hoConn= " + hoConnection + 1101 " hoConn.State= " + hoConnection.getState()); 1102 if (hoConnection.getState().isRinging()) { 1103 hoConnection.onDisconnect(DisconnectCause.INCOMING_MISSED); 1104 } else { 1105 hoConnection.onDisconnect(DisconnectCause.NOT_VALID); 1106 } 1107 // TODO: Do we need to update these hoConnections in Metrics ? 1108 it.remove(); 1109 } 1110 1111 // Any non-local disconnects: determine cause 1112 if (mDroppedDuringPoll.size() > 0) { 1113 mCi.getLastCallFailCause( 1114 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); 1115 } 1116 1117 if (needsPollDelay) { 1118 pollCallsAfterDelay(); 1119 } 1120 1121 // Cases when we can no longer keep disconnected Connection's 1122 // with their previous calls 1123 // 1) the phone has started to ring 1124 // 2) A Call/Connection object has changed state... 1125 // we may have switched or held or answered (but not hung up) 1126 if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) { 1127 internalClearDisconnected(); 1128 } 1129 1130 if (VDBG) log("handlePollCalls calling updatePhoneState()"); 1131 updatePhoneState(); 1132 1133 if (unknownConnectionAppeared) { 1134 if (isPhoneTypeGsm()) { 1135 for (Connection c : newUnknownConnectionsGsm) { 1136 log("Notify unknown for " + c); 1137 mPhone.notifyUnknownConnection(c); 1138 } 1139 } else { 1140 mPhone.notifyUnknownConnection(newUnknownConnectionCdma); 1141 } 1142 } 1143 1144 if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { 1145 mPhone.notifyPreciseCallStateChanged(); 1146 updateMetrics(mConnections); 1147 } 1148 1149 // If all handover connections are mapped during this poll process clean it up 1150 if (handoverConnectionsSize > 0 && mHandoverConnections.size() == 0) { 1151 Phone imsPhone = mPhone.getImsPhone(); 1152 if (imsPhone != null) { 1153 imsPhone.callEndCleanupHandOverCallIfAny(); 1154 } 1155 } 1156 //dumpState(); 1157 } 1158 updateMetrics(GsmCdmaConnection[] connections)1159 private void updateMetrics(GsmCdmaConnection[] connections) { 1160 ArrayList<GsmCdmaConnection> activeConnections = new ArrayList<>(); 1161 for (GsmCdmaConnection conn : connections) { 1162 if (conn != null) activeConnections.add(conn); 1163 } 1164 mMetrics.writeRilCallList(mPhone.getPhoneId(), activeConnections, getNetworkCountryIso()); 1165 mPhone.getVoiceCallSessionStats().onRilCallListChanged(activeConnections); 1166 } 1167 handleRadioNotAvailable()1168 private void handleRadioNotAvailable() { 1169 // handlePollCalls will clear out its 1170 // call list when it gets the CommandException 1171 // error result from this 1172 pollCallsWhenSafe(); 1173 } 1174 dumpState()1175 private void dumpState() { 1176 List l; 1177 1178 Rlog.i(LOG_TAG,"Phone State:" + mState); 1179 1180 Rlog.i(LOG_TAG,"Ringing call: " + mRingingCall.toString()); 1181 1182 l = mRingingCall.getConnections(); 1183 for (int i = 0, s = l.size(); i < s; i++) { 1184 Rlog.i(LOG_TAG,l.get(i).toString()); 1185 } 1186 1187 Rlog.i(LOG_TAG,"Foreground call: " + mForegroundCall.toString()); 1188 1189 l = mForegroundCall.getConnections(); 1190 for (int i = 0, s = l.size(); i < s; i++) { 1191 Rlog.i(LOG_TAG,l.get(i).toString()); 1192 } 1193 1194 Rlog.i(LOG_TAG,"Background call: " + mBackgroundCall.toString()); 1195 1196 l = mBackgroundCall.getConnections(); 1197 for (int i = 0, s = l.size(); i < s; i++) { 1198 Rlog.i(LOG_TAG,l.get(i).toString()); 1199 } 1200 1201 } 1202 1203 //***** Called from GsmCdmaConnection 1204 hangup(GsmCdmaConnection conn)1205 public void hangup(GsmCdmaConnection conn) throws CallStateException { 1206 if (conn.mOwner != this) { 1207 throw new CallStateException ("GsmCdmaConnection " + conn 1208 + "does not belong to GsmCdmaCallTracker " + this); 1209 } 1210 1211 if (conn == mPendingMO) { 1212 // We're hanging up an outgoing call that doesn't have it's 1213 // GsmCdma index assigned yet 1214 1215 if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); 1216 mHangupPendingMO = true; 1217 } else if (!isPhoneTypeGsm() 1218 && conn.getCall() == mRingingCall 1219 && mRingingCall.getState() == GsmCdmaCall.State.WAITING) { 1220 // Handle call waiting hang up case. 1221 // 1222 // The ringingCall state will change to IDLE in GsmCdmaCall.detach 1223 // if the ringing call connection size is 0. We don't specifically 1224 // set the ringing call state to IDLE here to avoid a race condition 1225 // where a new call waiting could get a hang up from an old call 1226 // waiting ringingCall. 1227 // 1228 // PhoneApp does the call log itself since only PhoneApp knows 1229 // the hangup reason is user ignoring or timing out. So conn.onDisconnect() 1230 // is not called here. Instead, conn.onLocalDisconnect() is called. 1231 conn.onLocalDisconnect(); 1232 1233 updatePhoneState(); 1234 mPhone.notifyPreciseCallStateChanged(); 1235 return; 1236 } else { 1237 try { 1238 mMetrics.writeRilHangup(mPhone.getPhoneId(), conn, conn.getGsmCdmaIndex(), 1239 getNetworkCountryIso()); 1240 mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage()); 1241 } catch (CallStateException ex) { 1242 // Ignore "connection not found" 1243 // Call may have hung up already 1244 Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: hangup() on absent connection " 1245 + conn); 1246 } 1247 } 1248 1249 conn.onHangupLocal(); 1250 } 1251 separate(GsmCdmaConnection conn)1252 public void separate(GsmCdmaConnection conn) throws CallStateException { 1253 if (conn.mOwner != this) { 1254 throw new CallStateException ("GsmCdmaConnection " + conn 1255 + "does not belong to GsmCdmaCallTracker " + this); 1256 } 1257 try { 1258 mCi.separateConnection (conn.getGsmCdmaIndex(), 1259 obtainCompleteMessage(EVENT_SEPARATE_RESULT)); 1260 } catch (CallStateException ex) { 1261 // Ignore "connection not found" 1262 // Call may have hung up already 1263 Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: separate() on absent connection " + conn); 1264 } 1265 } 1266 1267 //***** Called from GsmCdmaPhone 1268 1269 @UnsupportedAppUsage setMute(boolean mute)1270 public void setMute(boolean mute) { 1271 mDesiredMute = mute; 1272 mCi.setMute(mDesiredMute, null); 1273 } 1274 getMute()1275 public boolean getMute() { 1276 return mDesiredMute; 1277 } 1278 1279 1280 //***** Called from GsmCdmaCall 1281 hangup(GsmCdmaCall call)1282 public void hangup(GsmCdmaCall call) throws CallStateException { 1283 if (call.getConnectionsCount() == 0) { 1284 throw new CallStateException("no connections in call"); 1285 } 1286 1287 if (call == mRingingCall) { 1288 if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); 1289 logHangupEvent(call); 1290 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 1291 } else if (call == mForegroundCall) { 1292 if (call.isDialingOrAlerting()) { 1293 if (Phone.DEBUG_PHONE) { 1294 log("(foregnd) hangup dialing or alerting..."); 1295 } 1296 hangup((GsmCdmaConnection)(call.getConnections().get(0))); 1297 } else if (isPhoneTypeGsm() 1298 && mRingingCall.isRinging()) { 1299 // Do not auto-answer ringing on CHUP, instead just end active calls 1300 log("hangup all conns in active/background call, without affecting ringing call"); 1301 hangupAllConnections(call); 1302 } else { 1303 logHangupEvent(call); 1304 hangupForegroundResumeBackground(); 1305 } 1306 } else if (call == mBackgroundCall) { 1307 if (mRingingCall.isRinging()) { 1308 if (Phone.DEBUG_PHONE) { 1309 log("hangup all conns in background call"); 1310 } 1311 hangupAllConnections(call); 1312 } else { 1313 hangupWaitingOrBackground(); 1314 } 1315 } else { 1316 throw new RuntimeException ("GsmCdmaCall " + call + 1317 "does not belong to GsmCdmaCallTracker " + this); 1318 } 1319 1320 call.onHangupLocal(); 1321 mPhone.notifyPreciseCallStateChanged(); 1322 } 1323 logHangupEvent(GsmCdmaCall call)1324 private void logHangupEvent(GsmCdmaCall call) { 1325 for (Connection conn : call.getConnections()) { 1326 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1327 int call_index; 1328 try { 1329 call_index = c.getGsmCdmaIndex(); 1330 } catch (CallStateException e) { 1331 call_index = -1; 1332 } 1333 mMetrics.writeRilHangup(mPhone.getPhoneId(), c, call_index, getNetworkCountryIso()); 1334 } 1335 if (VDBG) { 1336 Rlog.v(LOG_TAG, "logHangupEvent logged " + call.getConnectionsCount() 1337 + " Connections "); 1338 } 1339 } 1340 hangupWaitingOrBackground()1341 public void hangupWaitingOrBackground() { 1342 if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); 1343 logHangupEvent(mBackgroundCall); 1344 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 1345 } 1346 hangupForegroundResumeBackground()1347 public void hangupForegroundResumeBackground() { 1348 if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); 1349 mCi.hangupForegroundResumeBackground(obtainCompleteMessage()); 1350 } 1351 hangupConnectionByIndex(GsmCdmaCall call, int index)1352 public void hangupConnectionByIndex(GsmCdmaCall call, int index) 1353 throws CallStateException { 1354 for (Connection conn : call.getConnections()) { 1355 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1356 if (!c.mDisconnected && c.getGsmCdmaIndex() == index) { 1357 mMetrics.writeRilHangup(mPhone.getPhoneId(), c, c.getGsmCdmaIndex(), 1358 getNetworkCountryIso()); 1359 mCi.hangupConnection(index, obtainCompleteMessage()); 1360 return; 1361 } 1362 } 1363 throw new CallStateException("no GsmCdma index found"); 1364 } 1365 hangupAllConnections(GsmCdmaCall call)1366 public void hangupAllConnections(GsmCdmaCall call) { 1367 try { 1368 for (Connection conn : call.getConnections()) { 1369 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1370 if (!c.mDisconnected) { 1371 mMetrics.writeRilHangup(mPhone.getPhoneId(), c, c.getGsmCdmaIndex(), 1372 getNetworkCountryIso()); 1373 mCi.hangupConnection(c.getGsmCdmaIndex(), obtainCompleteMessage()); 1374 } 1375 } 1376 } catch (CallStateException ex) { 1377 Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); 1378 } 1379 } 1380 getConnectionByIndex(GsmCdmaCall call, int index)1381 public GsmCdmaConnection getConnectionByIndex(GsmCdmaCall call, int index) 1382 throws CallStateException { 1383 for (Connection conn : call.getConnections()) { 1384 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1385 if (!c.mDisconnected && c.getGsmCdmaIndex() == index) { 1386 return c; 1387 } 1388 } 1389 return null; 1390 } 1391 1392 //CDMA notifyCallWaitingInfo(CdmaCallWaitingNotification obj)1393 private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) { 1394 if (mCallWaitingRegistrants != null) { 1395 mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null)); 1396 } 1397 } 1398 1399 //CDMA handleCallWaitingInfo(CdmaCallWaitingNotification cw)1400 private void handleCallWaitingInfo(CdmaCallWaitingNotification cw) { 1401 // Create a new GsmCdmaConnection which attaches itself to ringingCall. 1402 new GsmCdmaConnection(mPhone.getContext(), cw, this, mRingingCall); 1403 updatePhoneState(); 1404 1405 // Finally notify application 1406 notifyCallWaitingInfo(cw); 1407 } 1408 getFailedService(int what)1409 private Phone.SuppService getFailedService(int what) { 1410 switch (what) { 1411 case EVENT_SWITCH_RESULT: 1412 return Phone.SuppService.SWITCH; 1413 case EVENT_CONFERENCE_RESULT: 1414 return Phone.SuppService.CONFERENCE; 1415 case EVENT_SEPARATE_RESULT: 1416 return Phone.SuppService.SEPARATE; 1417 case EVENT_ECT_RESULT: 1418 return Phone.SuppService.TRANSFER; 1419 } 1420 return Phone.SuppService.UNKNOWN; 1421 } 1422 1423 //****** Overridden from Handler 1424 1425 @Override handleMessage(Message msg)1426 public void handleMessage(Message msg) { 1427 AsyncResult ar; 1428 1429 switch (msg.what) { 1430 case EVENT_POLL_CALLS_RESULT: 1431 Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received"); 1432 1433 if (msg == mLastRelevantPoll) { 1434 if (DBG_POLL) log( 1435 "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); 1436 mNeedsPoll = false; 1437 mLastRelevantPoll = null; 1438 handlePollCalls((AsyncResult)msg.obj); 1439 } 1440 break; 1441 1442 case EVENT_OPERATION_COMPLETE: 1443 operationComplete(); 1444 break; 1445 1446 case EVENT_CONFERENCE_RESULT: 1447 if (isPhoneTypeGsm()) { 1448 ar = (AsyncResult) msg.obj; 1449 if (ar.exception != null) { 1450 // The conference merge failed, so notify listeners. Ultimately this 1451 // bubbles up to Telecom, which will inform the InCall UI of the failure. 1452 Connection connection = mForegroundCall.getLatestConnection(); 1453 if (connection != null) { 1454 connection.onConferenceMergeFailed(); 1455 } 1456 } 1457 } 1458 // fall through 1459 case EVENT_SEPARATE_RESULT: 1460 case EVENT_ECT_RESULT: 1461 case EVENT_SWITCH_RESULT: 1462 if (isPhoneTypeGsm()) { 1463 ar = (AsyncResult) msg.obj; 1464 if (ar.exception != null) { 1465 if (msg.what == EVENT_SWITCH_RESULT) { 1466 Connection connection = mForegroundCall.getLatestConnection(); 1467 if (connection != null) { 1468 if (mBackgroundCall.getState() != GsmCdmaCall.State.HOLDING) { 1469 connection.onConnectionEvent( 1470 android.telecom.Connection.EVENT_CALL_HOLD_FAILED, 1471 null); 1472 } else { 1473 connection.onConnectionEvent( 1474 android.telecom.Connection.EVENT_CALL_SWITCH_FAILED, 1475 null); 1476 } 1477 } 1478 } 1479 mPhone.notifySuppServiceFailed(getFailedService(msg.what)); 1480 } 1481 operationComplete(); 1482 } else { 1483 if (msg.what != EVENT_SWITCH_RESULT) { 1484 // EVENT_SWITCH_RESULT in GSM call triggers operationComplete() which gets 1485 // the current call list. But in CDMA there is no list so there is nothing 1486 // to do. Other messages however are not expected in CDMA. 1487 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1488 "phone type " + mPhone.getPhoneType()); 1489 } 1490 } 1491 break; 1492 1493 case EVENT_GET_LAST_CALL_FAIL_CAUSE: 1494 int causeCode; 1495 String vendorCause = null; 1496 ar = (AsyncResult)msg.obj; 1497 1498 operationComplete(); 1499 1500 if (ar.exception != null) { 1501 if (ar.exception instanceof CommandException) { 1502 // If we get a CommandException, there are some modem-reported command 1503 // errors which are truly exceptional. We shouldn't treat these as 1504 // NORMAL_CLEARING, so we'll re-map to ERROR_UNSPECIFIED. 1505 CommandException commandException = (CommandException) ar.exception; 1506 switch (commandException.getCommandError()) { 1507 case RADIO_NOT_AVAILABLE: 1508 // Intentional fall-through. 1509 case NO_MEMORY: 1510 // Intentional fall-through. 1511 case INTERNAL_ERR: 1512 // Intentional fall-through. 1513 case NO_RESOURCES: 1514 causeCode = CallFailCause.ERROR_UNSPECIFIED; 1515 1516 // Report the actual internal command error as the vendor cause; 1517 // this will ensure it gets bubbled up into the Telecom logs. 1518 vendorCause = commandException.getCommandError().toString(); 1519 break; 1520 default: 1521 causeCode = CallFailCause.NORMAL_CLEARING; 1522 } 1523 } else { 1524 // An exception occurred...just treat the disconnect 1525 // cause as "normal" 1526 causeCode = CallFailCause.NORMAL_CLEARING; 1527 Rlog.i(LOG_TAG, 1528 "Exception during getLastCallFailCause, assuming normal " 1529 + "disconnect"); 1530 } 1531 } else { 1532 LastCallFailCause failCause = (LastCallFailCause)ar.result; 1533 causeCode = failCause.causeCode; 1534 vendorCause = failCause.vendorCause; 1535 } 1536 // Log the causeCode if its not normal 1537 if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || 1538 causeCode == CallFailCause.TEMPORARY_FAILURE || 1539 causeCode == CallFailCause.SWITCHING_CONGESTION || 1540 causeCode == CallFailCause.CHANNEL_NOT_AVAIL || 1541 causeCode == CallFailCause.QOS_NOT_AVAIL || 1542 causeCode == CallFailCause.BEARER_NOT_AVAIL || 1543 causeCode == CallFailCause.ERROR_UNSPECIFIED) { 1544 1545 CellLocation loc = mPhone.getCellIdentity().asCellLocation(); 1546 int cid = -1; 1547 if (loc != null) { 1548 if (loc instanceof GsmCellLocation) { 1549 cid = ((GsmCellLocation)loc).getCid(); 1550 } else if (loc instanceof CdmaCellLocation) { 1551 cid = ((CdmaCellLocation)loc).getBaseStationId(); 1552 } 1553 } 1554 EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, cid, 1555 TelephonyManager.getDefault().getNetworkType()); 1556 } 1557 1558 if (isEmcRetryCause(causeCode)) { 1559 String dialString = ""; 1560 for(Connection conn : mForegroundCall.mConnections) { 1561 GsmCdmaConnection gsmCdmaConnection = (GsmCdmaConnection)conn; 1562 dialString = gsmCdmaConnection.getOrigDialString(); 1563 gsmCdmaConnection.getCall().detach(gsmCdmaConnection); 1564 mDroppedDuringPoll.remove(gsmCdmaConnection); 1565 } 1566 mPhone.notifyVolteSilentRedial(dialString, causeCode); 1567 updatePhoneState(); 1568 if (mDroppedDuringPoll.isEmpty()) { 1569 log("LAST_CALL_FAIL_CAUSE - no Dropped normal Call"); 1570 return; 1571 } 1572 } 1573 1574 for (int i = 0, s = mDroppedDuringPoll.size(); i < s ; i++) { 1575 GsmCdmaConnection conn = mDroppedDuringPoll.get(i); 1576 1577 conn.onRemoteDisconnect(causeCode, vendorCause); 1578 } 1579 1580 updatePhoneState(); 1581 1582 mPhone.notifyPreciseCallStateChanged(); 1583 mMetrics.writeRilCallList(mPhone.getPhoneId(), mDroppedDuringPoll, 1584 getNetworkCountryIso()); 1585 mPhone.getVoiceCallSessionStats().onRilCallListChanged(mDroppedDuringPoll); 1586 mDroppedDuringPoll.clear(); 1587 break; 1588 1589 case EVENT_REPOLL_AFTER_DELAY: 1590 case EVENT_CALL_STATE_CHANGE: 1591 pollCallsWhenSafe(); 1592 break; 1593 1594 case EVENT_RADIO_AVAILABLE: 1595 handleRadioAvailable(); 1596 break; 1597 1598 case EVENT_RADIO_NOT_AVAILABLE: 1599 handleRadioNotAvailable(); 1600 break; 1601 1602 case EVENT_EXIT_ECM_RESPONSE_CDMA: 1603 if (!isPhoneTypeGsm()) { 1604 // no matter the result, we still do the same here 1605 if (mPendingCallInEcm) { 1606 mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(), 1607 mPendingMO.getEmergencyNumberInfo(), 1608 mPendingMO.hasKnownUserIntentEmergency(), 1609 mPendingCallClirMode, obtainCompleteMessage()); 1610 mPendingCallInEcm = false; 1611 } 1612 mPhone.unsetOnEcbModeExitResponse(this); 1613 } else { 1614 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1615 "phone type " + mPhone.getPhoneType()); 1616 } 1617 break; 1618 1619 case EVENT_CALL_WAITING_INFO_CDMA: 1620 if (!isPhoneTypeGsm()) { 1621 ar = (AsyncResult)msg.obj; 1622 if (ar.exception == null) { 1623 handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); 1624 Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); 1625 } 1626 } else { 1627 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1628 "phone type " + mPhone.getPhoneType()); 1629 } 1630 break; 1631 1632 case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: 1633 if (!isPhoneTypeGsm()) { 1634 ar = (AsyncResult)msg.obj; 1635 if (ar.exception == null) { 1636 // Assume 3 way call is connected 1637 mPendingMO.onConnectedInOrOut(); 1638 mPendingMO = null; 1639 } 1640 } else { 1641 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1642 "phone type " + mPhone.getPhoneType()); 1643 } 1644 break; 1645 1646 case EVENT_THREE_WAY_DIAL_BLANK_FLASH: 1647 if (!isPhoneTypeGsm()) { 1648 ar = (AsyncResult) msg.obj; 1649 if (ar.exception == null) { 1650 postDelayed( 1651 new Runnable() { 1652 public void run() { 1653 if (mPendingMO != null) { 1654 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 1655 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 1656 } 1657 } 1658 }, m3WayCallFlashDelay); 1659 } else { 1660 mPendingMO = null; 1661 Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call"); 1662 } 1663 } else { 1664 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1665 "phone type " + mPhone.getPhoneType()); 1666 } 1667 break; 1668 1669 default:{ 1670 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1671 "phone type " + mPhone.getPhoneType()); 1672 } 1673 } 1674 } 1675 1676 /** 1677 * Dispatches the CS call radio technology to all exist connections. 1678 * 1679 * @param vrat the RIL voice radio technology for CS calls, 1680 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 1681 */ dispatchCsCallRadioTech(@ilRadioTechnology int vrat)1682 public void dispatchCsCallRadioTech(@RilRadioTechnology int vrat) { 1683 if (mConnections == null) { 1684 log("dispatchCsCallRadioTech: mConnections is null"); 1685 return; 1686 } 1687 for (GsmCdmaConnection gsmCdmaConnection : mConnections) { 1688 if (gsmCdmaConnection != null) { 1689 gsmCdmaConnection.setCallRadioTech(vrat); 1690 } 1691 } 1692 } 1693 1694 //CDMA 1695 /** 1696 * Check and enable data call after an emergency call is dropped if it's 1697 * not in ECM 1698 */ checkAndEnableDataCallAfterEmergencyCallDropped()1699 private void checkAndEnableDataCallAfterEmergencyCallDropped() { 1700 if (mIsInEmergencyCall) { 1701 mIsInEmergencyCall = false; 1702 boolean inEcm = mPhone.isInEcm(); 1703 if (Phone.DEBUG_PHONE) { 1704 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm); 1705 } 1706 if (!inEcm) { 1707 // Re-initiate data connection 1708 mPhone.getDataEnabledSettings().setInternalDataEnabled(true); 1709 mPhone.notifyEmergencyCallRegistrants(false); 1710 } 1711 mPhone.sendEmergencyCallStateChange(false); 1712 } 1713 } 1714 1715 /** 1716 * Check the MT call to see if it's a new ring or 1717 * a unknown connection. 1718 */ checkMtFindNewRinging(DriverCall dc, int i)1719 private Connection checkMtFindNewRinging(DriverCall dc, int i) { 1720 1721 Connection newRinging = null; 1722 1723 // it's a ringing call 1724 if (mConnections[i].getCall() == mRingingCall) { 1725 newRinging = mConnections[i]; 1726 if (Phone.DEBUG_PHONE) log("Notify new ring " + dc); 1727 } else { 1728 // Something strange happened: a call which is neither 1729 // a ringing call nor the one we created. It could be the 1730 // call collision result from RIL 1731 Rlog.e(LOG_TAG,"Phantom call appeared " + dc); 1732 // If it's a connected call, set the connect time so that 1733 // it's non-zero. It may not be accurate, but at least 1734 // it won't appear as a Missed Call. 1735 if (dc.state != DriverCall.State.ALERTING 1736 && dc.state != DriverCall.State.DIALING) { 1737 mConnections[i].onConnectedInOrOut(); 1738 if (dc.state == DriverCall.State.HOLDING) { 1739 // We've transitioned into HOLDING 1740 mConnections[i].onStartedHolding(); 1741 } 1742 } 1743 } 1744 return newRinging; 1745 } 1746 1747 //CDMA 1748 /** 1749 * Check if current call is in emergency call 1750 * 1751 * @return true if it is in emergency call 1752 * false if it is not in emergency call 1753 */ isInEmergencyCall()1754 public boolean isInEmergencyCall() { 1755 return mIsInEmergencyCall; 1756 } 1757 1758 /** 1759 * @return {@code true} if the pending outgoing call or active call is an OTASP call, 1760 * {@code false} otherwise. 1761 */ isInOtaspCall()1762 public boolean isInOtaspCall() { 1763 return mPendingMO != null && mPendingMO.isOtaspCall() 1764 || (mForegroundCall.getConnections().stream() 1765 .filter(connection -> ((connection instanceof GsmCdmaConnection) 1766 && (((GsmCdmaConnection) connection).isOtaspCall()))) 1767 .count() > 0); 1768 } 1769 1770 @UnsupportedAppUsage isPhoneTypeGsm()1771 private boolean isPhoneTypeGsm() { 1772 return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM; 1773 } 1774 1775 @UnsupportedAppUsage 1776 @Override getPhone()1777 public GsmCdmaPhone getPhone() { 1778 return mPhone; 1779 } 1780 isEmcRetryCause(int causeCode)1781 private boolean isEmcRetryCause(int causeCode) { 1782 if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS || 1783 causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI) { 1784 return true; 1785 } 1786 return false; 1787 } 1788 1789 @UnsupportedAppUsage 1790 @Override log(String msg)1791 protected void log(String msg) { 1792 Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 1793 } 1794 1795 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1796 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1797 pw.println("GsmCdmaCallTracker extends:"); 1798 super.dump(fd, pw, args); 1799 pw.println("mConnections: length=" + mConnections.length); 1800 for(int i=0; i < mConnections.length; i++) { 1801 pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]); 1802 } 1803 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1804 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1805 if (!isPhoneTypeGsm()) { 1806 pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants); 1807 } 1808 pw.println(" mDroppedDuringPoll: size=" + mDroppedDuringPoll.size()); 1809 for(int i = 0; i < mDroppedDuringPoll.size(); i++) { 1810 pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i)); 1811 } 1812 pw.println(" mRingingCall=" + mRingingCall); 1813 pw.println(" mForegroundCall=" + mForegroundCall); 1814 pw.println(" mBackgroundCall=" + mBackgroundCall); 1815 pw.println(" mPendingMO=" + mPendingMO); 1816 pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1817 pw.println(" mPhone=" + mPhone); 1818 pw.println(" mDesiredMute=" + mDesiredMute); 1819 pw.println(" mState=" + mState); 1820 if (!isPhoneTypeGsm()) { 1821 pw.println(" mPendingCallInEcm=" + mPendingCallInEcm); 1822 pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall); 1823 pw.println(" mPendingCallClirMode=" + mPendingCallClirMode); 1824 } 1825 1826 } 1827 1828 @Override getState()1829 public PhoneConstants.State getState() { 1830 return mState; 1831 } 1832 getMaxConnectionsPerCall()1833 public int getMaxConnectionsPerCall() { 1834 return mPhone.isPhoneTypeGsm() ? 1835 MAX_CONNECTIONS_PER_CALL_GSM : 1836 MAX_CONNECTIONS_PER_CALL_CDMA; 1837 } 1838 getNetworkCountryIso()1839 private String getNetworkCountryIso() { 1840 String countryIso = ""; 1841 if (mPhone != null) { 1842 ServiceStateTracker sst = mPhone.getServiceStateTracker(); 1843 if (sst != null) { 1844 LocaleTracker lt = sst.getLocaleTracker(); 1845 if (lt != null) { 1846 countryIso = lt.getCurrentCountry(); 1847 } 1848 } 1849 } 1850 return countryIso; 1851 } 1852 1853 /** 1854 * Called to force the call tracker to cleanup any stale calls. Does this by triggering 1855 * {@code GET_CURRENT_CALLS} on the RIL. 1856 */ 1857 @Override cleanupCalls()1858 public void cleanupCalls() { 1859 pollCallsWhenSafe(); 1860 } 1861 } 1862