Merge "Fix error case which session change request failed when pausing video"
diff --git a/java/com/android/incallui/CallButtonPresenter.java b/java/com/android/incallui/CallButtonPresenter.java
index 7d12d52..f49eb2b 100644
--- a/java/com/android/incallui/CallButtonPresenter.java
+++ b/java/com/android/incallui/CallButtonPresenter.java
@@ -42,6 +42,7 @@
 import com.android.incallui.call.CallList;
 import com.android.incallui.call.DialerCall;
 import com.android.incallui.call.DialerCall.CameraDirection;
+import com.android.incallui.call.DialerCallListener;
 import com.android.incallui.call.TelecomAdapter;
 import com.android.incallui.call.state.DialerCallState;
 import com.android.incallui.incall.protocol.InCallButtonIds;
@@ -58,7 +59,8 @@
         InCallDetailsListener,
         CanAddCallListener,
         Listener,
-        InCallButtonUiDelegate {
+        InCallButtonUiDelegate,
+        DialerCallListener {
 
   private static final String KEY_AUTOMATICALLY_MUTED_BY_ADD_CALL =
       "incall_key_automatically_muted_by_add_call";
@@ -106,11 +108,18 @@
     InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
     InCallPresenter.getInstance().removeCanAddCallListener(this);
     isInCallButtonUiReady = false;
+
+    if (call != null) {
+      call.removeListener(this);
+    }
   }
 
   @Override
   public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
     Trace.beginSection("CallButtonPresenter.onStateChange");
+    if (call != null) {
+      call.removeListener(this);
+    }
     if (newState == InCallState.OUTGOING) {
       call = callList.getOutgoingCall();
     } else if (newState == InCallState.INCALL) {
@@ -133,6 +142,10 @@
     } else {
       call = null;
     }
+
+    if (call != null) {
+      call.addListener(this);
+    }
     updateUi(newState, call);
     Trace.endSection();
   }
