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