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