Fix answering waiting call simultaneously with active call disconnect

When the user answers a call-waiting call at the same time as the remote
end disconnects the formerly-active call, an error may occur where the
in-call ui is left in an inconsistent state and the waiting call is
never answered.

Normally, when a call-waiting call is answered, the active call will
first be held. Then, after the modem reports that as successful,
onCallHeld will issue the EVENT_RESUME_BACKGROUND message to answer the
call-waiting call. However, if the remote end disconnects while the user
is answering, onCallTerminated will be called instead.

This change alters onCallTerminated to issue the EVENT_RESUME_BACKGROUND
message if it occurs in the middle of a call-switch.

Change-Id: Icb757274ad63af46ebff5028dc484f8497777987
Fix: 31684640
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 6f5fa3e..062743c 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -776,8 +776,16 @@
 
             // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls.
             // If hold or resume later fails, we will swap them back.
+            boolean switchingWithWaitingCall = mBackgroundCall.getImsCall() == null &&
+                    mRingingCall != null &&
+                    mRingingCall.getState() == ImsPhoneCall.State.WAITING;
+
             mSwitchingFgAndBgCalls = true;
-            mCallExpectedToResume = mBackgroundCall.getImsCall();
+            if (switchingWithWaitingCall) {
+                mCallExpectedToResume = mRingingCall.getImsCall();
+            } else {
+                mCallExpectedToResume = mBackgroundCall.getImsCall();
+            }
             mForegroundCall.switchWith(mBackgroundCall);
 
             // Hold the foreground call; once the foreground call is held, the background call will
@@ -788,6 +796,7 @@
 
                 // If there is no background call to resume, then don't expect there to be a switch.
                 if (mCallExpectedToResume == null) {
+                    log("mCallExpectedToResume is null");
                     mSwitchingFgAndBgCalls = false;
                 }
             } catch (ImsException e) {
@@ -1446,6 +1455,16 @@
         public void onCallStarted(ImsCall imsCall) {
             if (DBG) log("onCallStarted");
 
+            if (mSwitchingFgAndBgCalls) {
+                // If we put a call on hold to answer an incoming call, we should reset the
+                // variables that keep track of the switch here.
+                if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) {
+                    if (DBG) log("onCallStarted: starting a call as a result of a switch.");
+                    mSwitchingFgAndBgCalls = false;
+                    mCallExpectedToResume = null;
+                }
+            }
+
             mPendingMO = null;
             processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
                     DisconnectCause.NOT_DISCONNECTED);
@@ -1483,6 +1502,16 @@
         public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
             if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
 
+            if (mSwitchingFgAndBgCalls) {
+                // If we put a call on hold to answer an incoming call, we should reset the
+                // variables that keep track of the switch here.
+                if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) {
+                    if (DBG) log("onCallStarted: starting a call as a result of a switch.");
+                    mSwitchingFgAndBgCalls = false;
+                    mCallExpectedToResume = null;
+                }
+            }
+
             if (mPendingMO != null) {
                 // To initiate dialing circuit-switched call
                 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
@@ -1591,7 +1620,12 @@
                 }
                 // This call terminated in the midst of a switch after the other call was held, so
                 // resume it back to ACTIVE state since the switch failed.
-                if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) {
+                log("onCallTerminated: foreground call in state " + mForegroundCall.getState() +
+                        " and ringing call in state " + (mRingingCall == null ? "null" :
+                        mRingingCall.getState().toString()));
+
+                if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING ||
+                        mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
                     sendEmptyMessage(EVENT_RESUME_BACKGROUND);
                     mSwitchingFgAndBgCalls = false;
                     mCallExpectedToResume = null;