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