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