@@ -393,7 +406,6 @@
             call.getTimeAddedMs());
 
     if (pause) {
-      call.getVideoTech().setCamera(null);
       call.getVideoTech().stopTransmission();
     } else {
       updateCamera(
@@ -584,6 +596,41 @@
   }
 
   @Override
+  public void onDialerCallSessionModificationStateChange() {
+    if (inCallButtonUi != null && call != null) {
+      inCallButtonUi.enableButton(InCallButtonIds.BUTTON_PAUSE_VIDEO, true);
+      updateButtonsState(call);
+    }
+  }
+
+  @Override
+  public void onDialerCallDisconnect() {}
+
+  @Override
+  public void onDialerCallUpdate() {}
+
+  @Override
+  public void onDialerCallChildNumberChange() {}
+
+  @Override
+  public void onDialerCallLastForwardedNumberChange() {}
+
+  @Override
+  public void onDialerCallUpgradeToVideo() {}
+
+  @Override
+  public void onWiFiToLteHandover() {}
+
+  @Override
+  public void onHandoverToWifiFailure() {}
+
+  @Override
+  public void onInternationalCallOnWifi() {}
+
+  @Override
+  public void onEnrichedCallSessionUpdate() {}
+
+  @Override
   public Context getContext() {
     return context;
   }
diff --git a/java/com/android/incallui/contactgrid/TopRow.java b/java/com/android/incallui/contactgrid/TopRow.java
index d242c3a..213a3c6 100644
--- a/java/com/android/incallui/contactgrid/TopRow.java
+++ b/java/com/android/incallui/contactgrid/TopRow.java
@@ -92,6 +92,8 @@
     } else if (VideoUtils.hasSentVideoUpgradeRequest(state.sessionModificationState())
         || VideoUtils.hasReceivedVideoUpgradeRequest(state.sessionModificationState())) {
       label = getLabelForVideoRequest(context, state);
+    } else if (state.sessionModificationState() == SessionModificationState.REQUEST_FAILED) {
+      label = context.getString(R.string.incall_video_call_operation_failed);
     } else if (state.state() == DialerCallState.PULLING) {
       label = context.getString(R.string.incall_transferring);
     } else if (state.state() == DialerCallState.DIALING
diff --git a/java/com/android/incallui/contactgrid/res/values/strings.xml b/java/com/android/incallui/contactgrid/res/values/strings.xml
index 9ee10c3..e8592b2 100644
--- a/java/com/android/incallui/contactgrid/res/values/strings.xml
+++ b/java/com/android/incallui/contactgrid/res/values/strings.xml
@@ -65,6 +65,9 @@
        requests and we timed out. -->
   <string name="incall_video_call_request_timed_out">Call timed out</string>
 
+  <!-- Displayed above the contact name when the user's operation for video calling is failed due to an unknown reason. -->
+  <string name="incall_video_call_operation_failed">Unable to operate</string>
+
   <!-- In-call screen: status label for a call that's in the process of hanging up
        [CHAR LIMIT=25] -->
   <string name="incall_hanging_up">Hanging up</string>
diff --git a/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java b/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
index d254d6d..3e6f441 100644
--- a/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
+++ b/java/com/android/incallui/videotech/ims/ImsVideoCallCallback.java
@@ -103,8 +103,6 @@
 
     if (videoTech.getSessionModificationState()
         == SessionModificationState.WAITING_FOR_UPGRADE_TO_VIDEO_RESPONSE) {
-      handler.removeCallbacksAndMessages(null); // Clear everything
-
       final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status);
       if (status == VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
         // Telecom manages audio route for us
@@ -114,31 +112,21 @@
         videoTech.setSessionModificationState(newSessionModificationState);
       }
 
-      // Wait for 4 seconds and then clean the session modification state. This allows the video UI
-      // to stay up so that the user can read the error message.
-      //
       // If the other person accepted the upgrade request then this will keep the video UI up until
       // the call's video state change. Without this we would switch to the voice call and then
       // switch back to video UI.
-      handler.postDelayed(
-          () -> {
-            if (videoTech.getSessionModificationState() == newSessionModificationState) {
-              LogUtil.i("ImsVideoCallCallback.onSessionModifyResponseReceived", "clearing state");
-              videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST);
-            } else {
-              LogUtil.i(
-                  "ImsVideoCallCallback.onSessionModifyResponseReceived",
-                  "session modification state has changed, not clearing state");
-            }
-          },
-          CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS);
+      clearFailedResponseState(newSessionModificationState);
     } else if (videoTech.getSessionModificationState()
         == SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
       requestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
       videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST);
     } else if (videoTech.getSessionModificationState()
         == SessionModificationState.WAITING_FOR_RESPONSE) {
-      videoTech.setSessionModificationState(getSessionModificationStateFromTelecomStatus(status));
+      final int newSessionModificationState = getSessionModificationStateFromTelecomStatus(status);
+      videoTech.setSessionModificationState(newSessionModificationState);
+      if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
+        clearFailedResponseState(newSessionModificationState);
+      }
     } else {
       LogUtil.i(
           "ImsVideoCallCallback.onSessionModifyResponseReceived",
@@ -146,6 +134,24 @@
     }
   }
 
+  private void clearFailedResponseState(final int newSessionModificationState) {
+    handler.removeCallbacksAndMessages(null); // Clear everything
+    // Wait for 4 seconds and then clean the session modification state. This allows the video UI
+    // to stay up so that the user can read the error message.
+    handler.postDelayed(
+        () -> {
+          if (videoTech.getSessionModificationState() == newSessionModificationState) {
+            LogUtil.i("ImsVideoCallCallback.onSessionModifyResponseReceived", "clearing state");
+            videoTech.setSessionModificationState(SessionModificationState.NO_REQUEST);
+          } else {
+            LogUtil.i(
+                "ImsVideoCallCallback.onSessionModifyResponseReceived",
+                "session modification state has changed, not clearing state");
+          }
+        },
+        CLEAR_FAILED_REQUEST_TIMEOUT_MILLIS);
+  }
+
   @SessionModificationState
   private int getSessionModificationStateFromTelecomStatus(int telecomStatus) {
     switch (telecomStatus) {
diff --git a/java/com/android/incallui/videotech/ims/ImsVideoTech.java b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
index 5b733d6..1d4fe76 100644
--- a/java/com/android/incallui/videotech/ims/ImsVideoTech.java
+++ b/java/com/android/incallui/videotech/ims/ImsVideoTech.java
@@ -227,6 +227,7 @@
     call.getVideoCall()
         .sendSessionModifyRequest(
             new VideoProfile(unpausedVideoState & ~VideoProfile.STATE_TX_ENABLED));
+    setSessionModificationState(SessionModificationState.WAITING_FOR_RESPONSE);
   }
 
   @Override