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