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