Status labels for video calls.

- Display the appropriate status labels for video calls, mostly
depending on whether one is in a video call, and whether one
has requested video and is waiting for a response, or a video
request has failed.
- Add a handler to the CallCardPresenter, so we can show an error
message in the fail case, but only show the message for a time.
- Add video icon in layout.
- Renamed CallVideoClientNotifier to reflect newer nomenclature.
- Add session modification state information on InCallUI call. This
helps us track state to know what strings to display on the card.

Bug: 16013340
Change-Id: Ib2bf84d93a05664adbf7fe838848b7d7b54a8254
diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml
index 474a3aa..cc53e05 100644
--- a/InCallUI/res/layout/primary_call_info.xml
+++ b/InCallUI/res/layout/primary_call_info.xml
@@ -44,13 +44,23 @@
         <ImageView android:id="@+id/callStateIcon"
             android:layout_width="16dp"
             android:layout_height="16dp"
-            android:layout_marginRight="4dp"
+            android:layout_marginEnd="4dp"
             android:baselineAlignBottom="true"
             android:tint="@color/incall_accent_color"
             android:alpha="0.7"
             android:scaleType="centerInside"
             android:visibility="gone" />
 
+        <ImageView android:id="@+id/videoCallIcon"
+            android:src="@drawable/ic_toolbar_video"
+            android:layout_width="16dp"
+            android:layout_height="16dp"
+            android:layout_marginEnd="4dp"
+            android:baselineAlignBottom="true"
+            android:tint="@color/incall_accent_color"
+            android:scaleType="centerInside"
+            android:visibility="gone" />
+
         <TextView android:id="@+id/callStateLabel"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index f145bfd..9c0ce64 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -98,6 +98,16 @@
         }
     }
 
+    /**
+     * Defines different states of session modify requests, which are used to upgrade to video, or
+     * downgrade to audio.
+     */
+    public static class SessionModificationState {
+        public static final int NO_REQUEST = 0;
+        public static final int WAITING_FOR_RESPONSE = 1;
+        public static final int REQUEST_FAILED = 1;
+    }
+
     private static final String ID_PREFIX = Call.class.getSimpleName() + "_";
     private static int sIdCounter = 0;
 
@@ -161,6 +171,7 @@
     private int mState = State.INVALID;
     private int mDisconnectCause;
     private String mParentCallId;
+    private int mSessionModificationState;
     private final List<String> mChildCallIds = new ArrayList<>();
 
     private InCallVideoCallListener mVideoCallListener;
@@ -335,6 +346,19 @@
         return VideoCallProfile.VideoState.isBidirectional(getVideoState());
     }
 
