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