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 } else { 514 mPhone.exitEmergencyCallbackMode(); 515 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 516 mPendingCallClirMode = clirMode; 517 mPendingCallInEcm = true; 518 } 519 } 520 521 if (mNumberConverted) { 522 mPendingMO.restoreDialedNumberAfterConversion(origNumber); 523 mNumberConverted = false; 524 } 525 526 updatePhoneState(); 527 mPhone.notifyPreciseCallStateChanged(); 528 529 return mPendingMO; 530 } 531 532 //CDMA dialThreeWay(String dialString, DialArgs dialArgs)533 private Connection dialThreeWay(String dialString, DialArgs dialArgs) { 534 Bundle intentExtras = dialArgs.intentExtras; 535 536 if (!mForegroundCall.isIdle()) { 537 // Check data call and possibly set mIsInEmergencyCall 538 disableDataCallInEmergencyCall(dialString); 539 540 // Attach the new connection to foregroundCall 541 mPendingMO = new GsmCdmaConnection(mPhone, dialString, this, mForegroundCall, 542 dialArgs); 543 if (intentExtras != null) { 544 Rlog.d(LOG_TAG, "dialThreeWay - emergency dialer " + intentExtras.getBoolean( 545 TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL)); 546 mPendingMO.setHasKnownUserIntentEmergency(intentExtras.getBoolean( 547 TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL)); 548 } 549 // Some networks need an empty flash before sending the normal one 550 CarrierConfigManager configManager = (CarrierConfigManager) 551 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 552 PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId()); 553 if (bundle != null) { 554 m3WayCallFlashDelay = 555 bundle.getInt(CarrierConfigManager.KEY_CDMA_3WAYCALL_FLASH_DELAY_INT); 556 } else { 557 // The default 3-way call flash delay is 0s 558 m3WayCallFlashDelay = 0; 559 } 560 if (m3WayCallFlashDelay > 0) { 561 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH)); 562 } else { 563 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 564 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 565 } 566 return mPendingMO; 567 } 568 return null; 569 } 570 dial(String dialString, DialArgs dialArgs)571 public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException { 572 if (isPhoneTypeGsm()) { 573 return dialGsm(dialString, dialArgs); 574 } else { 575 return dialCdma(dialString, dialArgs); 576 } 577 } 578 579 //GSM dialGsm(String dialString, UUSInfo uusInfo, Bundle intentExtras)580 public Connection dialGsm(String dialString, UUSInfo uusInfo, Bundle intentExtras) 581 throws CallStateException { 582 return dialGsm(dialString, new DialArgs.Builder<>() 583 .setUusInfo(uusInfo) 584 .setClirMode(CommandsInterface.CLIR_DEFAULT) 585 .setIntentExtras(intentExtras) 586 .build()); 587 } 588 589 //GSM dialGsm(String dialString, int clirMode, Bundle intentExtras)590 private Connection dialGsm(String dialString, int clirMode, Bundle intentExtras) 591 throws CallStateException { 592 return dialGsm(dialString, new DialArgs.Builder<>() 593 .setClirMode(clirMode) 594 .setIntentExtras(intentExtras) 595 .build()); 596 } 597 598 //GSM dialGsm(String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)599 public Connection dialGsm(String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras) 600 throws CallStateException { 601 return dialGsm(dialString, new DialArgs.Builder<>() 602 .setClirMode(clirMode) 603 .setUusInfo(uusInfo) 604 .setIntentExtras(intentExtras) 605 .build()); 606 } 607 acceptCall()608 public void acceptCall() throws CallStateException { 609 // FIXME if SWITCH fails, should retry with ANSWER 610 // in case the active/holding call disappeared and this 611 // is no longer call waiting 612 613 if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) { 614 Rlog.i("phone", "acceptCall: incoming..."); 615 // Always unmute when answering a new call 616 setMute(false); 617 mPhone.getVoiceCallSessionStats().onRilAcceptCall(mRingingCall.getConnections()); 618 mCi.acceptCall(obtainCompleteMessage()); 619 } else if (mRingingCall.getState() == GsmCdmaCall.State.WAITING) { 620 if (isPhoneTypeGsm()) { 621 setMute(false); 622 } else { 623 GsmCdmaConnection cwConn = (GsmCdmaConnection)(mRingingCall.getLatestConnection()); 624 625 // Since there is no network response for supplimentary 626 // service for CDMA, we assume call waiting is answered. 627 // ringing Call state change to idle is in GsmCdmaCall.detach 628 // triggered by updateParent. 629 cwConn.updateParent(mRingingCall, mForegroundCall); 630 cwConn.onConnectedInOrOut(); 631 updatePhoneState(); 632 } 633 switchWaitingOrHoldingAndActive(); 634 } else { 635 throw new CallStateException("phone not ringing"); 636 } 637 } 638 rejectCall()639 public void rejectCall() throws CallStateException { 640 // AT+CHLD=0 means "release held or UDUB" 641 // so if the phone isn't ringing, this could hang up held 642 if (mRingingCall.getState().isRinging()) { 643 mCi.rejectCall(obtainCompleteMessage()); 644 } else { 645 throw new CallStateException("phone not ringing"); 646 } 647 } 648 649 //CDMA flashAndSetGenericTrue()650 private void flashAndSetGenericTrue() { 651 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); 652 653 mPhone.notifyPreciseCallStateChanged(); 654 } 655 656 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) switchWaitingOrHoldingAndActive()657 public void switchWaitingOrHoldingAndActive() throws CallStateException { 658 // Should we bother with this check? 659 if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) { 660 throw new CallStateException("cannot be in the incoming state"); 661 } else { 662 if (isPhoneTypeGsm()) { 663 mCi.switchWaitingOrHoldingAndActive( 664 obtainCompleteMessage(EVENT_SWITCH_RESULT)); 665 } else { 666 if (mForegroundCall.getConnectionsCount() > 1) { 667 flashAndSetGenericTrue(); 668 } else { 669 // Send a flash command to CDMA network for putting the other party on hold. 670 // For CDMA networks which do not support this the user would just hear a beep 671 // from the network. For CDMA networks which do support it will put the other 672 // party on hold. 673 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT)); 674 } 675 } 676 } 677 } 678 conference()679 public void conference() { 680 if (isPhoneTypeGsm()) { 681 mCi.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT)); 682 } else { 683 // Should we be checking state? 684 flashAndSetGenericTrue(); 685 } 686 } 687 explicitCallTransfer()688 public void explicitCallTransfer() { 689 mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); 690 } 691 692 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) clearDisconnected()693 public void clearDisconnected() { 694 internalClearDisconnected(); 695 696 updatePhoneState(); 697 mPhone.notifyPreciseCallStateChanged(); 698 } 699 canConference()700 public boolean canConference() { 701 return mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE 702 && mBackgroundCall.getState() == GsmCdmaCall.State.HOLDING 703 && !mBackgroundCall.isFull() 704 && !mForegroundCall.isFull(); 705 } 706 707 /** 708 * Determines if there are issues which would preclude dialing an outgoing call. Throws a 709 * {@link CallStateException} if there is an issue. 710 * @throws CallStateException 711 */ checkForDialIssues(boolean isEmergencyCall)712 public void checkForDialIssues(boolean isEmergencyCall) throws CallStateException { 713 boolean disableCall = TelephonyProperties.disable_call().orElse(false); 714 715 if (mCi.getRadioState() != TelephonyManager.RADIO_POWER_ON) { 716 throw new CallStateException(CallStateException.ERROR_POWER_OFF, 717 "Modem not powered"); 718 } 719 if (disableCall) { 720 throw new CallStateException(CallStateException.ERROR_CALLING_DISABLED, 721 "Calling disabled via ro.telephony.disable-call property"); 722 } 723 if (mPendingMO != null) { 724 throw new CallStateException(CallStateException.ERROR_ALREADY_DIALING, 725 "A call is already dialing."); 726 } 727 if (mRingingCall.isRinging()) { 728 throw new CallStateException(CallStateException.ERROR_CALL_RINGING, 729 "Can't call while a call is ringing."); 730 } 731 if (isPhoneTypeGsm() 732 && mForegroundCall.getState().isAlive() && mBackgroundCall.getState().isAlive()) { 733 throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS, 734 "There is already a foreground and background call."); 735 } 736 if (!isPhoneTypeGsm() 737 // Essentially foreground call state is one of: 738 // HOLDING, DIALING, ALERTING, INCOMING, WAITING 739 && mForegroundCall.getState().isAlive() 740 && mForegroundCall.getState() != GsmCdmaCall.State.ACTIVE 741 742 && mBackgroundCall.getState().isAlive()) { 743 throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS, 744 "There is already a foreground and background call."); 745 } 746 if (!isEmergencyCall && isInOtaspCall()) { 747 throw new CallStateException(CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS, 748 "OTASP provisioning is in process."); 749 } 750 } 751 canTransfer()752 public boolean canTransfer() { 753 if (isPhoneTypeGsm()) { 754 return (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE 755 || mForegroundCall.getState() == GsmCdmaCall.State.ALERTING 756 || mForegroundCall.getState() == GsmCdmaCall.State.DIALING) 757 && mBackgroundCall.getState() == GsmCdmaCall.State.HOLDING; 758 } else { 759 Rlog.e(LOG_TAG, "canTransfer: not possible in CDMA"); 760 return false; 761 } 762 } 763 764 //***** Private Instance Methods 765 internalClearDisconnected()766 private void internalClearDisconnected() { 767 mRingingCall.clearDisconnected(); 768 mForegroundCall.clearDisconnected(); 769 mBackgroundCall.clearDisconnected(); 770 } 771 772 /** 773 * Obtain a message to use for signalling "invoke getCurrentCalls() when 774 * this operation and all other pending operations are complete 775 */ 776 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) obtainCompleteMessage()777 private Message obtainCompleteMessage() { 778 return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); 779 } 780 781 /** 782 * Obtain a message to use for signalling "invoke getCurrentCalls() when 783 * this operation and all other pending operations are complete 784 */ 785 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) obtainCompleteMessage(int what)786 private Message obtainCompleteMessage(int what) { 787 mPendingOperations++; 788 mLastRelevantPoll = null; 789 mNeedsPoll = true; 790 791 if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + 792 mPendingOperations + ", needsPoll=" + mNeedsPoll); 793 794 return obtainMessage(what); 795 } 796 operationComplete()797 private void operationComplete() { 798 mPendingOperations--; 799 800 if (DBG_POLL) log("operationComplete: pendingOperations=" + 801 mPendingOperations + ", needsPoll=" + mNeedsPoll); 802 803 if (mPendingOperations == 0 && mNeedsPoll) { 804 mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); 805 mCi.getCurrentCalls(mLastRelevantPoll); 806 } else if (mPendingOperations < 0) { 807 // this should never happen 808 Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0"); 809 mPendingOperations = 0; 810 } 811 } 812 813 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) updatePhoneState()814 private void updatePhoneState() { 815 PhoneConstants.State oldState = mState; 816 if (mRingingCall.isRinging()) { 817 mState = PhoneConstants.State.RINGING; 818 } else if (mPendingMO != null || 819 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 820 mState = PhoneConstants.State.OFFHOOK; 821 } else { 822 Phone imsPhone = mPhone.getImsPhone(); 823 if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){ 824 imsPhone.callEndCleanupHandOverCallIfAny(); 825 } 826 mState = PhoneConstants.State.IDLE; 827 } 828 829 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 830 mVoiceCallEndedRegistrants.notifyRegistrants( 831 new AsyncResult(null, null, null)); 832 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 833 mVoiceCallStartedRegistrants.notifyRegistrants ( 834 new AsyncResult(null, null, null)); 835 } 836 if (Phone.DEBUG_PHONE) { 837 log("update phone state, old=" + oldState + " new="+ mState); 838 } 839 if (mState != oldState) { 840 mPhone.notifyPhoneStateChanged(); 841 mMetrics.writePhoneState(mPhone.getPhoneId(), mState); 842 } 843 } 844 845 // ***** Overwritten from CallTracker 846 847 @Override handlePollCalls(AsyncResult ar)848 protected synchronized void handlePollCalls(AsyncResult ar) { 849 List polledCalls; 850 851 if (VDBG) log("handlePollCalls"); 852 if (ar.exception == null) { 853 polledCalls = (List)ar.result; 854 } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { 855 // just a placeholder empty ArrayList to cause the loop 856 // to hang up all the calls 857 polledCalls = new ArrayList(); 858 } else { 859 // Radio probably wasn't ready--try again in a bit 860 // But don't keep polling if the channel is closed 861 pollCallsAfterDelay(); 862 return; 863 } 864 865 Connection newRinging = null; //or waiting 866 ArrayList<Connection> newUnknownConnectionsGsm = new ArrayList<Connection>(); 867 Connection newUnknownConnectionCdma = null; 868 boolean hasNonHangupStateChanged = false; // Any change besides 869 // a dropped connection 870 boolean hasAnyCallDisconnected = false; 871 boolean needsPollDelay = false; 872 boolean unknownConnectionAppeared = false; 873 int handoverConnectionsSize = mHandoverConnections.size(); 874 875 //CDMA 876 boolean noConnectionExists = true; 877 878 for (int i = 0, curDC = 0, dcSize = polledCalls.size() 879 ; i < mConnections.length; i++) { 880 GsmCdmaConnection conn = mConnections[i]; 881 DriverCall dc = null; 882 883 // polledCall list is sparse 884 if (curDC < dcSize) { 885 dc = (DriverCall) polledCalls.get(curDC); 886 887 if (dc.index == i+1) { 888 curDC++; 889 } else { 890 dc = null; 891 } 892 } 893 894 //CDMA 895 if (conn != null || dc != null) { 896 noConnectionExists = false; 897 } 898 899 if (DBG_POLL) log("poll: conn[i=" + i + "]=" + 900 conn+", dc=" + dc); 901 902 if (conn == null && dc != null) { 903 // Connection appeared in CLCC response that we don't know about 904 if (mPendingMO != null && mPendingMO.compareTo(dc)) { 905 906 if (DBG_POLL) log("poll: pendingMO=" + mPendingMO); 907 908 // It's our pending mobile originating call 909 mConnections[i] = mPendingMO; 910 mPendingMO.mIndex = i; 911 mPendingMO.update(dc); 912 mPendingMO = null; 913 914 // Someone has already asked to hangup this call 915 if (mHangupPendingMO) { 916 mHangupPendingMO = false; 917 918 // Re-start Ecm timer when an uncompleted emergency call ends 919 if (!isPhoneTypeGsm() && mPhone.isEcmCanceledForEmergency()) { 920 mPhone.handleTimerInEmergencyCallbackMode( 921 GsmCdmaPhone.RESTART_ECM_TIMER); 922 } 923 924 try { 925 if (Phone.DEBUG_PHONE) log( 926 "poll: hangupPendingMO, hangup conn " + i); 927 hangup(mConnections[i]); 928 } catch (CallStateException ex) { 929 Rlog.e(LOG_TAG, "unexpected error on hangup"); 930 } 931 932 // Do not continue processing this poll 933 // Wait for hangup and repoll 934 return; 935 } 936 } else { 937 if (Phone.DEBUG_PHONE) { 938 log("pendingMo=" + mPendingMO + ", dc=" + dc); 939 } 940 941 mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i); 942 log("New connection is not mPendingMO. Creating new GsmCdmaConnection," 943 + " objId=" + System.identityHashCode(mConnections[i])); 944 945 Connection hoConnection = getHoConnection(dc); 946 if (hoConnection != null) { 947 log("Handover connection found."); 948 // Single Radio Voice Call Continuity (SRVCC) completed 949 mConnections[i].migrateFrom(hoConnection); 950 // Updating connect time for silent redial cases (ex: Calls are transferred 951 // from DIALING/ALERTING/INCOMING/WAITING to ACTIVE) 952 if (hoConnection.mPreHandoverState != GsmCdmaCall.State.ACTIVE && 953 hoConnection.mPreHandoverState != GsmCdmaCall.State.HOLDING && 954 dc.state == DriverCall.State.ACTIVE) { 955 mConnections[i].onConnectedInOrOut(); 956 } else { 957 mConnections[i].onConnectedConnectionMigrated(); 958 } 959 960 mHandoverConnections.remove(hoConnection); 961 962 if (isPhoneTypeGsm()) { 963 for (Iterator<Connection> it = mHandoverConnections.iterator(); 964 it.hasNext(); ) { 965 Connection c = it.next(); 966 Rlog.i(LOG_TAG, "HO Conn state is " + c.mPreHandoverState); 967 if (c.mPreHandoverState == mConnections[i].getState()) { 968 Rlog.i(LOG_TAG, "Removing HO conn " 969 + hoConnection + c.mPreHandoverState); 970 it.remove(); 971 } 972 } 973 } 974 975 mPhone.notifyHandoverStateChanged(mConnections[i]); 976 } else { 977 // find if the MT call is a new ring or unknown connection 978 log("New connection is not mPendingMO nor a pending handover."); 979 newRinging = checkMtFindNewRinging(dc,i); 980 if (newRinging == null) { 981 unknownConnectionAppeared = true; 982 if (isPhoneTypeGsm()) { 983 newUnknownConnectionsGsm.add(mConnections[i]); 984 } else { 985 newUnknownConnectionCdma = mConnections[i]; 986 } 987 } else if (hangupWaitingCallSilently(i)) { 988 return; 989 } 990 } 991 } 992 hasNonHangupStateChanged = true; 993 } else if (conn != null && dc == null) { 994 if (isPhoneTypeGsm()) { 995 // Connection missing in CLCC response that we were 996 // tracking. 997 mDroppedDuringPoll.add(conn); 998 } else { 999 // This case means the RIL has no more active call anymore and 1000 // we need to clean up the foregroundCall and ringingCall. 1001 // Loop through foreground call connections as 1002 // it contains the known logical connections. 1003 ArrayList<Connection> connections = mForegroundCall.getConnections(); 1004 for (Connection cn : connections) { 1005 if (Phone.DEBUG_PHONE) { 1006 log("adding fgCall cn " + cn + "to droppedDuringPoll"); 1007 } 1008 mDroppedDuringPoll.add((GsmCdmaConnection) cn); 1009 } 1010 1011 connections = mRingingCall.getConnections(); 1012 // Loop through ringing call connections as 1013 // it may contain the known logical connections. 1014 for (Connection cn : connections) { 1015 if (Phone.DEBUG_PHONE) { 1016 log("adding rgCall cn " + cn + "to droppedDuringPoll"); 1017 } 1018 mDroppedDuringPoll.add((GsmCdmaConnection) cn); 1019 } 1020 1021 // Re-start Ecm timer when the connected emergency call ends 1022 if (mPhone.isEcmCanceledForEmergency()) { 1023 mPhone.handleTimerInEmergencyCallbackMode(GsmCdmaPhone.RESTART_ECM_TIMER); 1024 } 1025 // If emergency call is not going through while dialing 1026 checkAndEnableDataCallAfterEmergencyCallDropped(); 1027 } 1028 // Dropped connections are removed from the CallTracker 1029 // list but kept in the Call list 1030 mConnections[i] = null; 1031 } else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) { 1032 // Connection in CLCC response does not match what 1033 // we were tracking. Assume dropped call and new call 1034 1035 mDroppedDuringPoll.add(conn); 1036 mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i); 1037 1038 if (mConnections[i].getCall() == mRingingCall) { 1039 newRinging = mConnections[i]; 1040 if (hangupWaitingCallSilently(i)) { 1041 return; 1042 } 1043 } // else something strange happened 1044 hasNonHangupStateChanged = true; 1045 } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ 1046 // Call collision case 1047 if (!isPhoneTypeGsm() && conn.isIncoming() != dc.isMT) { 1048 if (dc.isMT == true) { 1049 // Mt call takes precedence than Mo,drops Mo 1050 mDroppedDuringPoll.add(conn); 1051 // find if the MT call is a new ring or unknown connection 1052 newRinging = checkMtFindNewRinging(dc,i); 1053 if (newRinging == null) { 1054 unknownConnectionAppeared = true; 1055 newUnknownConnectionCdma = conn; 1056 } 1057 checkAndEnableDataCallAfterEmergencyCallDropped(); 1058 } else { 1059 // Call info stored in conn is not consistent with the call info from dc. 1060 // We should follow the rule of MT calls taking precedence over MO calls 1061 // when there is conflict, so here we drop the call info from dc and 1062 // continue to use the call info from conn, and only take a log. 1063 Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc); 1064 } 1065 } else { 1066 boolean changed; 1067 changed = conn.update(dc); 1068 hasNonHangupStateChanged = hasNonHangupStateChanged || changed; 1069 } 1070 } 1071 1072 if (REPEAT_POLLING) { 1073 if (dc != null) { 1074 // FIXME with RIL, we should not need this anymore 1075 if ((dc.state == DriverCall.State.DIALING 1076 /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) 1077 || (dc.state == DriverCall.State.ALERTING 1078 /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) 1079 || (dc.state == DriverCall.State.INCOMING 1080 /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) 1081 || (dc.state == DriverCall.State.WAITING 1082 /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)) { 1083 // Sometimes there's no unsolicited notification 1084 // for state transitions 1085 needsPollDelay = true; 1086 } 1087 } 1088 } 1089 } 1090 1091 // Safety check so that obj is not stuck with mIsInEmergencyCall set to true (and data 1092 // disabled). This should never happen though. 1093 if (!isPhoneTypeGsm() && noConnectionExists) { 1094 checkAndEnableDataCallAfterEmergencyCallDropped(); 1095 } 1096 1097 // This is the first poll after an ATD. 1098 // We expect the pending call to appear in the list 1099 // If it does not, we land here 1100 if (mPendingMO != null) { 1101 Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:" 1102 + mForegroundCall.getState()); 1103 1104 mDroppedDuringPoll.add(mPendingMO); 1105 mPendingMO = null; 1106 mHangupPendingMO = false; 1107 1108 if (!isPhoneTypeGsm()) { 1109 if( mPendingCallInEcm) { 1110 mPendingCallInEcm = false; 1111 } 1112 checkAndEnableDataCallAfterEmergencyCallDropped(); 1113 } 1114 } 1115 1116 if (newRinging != null) { 1117 mPhone.notifyNewRingingConnection(newRinging); 1118 } 1119 1120 // clear the "local hangup" and "missed/rejected call" 1121 // cases from the "dropped during poll" list 1122 // These cases need no "last call fail" reason 1123 ArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>(); 1124 for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) { 1125 GsmCdmaConnection conn = mDroppedDuringPoll.get(i); 1126 //CDMA 1127 boolean wasDisconnected = false; 1128 1129 if (conn.isIncoming() && conn.getConnectTime() == 0) { 1130 // Missed or rejected call 1131 int cause; 1132 if (conn.mCause == DisconnectCause.LOCAL) { 1133 cause = DisconnectCause.INCOMING_REJECTED; 1134 } else { 1135 cause = DisconnectCause.INCOMING_MISSED; 1136 } 1137 1138 if (Phone.DEBUG_PHONE) { 1139 log("missed/rejected call, conn.cause=" + conn.mCause); 1140 log("setting cause to " + cause); 1141 } 1142 mDroppedDuringPoll.remove(i); 1143 hasAnyCallDisconnected |= conn.onDisconnect(cause); 1144 wasDisconnected = true; 1145 locallyDisconnectedConnections.add(conn); 1146 } else if (conn.mCause == DisconnectCause.LOCAL 1147 || conn.mCause == DisconnectCause.INVALID_NUMBER) { 1148 mDroppedDuringPoll.remove(i); 1149 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause); 1150 wasDisconnected = true; 1151 locallyDisconnectedConnections.add(conn); 1152 } 1153 1154 if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared 1155 && conn == newUnknownConnectionCdma) { 1156 unknownConnectionAppeared = false; 1157 newUnknownConnectionCdma = null; 1158 } 1159 } 1160 1161 if (locallyDisconnectedConnections.size() > 0) { 1162 mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections, 1163 getNetworkCountryIso()); 1164 mPhone.getVoiceCallSessionStats().onRilCallListChanged(locallyDisconnectedConnections); 1165 } 1166 1167 /* Disconnect any pending Handover connections */ 1168 for (Iterator<Connection> it = mHandoverConnections.iterator(); 1169 it.hasNext();) { 1170 Connection hoConnection = it.next(); 1171 log("handlePollCalls - disconnect hoConn= " + hoConnection + 1172 " hoConn.State= " + hoConnection.getState()); 1173 if (hoConnection.getState().isRinging()) { 1174 hoConnection.onDisconnect(DisconnectCause.INCOMING_MISSED); 1175 } else { 1176 hoConnection.onDisconnect(DisconnectCause.NOT_VALID); 1177 } 1178 // TODO: Do we need to update these hoConnections in Metrics ? 1179 it.remove(); 1180 } 1181 1182 // Any non-local disconnects: determine cause 1183 if (mDroppedDuringPoll.size() > 0) { 1184 mCi.getLastCallFailCause( 1185 obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); 1186 } 1187 1188 if (needsPollDelay) { 1189 pollCallsAfterDelay(); 1190 } 1191 1192 // Cases when we can no longer keep disconnected Connection's 1193 // with their previous calls 1194 // 1) the phone has started to ring 1195 // 2) A Call/Connection object has changed state... 1196 // we may have switched or held or answered (but not hung up) 1197 if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) { 1198 internalClearDisconnected(); 1199 } 1200 1201 if (VDBG) log("handlePollCalls calling updatePhoneState()"); 1202 updatePhoneState(); 1203 1204 if (unknownConnectionAppeared) { 1205 if (isPhoneTypeGsm()) { 1206 for (Connection c : newUnknownConnectionsGsm) { 1207 log("Notify unknown for " + c); 1208 mPhone.notifyUnknownConnection(c); 1209 } 1210 } else { 1211 mPhone.notifyUnknownConnection(newUnknownConnectionCdma); 1212 } 1213 } 1214 1215 if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) { 1216 mPhone.notifyPreciseCallStateChanged(); 1217 updateMetrics(mConnections); 1218 } 1219 1220 // If all handover connections are mapped during this poll process clean it up 1221 if (handoverConnectionsSize > 0 && mHandoverConnections.size() == 0) { 1222 Phone imsPhone = mPhone.getImsPhone(); 1223 if (imsPhone != null) { 1224 imsPhone.callEndCleanupHandOverCallIfAny(); 1225 } 1226 } 1227 //dumpState(); 1228 } 1229 updateMetrics(GsmCdmaConnection[] connections)1230 private void updateMetrics(GsmCdmaConnection[] connections) { 1231 ArrayList<GsmCdmaConnection> activeConnections = new ArrayList<>(); 1232 for (GsmCdmaConnection conn : connections) { 1233 if (conn != null) activeConnections.add(conn); 1234 } 1235 mMetrics.writeRilCallList(mPhone.getPhoneId(), activeConnections, getNetworkCountryIso()); 1236 mPhone.getVoiceCallSessionStats().onRilCallListChanged(activeConnections); 1237 } 1238 handleRadioNotAvailable()1239 private void handleRadioNotAvailable() { 1240 // handlePollCalls will clear out its 1241 // call list when it gets the CommandException 1242 // error result from this 1243 pollCallsWhenSafe(); 1244 } 1245 dumpState()1246 private void dumpState() { 1247 List l; 1248 1249 Rlog.i(LOG_TAG,"Phone State:" + mState); 1250 1251 Rlog.i(LOG_TAG,"Ringing call: " + mRingingCall.toString()); 1252 1253 l = mRingingCall.getConnections(); 1254 for (int i = 0, s = l.size(); i < s; i++) { 1255 Rlog.i(LOG_TAG,l.get(i).toString()); 1256 } 1257 1258 Rlog.i(LOG_TAG,"Foreground call: " + mForegroundCall.toString()); 1259 1260 l = mForegroundCall.getConnections(); 1261 for (int i = 0, s = l.size(); i < s; i++) { 1262 Rlog.i(LOG_TAG,l.get(i).toString()); 1263 } 1264 1265 Rlog.i(LOG_TAG,"Background call: " + mBackgroundCall.toString()); 1266 1267 l = mBackgroundCall.getConnections(); 1268 for (int i = 0, s = l.size(); i < s; i++) { 1269 Rlog.i(LOG_TAG,l.get(i).toString()); 1270 } 1271 1272 } 1273 1274 //***** Called from GsmCdmaConnection 1275 hangup(GsmCdmaConnection conn)1276 public void hangup(GsmCdmaConnection conn) throws CallStateException { 1277 if (conn.mOwner != this) { 1278 throw new CallStateException ("GsmCdmaConnection " + conn 1279 + "does not belong to GsmCdmaCallTracker " + this); 1280 } 1281 1282 if (conn == mPendingMO) { 1283 // We're hanging up an outgoing call that doesn't have it's 1284 // GsmCdma index assigned yet 1285 1286 if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); 1287 mHangupPendingMO = true; 1288 } else if (!isPhoneTypeGsm() 1289 && conn.getCall() == mRingingCall 1290 && mRingingCall.getState() == GsmCdmaCall.State.WAITING) { 1291 // Handle call waiting hang up case. 1292 // 1293 // The ringingCall state will change to IDLE in GsmCdmaCall.detach 1294 // if the ringing call connection size is 0. We don't specifically 1295 // set the ringing call state to IDLE here to avoid a race condition 1296 // where a new call waiting could get a hang up from an old call 1297 // waiting ringingCall. 1298 // 1299 // PhoneApp does the call log itself since only PhoneApp knows 1300 // the hangup reason is user ignoring or timing out. So conn.onDisconnect() 1301 // is not called here. Instead, conn.onLocalDisconnect() is called. 1302 conn.onLocalDisconnect(); 1303 1304 updatePhoneState(); 1305 mPhone.notifyPreciseCallStateChanged(); 1306 return; 1307 } else { 1308 try { 1309 mMetrics.writeRilHangup(mPhone.getPhoneId(), conn, conn.getGsmCdmaIndex(), 1310 getNetworkCountryIso()); 1311 mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage()); 1312 } catch (CallStateException ex) { 1313 // Ignore "connection not found" 1314 // Call may have hung up already 1315 Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: hangup() on absent connection " 1316 + conn); 1317 } 1318 } 1319 1320 conn.onHangupLocal(); 1321 } 1322 separate(GsmCdmaConnection conn)1323 public void separate(GsmCdmaConnection conn) throws CallStateException { 1324 if (conn.mOwner != this) { 1325 throw new CallStateException ("GsmCdmaConnection " + conn 1326 + "does not belong to GsmCdmaCallTracker " + this); 1327 } 1328 try { 1329 mCi.separateConnection (conn.getGsmCdmaIndex(), 1330 obtainCompleteMessage(EVENT_SEPARATE_RESULT)); 1331 } catch (CallStateException ex) { 1332 // Ignore "connection not found" 1333 // Call may have hung up already 1334 Rlog.w(LOG_TAG,"GsmCdmaCallTracker WARN: separate() on absent connection " + conn); 1335 } 1336 } 1337 1338 //***** Called from GsmCdmaPhone 1339 1340 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) setMute(boolean mute)1341 public void setMute(boolean mute) { 1342 mDesiredMute = mute; 1343 mCi.setMute(mDesiredMute, null); 1344 } 1345 getMute()1346 public boolean getMute() { 1347 return mDesiredMute; 1348 } 1349 1350 1351 //***** Called from GsmCdmaCall 1352 hangup(GsmCdmaCall call)1353 public void hangup(GsmCdmaCall call) throws CallStateException { 1354 if (call.getConnectionsCount() == 0) { 1355 throw new CallStateException("no connections in call"); 1356 } 1357 1358 if (call == mRingingCall) { 1359 if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); 1360 logHangupEvent(call); 1361 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 1362 } else if (call == mForegroundCall) { 1363 if (call.isDialingOrAlerting()) { 1364 if (Phone.DEBUG_PHONE) { 1365 log("(foregnd) hangup dialing or alerting..."); 1366 } 1367 hangup((GsmCdmaConnection)(call.getConnections().get(0))); 1368 } else if (isPhoneTypeGsm() 1369 && mRingingCall.isRinging()) { 1370 // Do not auto-answer ringing on CHUP, instead just end active calls 1371 log("hangup all conns in active/background call, without affecting ringing call"); 1372 hangupAllConnections(call); 1373 } else { 1374 logHangupEvent(call); 1375 hangupForegroundResumeBackground(); 1376 } 1377 } else if (call == mBackgroundCall) { 1378 if (mRingingCall.isRinging()) { 1379 if (Phone.DEBUG_PHONE) { 1380 log("hangup all conns in background call"); 1381 } 1382 hangupAllConnections(call); 1383 } else { 1384 hangupWaitingOrBackground(); 1385 } 1386 } else { 1387 throw new RuntimeException ("GsmCdmaCall " + call + 1388 "does not belong to GsmCdmaCallTracker " + this); 1389 } 1390 1391 call.onHangupLocal(); 1392 mPhone.notifyPreciseCallStateChanged(); 1393 } 1394 logHangupEvent(GsmCdmaCall call)1395 private void logHangupEvent(GsmCdmaCall call) { 1396 for (Connection conn : call.getConnections()) { 1397 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1398 int call_index; 1399 try { 1400 call_index = c.getGsmCdmaIndex(); 1401 } catch (CallStateException e) { 1402 call_index = -1; 1403 } 1404 mMetrics.writeRilHangup(mPhone.getPhoneId(), c, call_index, getNetworkCountryIso()); 1405 } 1406 if (VDBG) { 1407 Rlog.v(LOG_TAG, "logHangupEvent logged " + call.getConnectionsCount() 1408 + " Connections "); 1409 } 1410 } 1411 hangupWaitingOrBackground()1412 public void hangupWaitingOrBackground() { 1413 if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); 1414 logHangupEvent(mBackgroundCall); 1415 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 1416 } 1417 hangupForegroundResumeBackground()1418 public void hangupForegroundResumeBackground() { 1419 if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); 1420 mCi.hangupForegroundResumeBackground(obtainCompleteMessage()); 1421 } 1422 hangupConnectionByIndex(GsmCdmaCall call, int index)1423 public void hangupConnectionByIndex(GsmCdmaCall call, int index) 1424 throws CallStateException { 1425 for (Connection conn : call.getConnections()) { 1426 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1427 if (!c.mDisconnected && c.getGsmCdmaIndex() == index) { 1428 mMetrics.writeRilHangup(mPhone.getPhoneId(), c, c.getGsmCdmaIndex(), 1429 getNetworkCountryIso()); 1430 mCi.hangupConnection(index, obtainCompleteMessage()); 1431 return; 1432 } 1433 } 1434 throw new CallStateException("no GsmCdma index found"); 1435 } 1436 hangupAllConnections(GsmCdmaCall call)1437 public void hangupAllConnections(GsmCdmaCall call) { 1438 try { 1439 for (Connection conn : call.getConnections()) { 1440 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1441 if (!c.mDisconnected) { 1442 mMetrics.writeRilHangup(mPhone.getPhoneId(), c, c.getGsmCdmaIndex(), 1443 getNetworkCountryIso()); 1444 mCi.hangupConnection(c.getGsmCdmaIndex(), obtainCompleteMessage()); 1445 } 1446 } 1447 } catch (CallStateException ex) { 1448 Rlog.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); 1449 } 1450 } 1451 getConnectionByIndex(GsmCdmaCall call, int index)1452 public GsmCdmaConnection getConnectionByIndex(GsmCdmaCall call, int index) 1453 throws CallStateException { 1454 for (Connection conn : call.getConnections()) { 1455 GsmCdmaConnection c = (GsmCdmaConnection) conn; 1456 if (!c.mDisconnected && c.getGsmCdmaIndex() == index) { 1457 return c; 1458 } 1459 } 1460 return null; 1461 } 1462 1463 //CDMA notifyCallWaitingInfo(CdmaCallWaitingNotification obj)1464 private void notifyCallWaitingInfo(CdmaCallWaitingNotification obj) { 1465 if (mCallWaitingRegistrants != null) { 1466 mCallWaitingRegistrants.notifyRegistrants(new AsyncResult(null, obj, null)); 1467 } 1468 } 1469 1470 //CDMA handleCallWaitingInfo(CdmaCallWaitingNotification cw)1471 private void handleCallWaitingInfo(CdmaCallWaitingNotification cw) { 1472 // Create a new GsmCdmaConnection which attaches itself to ringingCall. 1473 new GsmCdmaConnection(mPhone.getContext(), cw, this, mRingingCall); 1474 updatePhoneState(); 1475 1476 // Finally notify application 1477 notifyCallWaitingInfo(cw); 1478 } 1479 getFailedService(int what)1480 private Phone.SuppService getFailedService(int what) { 1481 switch (what) { 1482 case EVENT_SWITCH_RESULT: 1483 return Phone.SuppService.SWITCH; 1484 case EVENT_CONFERENCE_RESULT: 1485 return Phone.SuppService.CONFERENCE; 1486 case EVENT_SEPARATE_RESULT: 1487 return Phone.SuppService.SEPARATE; 1488 case EVENT_ECT_RESULT: 1489 return Phone.SuppService.TRANSFER; 1490 } 1491 return Phone.SuppService.UNKNOWN; 1492 } 1493 1494 //****** Overridden from Handler 1495 1496 @Override handleMessage(Message msg)1497 public void handleMessage(Message msg) { 1498 AsyncResult ar; 1499 1500 switch (msg.what) { 1501 case EVENT_POLL_CALLS_RESULT: 1502 if (DBG_POLL) Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received"); 1503 1504 if (msg == mLastRelevantPoll) { 1505 if (DBG_POLL) log( 1506 "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); 1507 mNeedsPoll = false; 1508 mLastRelevantPoll = null; 1509 handlePollCalls((AsyncResult)msg.obj); 1510 } 1511 break; 1512 1513 case EVENT_OPERATION_COMPLETE: 1514 operationComplete(); 1515 break; 1516 1517 case EVENT_CONFERENCE_RESULT: 1518 if (isPhoneTypeGsm()) { 1519 ar = (AsyncResult) msg.obj; 1520 if (ar.exception != null) { 1521 // The conference merge failed, so notify listeners. Ultimately this 1522 // bubbles up to Telecom, which will inform the InCall UI of the failure. 1523 Connection connection = mForegroundCall.getLatestConnection(); 1524 if (connection != null) { 1525 connection.onConferenceMergeFailed(); 1526 } 1527 } 1528 } 1529 // fall through 1530 case EVENT_SEPARATE_RESULT: 1531 case EVENT_ECT_RESULT: 1532 case EVENT_SWITCH_RESULT: 1533 if (isPhoneTypeGsm()) { 1534 ar = (AsyncResult) msg.obj; 1535 if (ar.exception != null) { 1536 if (msg.what == EVENT_SWITCH_RESULT) { 1537 Connection connection = mForegroundCall.getLatestConnection(); 1538 if (connection != null) { 1539 if (mBackgroundCall.getState() != GsmCdmaCall.State.HOLDING) { 1540 connection.onConnectionEvent( 1541 android.telecom.Connection.EVENT_CALL_HOLD_FAILED, 1542 null); 1543 } else { 1544 connection.onConnectionEvent( 1545 android.telecom.Connection.EVENT_CALL_SWITCH_FAILED, 1546 null); 1547 } 1548 } 1549 } 1550 mPhone.notifySuppServiceFailed(getFailedService(msg.what)); 1551 } 1552 operationComplete(); 1553 } else { 1554 if (msg.what != EVENT_SWITCH_RESULT) { 1555 // EVENT_SWITCH_RESULT in GSM call triggers operationComplete() which gets 1556 // the current call list. But in CDMA there is no list so there is nothing 1557 // to do. Other messages however are not expected in CDMA. 1558 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1559 "phone type " + mPhone.getPhoneType()); 1560 } 1561 } 1562 break; 1563 1564 case EVENT_GET_LAST_CALL_FAIL_CAUSE: 1565 int causeCode; 1566 String vendorCause = null; 1567 ar = (AsyncResult)msg.obj; 1568 1569 operationComplete(); 1570 1571 if (ar.exception != null) { 1572 if (ar.exception instanceof CommandException) { 1573 // If we get a CommandException, there are some modem-reported command 1574 // errors which are truly exceptional. We shouldn't treat these as 1575 // NORMAL_CLEARING, so we'll re-map to ERROR_UNSPECIFIED. 1576 CommandException commandException = (CommandException) ar.exception; 1577 switch (commandException.getCommandError()) { 1578 case RADIO_NOT_AVAILABLE: 1579 // Intentional fall-through. 1580 case NO_MEMORY: 1581 // Intentional fall-through. 1582 case INTERNAL_ERR: 1583 // Intentional fall-through. 1584 case NO_RESOURCES: 1585 causeCode = CallFailCause.ERROR_UNSPECIFIED; 1586 1587 // Report the actual internal command error as the vendor cause; 1588 // this will ensure it gets bubbled up into the Telecom logs. 1589 vendorCause = commandException.getCommandError().toString(); 1590 break; 1591 default: 1592 causeCode = CallFailCause.NORMAL_CLEARING; 1593 } 1594 } else { 1595 // An exception occurred...just treat the disconnect 1596 // cause as "normal" 1597 causeCode = CallFailCause.NORMAL_CLEARING; 1598 Rlog.i(LOG_TAG, 1599 "Exception during getLastCallFailCause, assuming normal " 1600 + "disconnect"); 1601 } 1602 } else { 1603 LastCallFailCause failCause = (LastCallFailCause)ar.result; 1604 causeCode = failCause.causeCode; 1605 vendorCause = failCause.vendorCause; 1606 } 1607 // Log the causeCode if its not normal 1608 if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || 1609 causeCode == CallFailCause.TEMPORARY_FAILURE || 1610 causeCode == CallFailCause.SWITCHING_CONGESTION || 1611 causeCode == CallFailCause.CHANNEL_NOT_AVAIL || 1612 causeCode == CallFailCause.QOS_NOT_AVAIL || 1613 causeCode == CallFailCause.BEARER_NOT_AVAIL || 1614 causeCode == CallFailCause.ERROR_UNSPECIFIED) { 1615 1616 CellLocation loc = mPhone.getCurrentCellIdentity().asCellLocation(); 1617 int cid = -1; 1618 if (loc != null) { 1619 if (loc instanceof GsmCellLocation) { 1620 cid = ((GsmCellLocation)loc).getCid(); 1621 } else if (loc instanceof CdmaCellLocation) { 1622 cid = ((CdmaCellLocation)loc).getBaseStationId(); 1623 } 1624 } 1625 EventLog.writeEvent(EventLogTags.CALL_DROP, causeCode, cid, 1626 TelephonyManager.getDefault().getNetworkType()); 1627 } 1628 1629 if (isEmcRetryCause(causeCode) && mPhone.useImsForEmergency()) { 1630 String dialString = ""; 1631 for(Connection conn : mForegroundCall.mConnections) { 1632 GsmCdmaConnection gsmCdmaConnection = (GsmCdmaConnection)conn; 1633 dialString = gsmCdmaConnection.getOrigDialString(); 1634 gsmCdmaConnection.getCall().detach(gsmCdmaConnection); 1635 mDroppedDuringPoll.remove(gsmCdmaConnection); 1636 } 1637 mPhone.notifyVolteSilentRedial(dialString, causeCode); 1638 updatePhoneState(); 1639 if (mDroppedDuringPoll.isEmpty()) { 1640 log("LAST_CALL_FAIL_CAUSE - no Dropped normal Call"); 1641 return; 1642 } 1643 } 1644 1645 for (int i = 0, s = mDroppedDuringPoll.size(); i < s ; i++) { 1646 GsmCdmaConnection conn = mDroppedDuringPoll.get(i); 1647 1648 conn.onRemoteDisconnect(causeCode, vendorCause); 1649 } 1650 1651 updatePhoneState(); 1652 1653 mPhone.notifyPreciseCallStateChanged(); 1654 mMetrics.writeRilCallList(mPhone.getPhoneId(), mDroppedDuringPoll, 1655 getNetworkCountryIso()); 1656 mPhone.getVoiceCallSessionStats().onRilCallListChanged(mDroppedDuringPoll); 1657 mDroppedDuringPoll.clear(); 1658 break; 1659 1660 case EVENT_REPOLL_AFTER_DELAY: 1661 case EVENT_CALL_STATE_CHANGE: 1662 pollCallsWhenSafe(); 1663 break; 1664 1665 case EVENT_RADIO_AVAILABLE: 1666 handleRadioAvailable(); 1667 break; 1668 1669 case EVENT_RADIO_NOT_AVAILABLE: 1670 handleRadioNotAvailable(); 1671 break; 1672 1673 case EVENT_EXIT_ECM_RESPONSE_CDMA: 1674 if (!isPhoneTypeGsm()) { 1675 // no matter the result, we still do the same here 1676 if (mPendingCallInEcm) { 1677 mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(), 1678 mPendingMO.getEmergencyNumberInfo(), 1679 mPendingMO.hasKnownUserIntentEmergency(), 1680 mPendingCallClirMode, obtainCompleteMessage()); 1681 mPendingCallInEcm = false; 1682 } 1683 mPhone.unsetOnEcbModeExitResponse(this); 1684 } else { 1685 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1686 "phone type " + mPhone.getPhoneType()); 1687 } 1688 break; 1689 1690 case EVENT_CALL_WAITING_INFO_CDMA: 1691 if (!isPhoneTypeGsm()) { 1692 ar = (AsyncResult)msg.obj; 1693 if (ar.exception == null) { 1694 handleCallWaitingInfo((CdmaCallWaitingNotification)ar.result); 1695 Rlog.d(LOG_TAG, "Event EVENT_CALL_WAITING_INFO_CDMA Received"); 1696 } 1697 } else { 1698 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1699 "phone type " + mPhone.getPhoneType()); 1700 } 1701 break; 1702 1703 case EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA: 1704 if (!isPhoneTypeGsm()) { 1705 ar = (AsyncResult)msg.obj; 1706 if (ar.exception == null) { 1707 // Assume 3 way call is connected 1708 mPendingMO.onConnectedInOrOut(); 1709 mPendingMO = null; 1710 } 1711 } else { 1712 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1713 "phone type " + mPhone.getPhoneType()); 1714 } 1715 break; 1716 1717 case EVENT_THREE_WAY_DIAL_BLANK_FLASH: 1718 if (!isPhoneTypeGsm()) { 1719 ar = (AsyncResult) msg.obj; 1720 if (ar.exception == null) { 1721 postDelayed( 1722 new Runnable() { 1723 public void run() { 1724 if (mPendingMO != null) { 1725 mCi.sendCDMAFeatureCode(mPendingMO.getAddress(), 1726 obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA)); 1727 } 1728 } 1729 }, m3WayCallFlashDelay); 1730 } else { 1731 mPendingMO = null; 1732 Rlog.w(LOG_TAG, "exception happened on Blank Flash for 3-way call"); 1733 } 1734 } else { 1735 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1736 "phone type " + mPhone.getPhoneType()); 1737 } 1738 break; 1739 1740 default:{ 1741 throw new RuntimeException("unexpected event " + msg.what + " not handled by " + 1742 "phone type " + mPhone.getPhoneType()); 1743 } 1744 } 1745 } 1746 1747 /** 1748 * Dispatches the CS call radio technology to all exist connections. 1749 * 1750 * @param vrat the RIL voice radio technology for CS calls, 1751 * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}. 1752 */ dispatchCsCallRadioTech(@ilRadioTechnology int vrat)1753 public void dispatchCsCallRadioTech(@RilRadioTechnology int vrat) { 1754 if (mConnections == null) { 1755 log("dispatchCsCallRadioTech: mConnections is null"); 1756 return; 1757 } 1758 for (GsmCdmaConnection gsmCdmaConnection : mConnections) { 1759 if (gsmCdmaConnection != null) { 1760 gsmCdmaConnection.setCallRadioTech(vrat); 1761 } 1762 } 1763 } 1764 1765 //CDMA 1766 /** 1767 * Check and enable data call after an emergency call is dropped if it's 1768 * not in ECM 1769 */ checkAndEnableDataCallAfterEmergencyCallDropped()1770 private void checkAndEnableDataCallAfterEmergencyCallDropped() { 1771 if (mIsInEmergencyCall) { 1772 mIsInEmergencyCall = false; 1773 boolean inEcm = mPhone.isInEcm(); 1774 if (Phone.DEBUG_PHONE) { 1775 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm); 1776 } 1777 if (!inEcm) { 1778 // Re-initiate data connection 1779 mPhone.notifyEmergencyCallRegistrants(false); 1780 } 1781 mPhone.sendEmergencyCallStateChange(false); 1782 } 1783 } 1784 1785 /** 1786 * Check the MT call to see if it's a new ring or 1787 * a unknown connection. 1788 */ checkMtFindNewRinging(DriverCall dc, int i)1789 private Connection checkMtFindNewRinging(DriverCall dc, int i) { 1790 1791 Connection newRinging = null; 1792 1793 // it's a ringing call 1794 if (mConnections[i].getCall() == mRingingCall) { 1795 newRinging = mConnections[i]; 1796 if (Phone.DEBUG_PHONE) log("Notify new ring " + dc); 1797 } else { 1798 // Something strange happened: a call which is neither 1799 // a ringing call nor the one we created. It could be the 1800 // call collision result from RIL 1801 Rlog.e(LOG_TAG,"Phantom call appeared " + dc); 1802 // If it's a connected call, set the connect time so that 1803 // it's non-zero. It may not be accurate, but at least 1804 // it won't appear as a Missed Call. 1805 if (dc.state != DriverCall.State.ALERTING 1806 && dc.state != DriverCall.State.DIALING) { 1807 mConnections[i].onConnectedInOrOut(); 1808 if (dc.state == DriverCall.State.HOLDING) { 1809 // We've transitioned into HOLDING 1810 mConnections[i].onStartedHolding(); 1811 } 1812 } 1813 } 1814 return newRinging; 1815 } 1816 1817 //CDMA 1818 /** 1819 * Check if current call is in emergency call 1820 * 1821 * @return true if it is in emergency call 1822 * false if it is not in emergency call 1823 */ isInEmergencyCall()1824 public boolean isInEmergencyCall() { 1825 return mIsInEmergencyCall; 1826 } 1827 1828 /** 1829 * @return {@code true} if the pending outgoing call or active call is an OTASP call, 1830 * {@code false} otherwise. 1831 */ isInOtaspCall()1832 public boolean isInOtaspCall() { 1833 return mPendingMO != null && mPendingMO.isOtaspCall() 1834 || (mForegroundCall.getConnections().stream() 1835 .filter(connection -> ((connection instanceof GsmCdmaConnection) 1836 && (((GsmCdmaConnection) connection).isOtaspCall()))) 1837 .count() > 0); 1838 } 1839 1840 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) isPhoneTypeGsm()1841 private boolean isPhoneTypeGsm() { 1842 return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM; 1843 } 1844 1845 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1846 @Override getPhone()1847 public GsmCdmaPhone getPhone() { 1848 return mPhone; 1849 } 1850 isEmcRetryCause(int causeCode)1851 private boolean isEmcRetryCause(int causeCode) { 1852 if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) { 1853 log("isEmcRetryCause AP based domain selection ignores the cause"); 1854 return false; 1855 } 1856 if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS || 1857 causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI) { 1858 return true; 1859 } 1860 return false; 1861 } 1862 1863 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1864 @Override log(String msg)1865 protected void log(String msg) { 1866 Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 1867 } 1868 1869 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1870 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1871 pw.println("GsmCdmaCallTracker extends:"); 1872 super.dump(fd, pw, args); 1873 pw.println("mConnections: length=" + mConnections.length); 1874 for(int i=0; i < mConnections.length; i++) { 1875 pw.printf(" mConnections[%d]=%s\n", i, mConnections[i]); 1876 } 1877 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 1878 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 1879 if (!isPhoneTypeGsm()) { 1880 pw.println(" mCallWaitingRegistrants=" + mCallWaitingRegistrants); 1881 } 1882 pw.println(" mDroppedDuringPoll: size=" + mDroppedDuringPoll.size()); 1883 for(int i = 0; i < mDroppedDuringPoll.size(); i++) { 1884 pw.printf( " mDroppedDuringPoll[%d]=%s\n", i, mDroppedDuringPoll.get(i)); 1885 } 1886 pw.println(" mRingingCall=" + mRingingCall); 1887 pw.println(" mForegroundCall=" + mForegroundCall); 1888 pw.println(" mBackgroundCall=" + mBackgroundCall); 1889 pw.println(" mPendingMO=" + mPendingMO); 1890 pw.println(" mHangupPendingMO=" + mHangupPendingMO); 1891 pw.println(" mPhone=" + mPhone); 1892 pw.println(" mDesiredMute=" + mDesiredMute); 1893 pw.println(" mState=" + mState); 1894 if (!isPhoneTypeGsm()) { 1895 pw.println(" mPendingCallInEcm=" + mPendingCallInEcm); 1896 pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall); 1897 pw.println(" mPendingCallClirMode=" + mPendingCallClirMode); 1898 } 1899 1900 } 1901 1902 @Override getState()1903 public PhoneConstants.State getState() { 1904 return mState; 1905 } 1906 getMaxConnectionsPerCall()1907 public int getMaxConnectionsPerCall() { 1908 return mPhone.isPhoneTypeGsm() ? 1909 MAX_CONNECTIONS_PER_CALL_GSM : 1910 MAX_CONNECTIONS_PER_CALL_CDMA; 1911 } 1912 getNetworkCountryIso()1913 private String getNetworkCountryIso() { 1914 String countryIso = ""; 1915 if (mPhone != null) { 1916 ServiceStateTracker sst = mPhone.getServiceStateTracker(); 1917 if (sst != null) { 1918 LocaleTracker lt = sst.getLocaleTracker(); 1919 if (lt != null) { 1920 countryIso = lt.getCurrentCountry(); 1921 } 1922 } 1923 } 1924 return countryIso; 1925 } 1926 1927 /** 1928 * Called to force the call tracker to cleanup any stale calls. Does this by triggering 1929 * {@code GET_CURRENT_CALLS} on the RIL. 1930 */ 1931 @Override cleanupCalls()1932 public void cleanupCalls() { 1933 pollCallsWhenSafe(); 1934 } 1935 hangupWaitingCallSilently(int index)1936 private boolean hangupWaitingCallSilently(int index) { 1937 if (index < 0 || index >= mConnections.length) return false; 1938 1939 GsmCdmaConnection newRinging = mConnections[index]; 1940 if (newRinging == null) return false; 1941 1942 if ((mPhone.getTerminalBasedCallWaitingState(true) 1943 == CallWaitingController.TERMINAL_BASED_NOT_ACTIVATED) 1944 && (newRinging.getState() == Call.State.WAITING)) { 1945 Rlog.d(LOG_TAG, "hangupWaitingCallSilently"); 1946 newRinging.dispose(); 1947 mConnections[index] = null; 1948 mCi.hangupWaitingOrBackground(obtainCompleteMessage()); 1949 return true; 1950 } 1951 return false; 1952 } 1953 } 1954