+    public void setSessionModificationState(int state) {
+        boolean hasChanged = mSessionModificationState != state;
+        mSessionModificationState = state;
+
+        if (hasChanged) {
+            update();
+        }
+    }
+
+    public int getSessionModificationState() {
+        return mSessionModificationState;
+    }
+
     @Override
     public String toString() {
         return String.format(Locale.US, "[%s, %s, %s, children:%s, parent:%s, videoState:%d]",
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index a5b3073..2e2ea4e 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -234,6 +234,8 @@
         VideoCallProfile videoCallProfile =
                 new VideoCallProfile(VideoCallProfile.VideoState.BIDIRECTIONAL);
         videoCall.sendSessionModifyRequest(videoCallProfile);
+
+        mCall.setSessionModificationState(Call.SessionModificationState.REQUEST_FAILED);
     }
 
     /**
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 87e947c..cb89b85 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -26,6 +26,7 @@
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.telecomm.VideoCallProfile;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
@@ -65,6 +66,7 @@
     private TextView mPrimaryName;
     private View mCallStateButton;
     private ImageView mCallStateIcon;
+    private ImageView mCallStateVideoCallIcon;
     private TextView mCallStateLabel;
     private TextView mCallTypeLabel;
     private View mCallNumberAndLabel;
@@ -159,6 +161,7 @@
         mPhoto = (ImageView) view.findViewById(R.id.photo);
         mCallStateButton = view.findViewById(R.id.callStateButton);
         mCallStateIcon = (ImageView) view.findViewById(R.id.callStateIcon);
+        mCallStateVideoCallIcon = (ImageView) view.findViewById(R.id.videoCallIcon);
         mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel);
         mCallNumberAndLabel = view.findViewById(R.id.labelAndNumber);
         mCallTypeLabel = (TextView) view.findViewById(R.id.callTypeLabel);
@@ -461,11 +464,11 @@
     }
 
     @Override
-    public void setCallState(int state, int cause, String connectionLabel, Drawable connectionIcon,
-            String gatewayNumber) {
+    public void setCallState(int state, int videoState, int sessionModificationState, int cause,
+            String connectionLabel, Drawable connectionIcon, String gatewayNumber) {
         boolean isGatewayCall = !TextUtils.isEmpty(gatewayNumber);
         String callStateLabel = getCallStateLabelFromState(
-                state, cause, connectionLabel, isGatewayCall);
+                state, videoState, sessionModificationState, cause, connectionLabel, isGatewayCall);
 
         Log.v(this, "setCallState " + callStateLabel);
         Log.v(this, "DisconnectCause " + DisconnectCause.toString(cause));
@@ -484,6 +487,14 @@
                 mCallStateIcon.setImageDrawable(connectionIcon);
             }
 
+            if (VideoCallProfile.VideoState.isBidirectional(videoState)
+                    || (state == Call.State.ACTIVE && sessionModificationState
+                            == Call.SessionModificationState.WAITING_FOR_RESPONSE)) {
+                mCallStateVideoCallIcon.setVisibility(View.VISIBLE);
+            } else {
+                mCallStateVideoCallIcon.setVisibility(View.GONE);
+            }
+
             if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
                 mCallStateLabel.clearAnimation();
             } else {
@@ -496,10 +507,11 @@
             }
             mCallStateLabel.setAlpha(0);
             mCallStateLabel.setVisibility(View.GONE);
+
+            mCallStateVideoCallIcon.setVisibility(View.GONE);
         }
     }
 
-
     @Override
     public void setCallDetails(android.telecomm.Call.Details details) {
     }
@@ -575,15 +587,16 @@
     }
 
     /**
-     * Gets the call state label based on the state of the call and
-     * cause of disconnect
+     * Gets the call state label based on the state of the call or cause of disconnect.
      *
      * Additional labels are applied as follows:
-     *         1. All outgoing calls with display "Calling via [Provider]"
-     *         2. Ongoing calls will display the name of the provider
-     *         2. Incoming calls will only display "Incoming via..." for accounts
+     *         1. All outgoing calls with display "Calling via [Provider]".
+     *         2. Ongoing calls will display the name of the provider.
+     *         3. Incoming calls will only display "Incoming via..." for accounts.
+     *         4. Video calls, and session modification states (eg. requesting video).
      */
-    private String getCallStateLabelFromState(int state, int cause, String label,
+    private String getCallStateLabelFromState(int state, int videoState,
+            int sessionModificationState, int disconnectCause, String label,
             boolean isGatewayCall) {
         final Context context = getView().getContext();
         String callStateLabel = null;  // Label to display as part of the call banner
@@ -600,6 +613,14 @@
                 // (but we can use the call state label to display the provider name).
                 if (isAccount) {
                     callStateLabel = label;
+                } else if (sessionModificationState
+                        == Call.SessionModificationState.REQUEST_FAILED) {
+                    callStateLabel = context.getString(R.string.card_title_video_call_error);
+                } else if (sessionModificationState
+                        == Call.SessionModificationState.WAITING_FOR_RESPONSE) {
+                    callStateLabel = context.getString(R.string.card_title_video_call_requesting);
+                } else if (VideoCallProfile.VideoState.isBidirectional(videoState)) {
+                    callStateLabel = context.getString(R.string.card_title_video_call);
                 }
                 break;
             case Call.State.ONHOLD:
@@ -619,6 +640,8 @@
             case Call.State.CALL_WAITING:
                 if (isAccount) {
                     callStateLabel = context.getString(R.string.incoming_via_template, label);
+                } else if (VideoCallProfile.VideoState.isBidirectional(videoState)) {
+                    callStateLabel = context.getString(R.string.notification_incoming_video_call);
                 } else {
                     callStateLabel = context.getString(R.string.card_title_incoming_call);
                 }
@@ -633,7 +656,7 @@
                 callStateLabel = context.getString(R.string.card_title_hanging_up);
                 break;
             case Call.State.DISCONNECTED:
-                callStateLabel = getCallFailedString(cause);
+                callStateLabel = getCallFailedString(disconnectCause);
                 break;
             default:
                 Log.wtf(this, "updateCallStateWidgets: unexpected call: " + state);
@@ -646,14 +669,14 @@
      *
      * @param cause disconnect cause as defined in {@link DisconnectCause}
      */
-    private String getCallFailedString(int cause) {
+    private String getCallFailedString(int disconnectCause) {
         int resID = R.string.card_title_call_ended;
 
         // TODO: The card *title* should probably be "Call ended" in all
         // cases, but if the DisconnectCause was an error condition we should
         // probably also display the specific failure reason somewhere...
 
-        switch (cause) {
+        switch (disconnectCause) {
             case DisconnectCause.BUSY:
                 resID = R.string.callFailed_userBusy;
                 break;
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index bb6edfb..e01a4bc 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -22,11 +22,13 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.telecomm.CallCapabilities;
 import android.telecomm.PhoneAccount;
 import android.telecomm.PhoneAccountHandle;
 import android.telecomm.StatusHints;
 import android.telecomm.TelecommManager;
+import android.telecomm.VideoCallProfile;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
@@ -53,16 +55,19 @@
  */
 public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
         implements InCallStateListener, AudioModeListener, IncomingCallListener,
-        InCallDetailsListener, InCallEventListener {
+        InCallDetailsListener, InCallEventListener,
+        InCallVideoCallListenerNotifier.SessionModificationListener {
 
     private static final String TAG = CallCardPresenter.class.getSimpleName();
-    private static final long CALL_TIME_UPDATE_INTERVAL = 1000; // in milliseconds
+    private static final long CALL_TIME_UPDATE_INTERVAL_MS = 1000;
+    private static final long SESSION_MODIFICATION_RESET_DELAY_MS = 3000;
 
     private Call mPrimary;
     private Call mSecondary;
     private ContactCacheEntry mPrimaryContactInfo;
     private ContactCacheEntry mSecondaryContactInfo;
     private CallTimer mCallTimer;
+    private Handler mSessionModificationResetHandler;
     private Context mContext;
     private TelecommManager mTelecommManager;
 
@@ -101,6 +106,7 @@
                 updateCallTime();
             }
         });
+        mSessionModificationResetHandler = new Handler();
     }
 
     public void init(Context context, Call call) {
@@ -135,12 +141,16 @@
         InCallPresenter.getInstance().addIncomingCallListener(this);
         InCallPresenter.getInstance().addDetailsListener(this);
         InCallPresenter.getInstance().addInCallEventListener(this);
+
+        InCallVideoCallListenerNotifier.getInstance().addSessionModificationListener(this);
     }
 
     @Override
     public void onUiUnready(CallCardUi ui) {
         super.onUiUnready(ui);
 
+        InCallVideoCallListenerNotifier.getInstance().removeSessionModificationListener(this);
+
         // stop getting call state changes
         InCallPresenter.getInstance().removeListener(this);
         InCallPresenter.getInstance().removeIncomingCallListener(this);
@@ -198,6 +208,7 @@
                     mPrimary.getState() == Call.State.INCOMING);
             updatePrimaryDisplayInfo(mPrimaryContactInfo, isConference(mPrimary));
             maybeStartSearch(mPrimary, true);
+            mPrimary.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
         }
 
         if (mSecondary == null) {
@@ -210,12 +221,13 @@
                     mSecondary.getState() == Call.State.INCOMING);
             updateSecondaryDisplayInfo(mSecondary.isConferenceCall());
             maybeStartSearch(mSecondary, false);
+            mSecondary.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
         }
 
-        // Start/Stop the call time update timer
+        // Start/stop timers.
         if (mPrimary != null && mPrimary.getState() == Call.State.ACTIVE) {
             Log.d(this, "Starting the calltime timer");
-            mCallTimer.start(CALL_TIME_UPDATE_INTERVAL);
+            mCallTimer.start(CALL_TIME_UPDATE_INTERVAL_MS);
         } else {
             Log.d(this, "Canceling the calltime timer");
             mCallTimer.cancel();
@@ -227,8 +239,14 @@
         if (mPrimary != null) {
             callState = mPrimary.getState();
 
-            getUi().setCallState(callState, mPrimary.getDisconnectCause(), getConnectionLabel(),
-                    getCallProviderIcon(mPrimary), getGatewayNumber());
+            getUi().setCallState(
+                    callState,
+                    mPrimary.getVideoState(),
+                    mPrimary.getSessionModificationState(),
+                    mPrimary.getDisconnectCause(),
+                    getConnectionLabel(),
+                    getCallProviderIcon(mPrimary),
+                    getGatewayNumber());
 
             String currentNumber = getNumberFromHandle(mPrimary.getHandle());
             if (PhoneNumberUtils.isEmergencyNumber(currentNumber)) {
@@ -236,7 +254,14 @@
                 setCallbackNumber(callbackNumber, true);
             }
         } else {
-            getUi().setCallState(callState, DisconnectCause.NOT_VALID, null, null, null);
+            getUi().setCallState(
+                    callState,
+                    VideoCallProfile.VideoState.AUDIO_ONLY,
+                    Call.SessionModificationState.NO_REQUEST,
+                    DisconnectCause.NOT_VALID,
+                    null,
+                    null,
+                    null);
         }
 
         // Hide/show the contact photo based on the video state.
@@ -352,6 +377,42 @@
         TelecommAdapter.getInstance().phoneAccountClicked(mPrimary.getId());
     }
 
+    @Override
+    public void onUpgradeToVideoRequest(Call call) {
+        // Implementing to satsify interface.
+    }
+
+    @Override
+    public void onUpgradeToVideoSuccess(Call call) {
+        if (mPrimary == null || !areCallsSame(mPrimary, call)) {
+            return;
+        }
+
+        mPrimary.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
+    }
+
+    @Override
+    public void onUpgradeToVideoFail(Call call) {
+        if (mPrimary == null || !areCallsSame(mPrimary, call)) {
+            return;
+        }
+
+        call.setSessionModificationState(Call.SessionModificationState.REQUEST_FAILED);
+
+        // Start handler to change state from REQUEST_FAILED to NO_REQUEST after an interval.
+        mSessionModificationResetHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mPrimary.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
+            }
+        }, SESSION_MODIFICATION_RESET_DELAY_MS);
+    }
+
+    @Override
+    public void onDowngradeToAudio(Call call) {
+        // Implementing to satsify interface.
+    }
+
     private boolean areCallsSame(Call call1, Call call2) {
         if (call1 == null && call2 == null) {
             return true;
@@ -652,8 +713,8 @@
         void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
                 String providerLabel, Drawable providerIcon, boolean isConference,
                 boolean isGeneric);
-        void setCallState(int state, int cause, String connectionLabel, Drawable connectionIcon,
-                String gatewayNumber);
+        void setCallState(int state, int videoState, int sessionModificationState, int cause,
+                String connectionLabel, Drawable connectionIcon, String gatewayNumber);
         void setPrimaryCallElapsedTime(boolean show, String duration);
         void setPrimaryName(String name, boolean nameIsNumber);
         void setPrimaryImage(Drawable image);
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallListener.java b/InCallUI/src/com/android/incallui/InCallVideoCallListener.java
index 2e0f12b..a3ebf78 100644
--- a/InCallUI/src/com/android/incallui/InCallVideoCallListener.java
+++ b/InCallUI/src/com/android/incallui/InCallVideoCallListener.java
@@ -49,27 +49,21 @@
         int previousVideoState = mCall.getVideoState();
         int newVideoState = videoCallProfile.getVideoState();
 
-        boolean wasVideoState = isVideoStateSet(previousVideoState,
-                VideoCallProfile.VideoState.BIDIRECTIONAL);
-        boolean isVideoState = isVideoStateSet(newVideoState,
-                VideoCallProfile.VideoState.BIDIRECTIONAL);
+        boolean wasVideoCall = VideoCallProfile.VideoState.isBidirectional(previousVideoState);
+        boolean isVideoCall = VideoCallProfile.VideoState.isBidirectional(newVideoState);
 
-        boolean wasPaused = isVideoStateSet(previousVideoState, VideoCallProfile.VideoState.PAUSED);
-        boolean isPaused = isVideoStateSet(newVideoState, VideoCallProfile.VideoState.PAUSED);
+        boolean wasPaused = VideoCallProfile.VideoState.isPaused(previousVideoState);
+        boolean isPaused = VideoCallProfile.VideoState.isPaused(newVideoState);
 
         // Check for upgrades to video and downgrades to audio.
-        if (!wasVideoState && isVideoState) {
-            CallVideoClientNotifier.getInstance().upgradeToVideoRequest(mCall);
-        } else if (wasVideoState && !isVideoState) {
-            CallVideoClientNotifier.getInstance().downgradeToAudio(mCall);
+        if (!wasVideoCall && isVideoCall) {
+            InCallVideoCallListenerNotifier.getInstance().upgradeToVideoRequest(mCall);
+        } else if (wasVideoCall && !isVideoCall) {
+            InCallVideoCallListenerNotifier.getInstance().downgradeToAudio(mCall);
         }
 
-        // Check for pause/un-pause
-        if (!wasPaused && isPaused) {
-            CallVideoClientNotifier.getInstance().peerPausedStateChanged(mCall, true /* pause */);
-        } else {
-            CallVideoClientNotifier.getInstance().peerPausedStateChanged(mCall, false /* resume */);
-        }
+        boolean pause = !wasPaused && isPaused;
+        InCallVideoCallListenerNotifier.getInstance().peerPausedStateChanged(mCall, pause);
     }
 
     /**
@@ -83,8 +77,18 @@
      * @param responseProfile The actual profile changes made by the peer device.
      */
     @Override
-    public void onSessionModifyResponseReceived(int status, VideoCallProfile requestedProfile,
-            VideoCallProfile responseProfile) {
+    public void onSessionModifyResponseReceived(
+            int status, VideoCallProfile requestedProfile, VideoCallProfile responseProfile) {
+        boolean modifySucceeded =
+                requestedProfile.getVideoState() == responseProfile.getVideoState();
+        boolean isVideoCall =
+                VideoCallProfile.VideoState.isBidirectional(responseProfile.getVideoState());
+
+        if (modifySucceeded && isVideoCall) {
+            InCallVideoCallListenerNotifier.getInstance().upgradeToVideoSuccess(mCall);
+        } else if (!modifySucceeded || status != VideoCall.SESSION_MODIFY_REQUEST_SUCCESS) {
+            InCallVideoCallListenerNotifier.getInstance().upgradeToVideoFail(mCall);
+        }
     }
 
     /**
@@ -104,7 +108,7 @@
      */
     @Override
     public void onPeerDimensionsChanged(int width, int height) {
-        CallVideoClientNotifier.getInstance().peerDimensionsChanged(mCall, width, height);
+        InCallVideoCallListenerNotifier.getInstance().peerDimensionsChanged(mCall, width, height);
     }
 
     /**
@@ -125,18 +129,7 @@
      */
     @Override
     public void onCameraCapabilitiesChanged(CallCameraCapabilities callCameraCapabilities) {
-        CallVideoClientNotifier.getInstance().cameraDimensionsChanged(mCall,
-                callCameraCapabilities.getWidth(), callCameraCapabilities.getHeight());
-    }
-
-    /**
-     * Determines if a specified state is set in a videoState bit-mask.
-     *
-     * @param videoState The video state bit-mask.
-     * @param state The state to check.
-     * @return {@code True} if the state is set.
-     */
-    private boolean isVideoStateSet(int videoState, int state) {
-        return (videoState & state) == state;
+        InCallVideoCallListenerNotifier.getInstance().cameraDimensionsChanged(
+                mCall, callCameraCapabilities.getWidth(), callCameraCapabilities.getHeight());
     }
 }
diff --git a/InCallUI/src/com/android/incallui/CallVideoClientNotifier.java b/InCallUI/src/com/android/incallui/InCallVideoCallListenerNotifier.java
similarity index 80%
rename from InCallUI/src/com/android/incallui/CallVideoClientNotifier.java
rename to InCallUI/src/com/android/incallui/InCallVideoCallListenerNotifier.java
index cc8b61d..0e64cd7 100644
--- a/InCallUI/src/com/android/incallui/CallVideoClientNotifier.java
+++ b/InCallUI/src/com/android/incallui/InCallVideoCallListenerNotifier.java
@@ -22,13 +22,14 @@
 import java.util.Set;
 
 /**
- * Class used by {@link InCallVideoClient}s to notify interested parties of incoming events.
+ * Class used by {@link InCallService.VideoCallListener} to notify interested parties of incoming
+ * events.
  */
-public class CallVideoClientNotifier {
+public class InCallVideoCallListenerNotifier {
     /**
      * Singleton instance of this class.
      */
-    private static CallVideoClientNotifier sInstance = new CallVideoClientNotifier();
+    private static InCallVideoCallListenerNotifier sInstance = new InCallVideoCallListenerNotifier();
 
     private final Set<SessionModificationListener> mSessionModificationListeners =
             Sets.newHashSet();
@@ -38,14 +39,14 @@
     /**
      * Static singleton accessor method.
      */
-    public static CallVideoClientNotifier getInstance() {
+    public static InCallVideoCallListenerNotifier getInstance() {
         return sInstance;
     }
 
     /**
      * Private constructor.  Instance should only be acquired through getInstance().
      */
-    private CallVideoClientNotifier() {
+    private InCallVideoCallListenerNotifier() {
     }
 
     /**
@@ -120,6 +121,28 @@
     }
 
     /**
+     * Inform listeners of a successful response to a video request for a call.
+     *
+     * @param call The call.
+     */
+    public void upgradeToVideoSuccess(Call call) {
+        for (SessionModificationListener listener : mSessionModificationListeners) {
+            listener.onUpgradeToVideoSuccess(call);
+        }
+    }
+
+    /**
+     * Inform listeners of an unsuccessful response to a video request for a call.
+     *
+     * @param call The call.
+     */
+    public void upgradeToVideoFail(Call call) {
+        for (SessionModificationListener listener : mSessionModificationListeners) {
+            listener.onUpgradeToVideoFail(call);
+        }
+    }
+
+    /**
      * Inform listeners of a downgrade to audio.
      *
      * @param call The call.
@@ -181,6 +204,23 @@
         public void onUpgradeToVideoRequest(Call call);
 
         /**
+         * Called when a request to a peer to upgrade an audio-only call to a video call is
+         * successful.
+         *
+         * @param call The call the request was successful for.
+         */
+        public void onUpgradeToVideoSuccess(Call call);
+
+        /**
+         * Called when a request to a peer to upgrade an audio-only call to a video call is
+         * NOT successful. This can be if the peer chooses rejects the the video call, or if the
+         * peer does not support video calling, or if there is some error in sending the request.
+         *
+         * @param call The call the request was successful for.
+         */
+        public void onUpgradeToVideoFail(Call call);
+
+        /**
          * Called when a call has been downgraded to audio-only.
          *
          * @param call The call which was downgraded to audio-only.
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index 87d09b1..0aea18c 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -17,9 +17,8 @@
 package com.android.incallui;
 
 import com.google.common.base.Preconditions;
-
-import com.android.incallui.CallVideoClientNotifier.SurfaceChangeListener;
-import com.android.incallui.CallVideoClientNotifier.VideoEventListener;
+import com.android.incallui.InCallVideoCallListenerNotifier.SurfaceChangeListener;
+import com.android.incallui.InCallVideoCallListenerNotifier.VideoEventListener;
 import com.android.incallui.InCallPresenter.InCallDetailsListener;
 import com.android.incallui.InCallPresenter.InCallOrientationListener;
 import com.android.incallui.InCallPresenter.InCallStateListener;
@@ -166,8 +165,8 @@
         InCallPresenter.getInstance().addOrientationListener(this);
 
         // Register for surface and video events from {@link InCallVideoProvider}s.
-        CallVideoClientNotifier.getInstance().addSurfaceChangeListener(this);
-        CallVideoClientNotifier.getInstance().addVideoEventListener(this);
+        InCallVideoCallListenerNotifier.getInstance().addSurfaceChangeListener(this);
+        InCallVideoCallListenerNotifier.getInstance().addVideoEventListener(this);
 
         mInCallCameraManager = InCallPresenter.getInstance().getInCallCameraManager();
         mIsVideoCall = false;
@@ -185,8 +184,8 @@
         InCallPresenter.getInstance().removeListener(this);
         InCallPresenter.getInstance().removeIncomingCallListener(this);
         InCallPresenter.getInstance().removeOrientationListener(this);
-        CallVideoClientNotifier.getInstance().removeSurfaceChangeListener(this);
-        CallVideoClientNotifier.getInstance().removeVideoEventListener(this);
+        InCallVideoCallListenerNotifier.getInstance().removeSurfaceChangeListener(this);
+        InCallVideoCallListenerNotifier.getInstance().removeVideoEventListener(this);
 
         mInCallCameraManager = null;
     }