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