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