Video call upgrade/dowgrade request changes.

- fixed potential NPE in VideoCallFragment when setting preview size.
- moved photo load into the postExecute for the async task -- it is
already threaded and I was seeing intermittent concurrency issues.

- Changed CallButtonFragment to retrieve max # of buttons from config.xml.
  - Added override for wider screens (e.g. N6 and wider) to show an extra
    button.
- Reorganized call buttons so that the "Camera on/off" button is adjacent
to the flip camera button.
- Changed answer Glowpad to pass correct video state so that accepting a
video request uses the correct state (important for accepting requests to
turn camera back on).
- added new Session modification state REQUEST_REJECTED for when the remote
user explicitly declines the request.  This is used to trigger a
"video request rejected" message when the remote party rejects the
request.

Bug: 20257400
Change-Id: Ibe25eb045ee868748f91bf411f285629d36ebcd2
diff --git a/InCallUI/res/layout/call_button_fragment.xml b/InCallUI/res/layout/call_button_fragment.xml
index 73d14eb..62f796a 100644
--- a/InCallUI/res/layout/call_button_fragment.xml
+++ b/InCallUI/res/layout/call_button_fragment.xml
@@ -123,6 +123,13 @@
         <!-- This slot is either "Add" or "Merge", depending on the state of the call.  One or the
              other of these must always be set to GONE. -->
 
+        <!-- "Turn off camera" for video calls. -->
+        <ToggleButton android:id="@+id/pauseVideoButton"
+            style="@style/InCallCompoundButton"
+            android:background="@drawable/btn_compound_video_off"
+            android:contentDescription="@string/onscreenPauseVideoText"
+            android:visibility="gone" />
+
         <!-- "Add Call" -->
         <ImageButton android:id="@+id/addButton"
             style="@style/InCallButton"
@@ -139,13 +146,6 @@
             android:contentDescription="@string/onscreenMergeCallsText"
             android:visibility="gone" />
 
-        <!-- "Turn off camera" for video calls. -->
-        <ToggleButton android:id="@+id/pauseVideoButton"
-            style="@style/InCallCompoundButton"
-            android:background="@drawable/btn_compound_video_off"
-            android:contentDescription="@string/onscreenPauseVideoText"
-            android:visibility="gone" />
-
         <!-- "Overflow" -->
         <ImageButton android:id="@+id/overflowButton"
             style="@style/InCallButton"
diff --git a/InCallUI/res/layout/call_card_fragment.xml b/InCallUI/res/layout/call_card_fragment.xml
index 980003a..7b1ca44 100644
--- a/InCallUI/res/layout/call_card_fragment.xml
+++ b/InCallUI/res/layout/call_card_fragment.xml
@@ -70,6 +70,13 @@
         android:background="@android:color/white"
         android:src="@drawable/img_no_image_automirrored" />
 
+    <fragment android:name="com.android.incallui.VideoCallFragment"
+        android:id="@+id/videoCallFragment"
+        android:layout_alignParentTop="true"
+        android:layout_gravity="top|center_horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
     <!-- Progress spinner, useful for indicating pending operations such as upgrade to video. -->
     <FrameLayout
         android:id="@+id/progressSpinner"
@@ -90,13 +97,6 @@
             android:indeterminate="true" />
     </FrameLayout>
 
-    <fragment android:name="com.android.incallui.VideoCallFragment"
-        android:id="@+id/videoCallFragment"
-        android:layout_alignParentTop="true"
-        android:layout_gravity="top|center_horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
     <!-- Secondary "Call info" block, for the background ("on hold") call. -->
     <include layout="@layout/secondary_call_info" />
 
diff --git a/InCallUI/res/values-sw410dp/config.xml b/InCallUI/res/values-sw410dp/config.xml
new file mode 100644
index 0000000..a57f867
--- /dev/null
+++ b/InCallUI/res/values-sw410dp/config.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <!-- Determines the maximum number of buttons visible on the call card.  Any buttons over this
+         count are put into the overflow menu. -->
+    <integer name="call_card_max_buttons">6</integer>
+</resources>
\ No newline at end of file
diff --git a/InCallUI/res/values/array.xml b/InCallUI/res/values/array.xml
index 2e38c2c..7877ec8 100644
--- a/InCallUI/res/values/array.xml
+++ b/InCallUI/res/values/array.xml
@@ -112,61 +112,24 @@
         <item>@string/description_direction_down</item>
     </array>
 
-
-    <!-- For upgrade to video from VOLTE to VT (Tx/Rx/Bidirectional) in an active video call.
+    <!-- For accept/reject upgrade to video in active video call
          - Accept upgrade to video request (drag right)
-         - Decline upgrade to video request (drag left)
-         - Answer as audio call (drag down) -->
-    <array name="incoming_call_widget_video_upgrade_request_targets">
+         - Decline upgrade to video request (drag left)-->
+    <array name="incoming_call_widget_video_request_targets">
         <item>@drawable/ic_lockscreen_answer_video</item>
-        <item>@null</item>
-        <item>@drawable/ic_lockscreen_decline</item>
-        <item>@drawable/ic_lockscreen_answer</item>
+        <item>@drawable/ic_lockscreen_decline_video</item>
     </array>
-    <array name="incoming_call_widget_video_upgrade_request_target_descriptions">
+
+    <array name="incoming_call_widget_video_request_target_descriptions">
         <item>@string/description_target_accept_upgrade_to_video_request</item>
         <item>@null</item>
         <item>@string/description_target_decline_upgrade_to_video_request</item>
         <item>@null</item>"
     </array>
-    <array name="incoming_call_widget_video_upgrade_request_target_direction_descriptions">
+    <array name="incoming_call_widget_video_request_target_direction_descriptions">
         <item>@string/description_direction_right</item>
         <item>@null</item>
         <item>@string/description_direction_left</item>
         <item>@null</item>
     </array>
-
-    <!-- For accept/reject upgrade to video in active video call
-         - Accept upgrade to video request (drag right)
-         - Decline upgrade to video request (drag left)-->
-    <array name="incoming_call_widget_bidirectional_video_accept_reject_request_targets">
-        <item>@drawable/ic_lockscreen_answer_video</item>
-        <item>@drawable/ic_lockscreen_decline</item>
-    </array>
-
-    <!-- For accept/reject upgrade to video transmit in active video call
-         - Accept upgrade to video request (drag right)
-         - Decline upgrade to video request (drag left)
-         TODO: This should be automatically rejected in the lower layers -->
-    <array name="incoming_call_widget_video_transmit_accept_reject_request_targets">
-        <item>@drawable/ic_lockscreen_answer_video</item>
-        <item>@drawable/ic_lockscreen_decline</item>
-    </array>
-    <array name="incoming_call_widget_video_transmit_request_target_descriptions">
-        <item>@string/description_target_accept_upgrade_to_video_request</item>
-        <item>@string/description_target_decline_upgrade_to_video_request</item>
-    </array>
-
-    <!-- For accept/reject upgrade to video receive in active video call
-         - Accept upgrade to video request (drag right)
-         - Decline upgrade to video request (drag left)-->
-    <array name="incoming_call_widget_video_receive_accept_reject_request_targets">
-        <item>@drawable/ic_lockscreen_answer_video</item>
-        <item>@drawable/ic_lockscreen_decline</item>
-    </array>
-    <array name="incoming_call_widget_video_receive_request_target_descriptions">
-        <item>@string/description_target_accept_upgrade_to_video_request</item>
-        <item>@string/description_target_decline_upgrade_to_video_request</item>
-    </array>
-
 </resources>
diff --git a/InCallUI/res/values/config.xml b/InCallUI/res/values/config.xml
new file mode 100644
index 0000000..8acbd59
--- /dev/null
+++ b/InCallUI/res/values/config.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+    <!-- Determines the maximum number of buttons visible on the call card.  Any buttons over this
+         count are put into the overflow menu. -->
+    <integer name="call_card_max_buttons">5</integer>
+</resources>
\ No newline at end of file
diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml
index 628d0bf..0c3478d 100644
--- a/InCallUI/res/values/strings.xml
+++ b/InCallUI/res/values/strings.xml
@@ -114,8 +114,8 @@
     <string name="card_title_video_call_requesting">Requesting video</string>
     <!-- In-call screen: status label when there is a problem connecting a video call. -->
     <string name="card_title_video_call_error">Can\'t connect video call</string>
-    <!-- In-call screen: status label when in a paused video call. -->
-    <string name="card_title_video_call_paused">Video call (Paused)</string>
+    <!-- In-call screen: status label when the remote party rejects a video call request. -->
+    <string name="card_title_video_call_rejected">Video request rejected</string>
 
     <!-- In-call screen: string shown to the user when their outgoing number is different than the
          number reported by TelephonyManager#getLine1Number() -->
diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java
index a066396..b6ac7ba 100644
--- a/InCallUI/src/com/android/incallui/AnswerFragment.java
+++ b/InCallUI/src/com/android/incallui/AnswerFragment.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.telecom.VideoProfile;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.LayoutInflater;
@@ -49,10 +50,7 @@
     public static final int TARGET_SET_FOR_AUDIO_WITH_SMS = 1;
     public static final int TARGET_SET_FOR_VIDEO_WITHOUT_SMS = 2;
     public static final int TARGET_SET_FOR_VIDEO_WITH_SMS = 3;
-    public static final int TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST = 4;
-    public static final int TARGET_SET_FOR_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST = 5;
-    public static final int TARGET_SET_FOR_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST = 6;
-    public static final int TARGET_SET_FOR_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST = 7;
+    public static final int TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST = 4;
 
     /**
      * The popup showing the list of canned responses.
@@ -126,12 +124,21 @@
      * Sets targets on the glowpad according to target set identified by the parameter.
      * @param targetSet Integer identifying the set of targets to use.
      */
-    @Override
     public void showTargets(int targetSet) {
+        showTargets(targetSet, VideoProfile.VideoState.BIDIRECTIONAL);
+    }
+
+    /**
+     * Sets targets on the glowpad according to target set identified by the parameter.
+     * @param targetSet Integer identifying the set of targets to use.
+     */
+    @Override
+    public void showTargets(int targetSet, int videoState) {
         final int targetResourceId;
         final int targetDescriptionsResourceId;
         final int directionDescriptionsResourceId;
         final int handleDrawableResourceId;
+        mGlowpad.setVideoState(videoState);
 
         switch (targetSet) {
             case TARGET_SET_FOR_AUDIO_WITH_SMS:
@@ -158,39 +165,13 @@
                         R.array.incoming_call_widget_video_with_sms_direction_descriptions;
                 handleDrawableResourceId = R.drawable.ic_incall_video_handle;
                 break;
-            case TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST:
-                targetResourceId = R.array.incoming_call_widget_video_upgrade_request_targets;
-                targetDescriptionsResourceId =
-                        R.array.incoming_call_widget_video_upgrade_request_target_descriptions;
-                directionDescriptionsResourceId = R.array
-                        .incoming_call_widget_video_upgrade_request_target_direction_descriptions;
-                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
-                break;
-            case TARGET_SET_FOR_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST:
+            case TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST:
                 targetResourceId =
-                    R.array.incoming_call_widget_bidirectional_video_accept_reject_request_targets;
+                    R.array.incoming_call_widget_video_request_targets;
                 targetDescriptionsResourceId =
-                        R.array.incoming_call_widget_video_upgrade_request_target_descriptions;
+                        R.array.incoming_call_widget_video_request_target_descriptions;
                 directionDescriptionsResourceId = R.array
-                        .incoming_call_widget_video_upgrade_request_target_direction_descriptions;
-                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
-                break;
-            case TARGET_SET_FOR_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST:
-                targetResourceId =
-                    R.array.incoming_call_widget_video_transmit_accept_reject_request_targets;
-                targetDescriptionsResourceId =
-                        R.array.incoming_call_widget_video_transmit_request_target_descriptions;
-                directionDescriptionsResourceId = R.array
-                        .incoming_call_widget_video_upgrade_request_target_direction_descriptions;
-                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
-                break;
-            case TARGET_SET_FOR_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST:
-                targetResourceId =
-                    R.array.incoming_call_widget_video_receive_accept_reject_request_targets;
-                targetDescriptionsResourceId =
-                        R.array.incoming_call_widget_video_receive_request_target_descriptions;
-                directionDescriptionsResourceId = R.array
-                        .incoming_call_widget_video_upgrade_request_target_direction_descriptions;
+                        .incoming_call_widget_video_request_target_direction_descriptions;
                 handleDrawableResourceId = R.drawable.ic_incall_video_handle;
                 break;
             case TARGET_SET_FOR_AUDIO_WITHOUT_SMS:
@@ -378,6 +359,11 @@
     }
 
     @Override
+    public void onDeclineUpgradeRequest(Context context) {
+        InCallPresenter.getInstance().declineUpgradeRequest(context);
+    }
+
+    @Override
     public void onText() {
         getPresenter().onText();
     }
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 597975a..e898adf 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -17,7 +17,6 @@
 package com.android.incallui;
 
 import android.content.Context;
-import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 
 import java.util.List;
@@ -101,6 +100,18 @@
         }
     }
 
+    @Override
+    public void onSessionModificationStateChange(int sessionModificationState) {
+        boolean isUpgradePending = sessionModificationState ==
+                Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
+
+        if (!isUpgradePending) {
+            // Stop listening for updates.
+            CallList.getInstance().removeCallUpdateListener(mCallId, this);
+            getUi().showAnswerUi(false);
+        }
+    }
+
     private boolean isVideoUpgradePending(Call call) {
         return call.getSessionModificationState()
                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
@@ -162,28 +173,11 @@
             return;
         }
         ui.showAnswerUi(true);
-        ui.showTargets(getUiTarget(currentVideoState, modifyToVideoState));
+        ui.showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST,
+                modifyToVideoState);
 
     }
 
-    private int getUiTarget(int currentVideoState, int modifyToVideoState) {
-        if (showVideoUpgradeOptions(currentVideoState, modifyToVideoState)) {
-            return AnswerFragment.TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST;
-        } else if (isEnabled(modifyToVideoState, VideoProfile.VideoState.BIDIRECTIONAL)) {
-            return AnswerFragment.TARGET_SET_FOR_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST;
-        }  else if (isEnabled(modifyToVideoState, VideoProfile.VideoState.TX_ENABLED)) {
-            return AnswerFragment.TARGET_SET_FOR_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST;
-        }  else if (isEnabled(modifyToVideoState, VideoProfile.VideoState.RX_ENABLED)) {
-            return AnswerFragment.TARGET_SET_FOR_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST;
-        }
-        return AnswerFragment.TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST;
-    }
-
-    private boolean showVideoUpgradeOptions(int currentVideoState, int modifyToVideoState) {
-        return currentVideoState == VideoProfile.VideoState.AUDIO_ONLY &&
-            isEnabled(modifyToVideoState, VideoProfile.VideoState.BIDIRECTIONAL);
-    }
-
     private boolean isEnabled(int videoState, int mask) {
         return (videoState & mask) == mask;
     }
@@ -215,15 +209,16 @@
     }
 
     public void onAnswer(int videoState, Context context) {
-        Log.d(this, "onAnswer mCallId=" + mCallId + " videoState=" + videoState);
         if (mCallId == null) {
             return;
         }
 
         if (mCall.getSessionModificationState()
                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+            Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState);
             InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context);
         } else {
+            Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);
             TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState);
         }
     }
@@ -288,6 +283,7 @@
     interface AnswerUi extends Ui {
         public void showAnswerUi(boolean show);
         public void showTargets(int targetSet);
+        public void showTargets(int targetSet, int videoState);
         public void showMessageDialog();
         public void configureMessageDialog(List<String> textResponses);
         public Context getContext();
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index 3b97b0b..8ca524e 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -121,6 +121,7 @@
         public static final int REQUEST_FAILED = 2;
         public static final int RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3;
         public static final int UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT = 4;
+        public static final int REQUEST_REJECTED = 5;
     }
 
     public static class VideoSettings {
@@ -485,7 +486,7 @@
         Log.d(this, "setSessionModificationState " + state + " mSessionModificationState="
                 + mSessionModificationState);
         if (hasChanged) {
-            update();
+            CallList.getInstance().onSessionModificationStateChange(this, state);
         }
     }
 
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index 6da66ba..9094037 100644
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java
@@ -18,9 +18,7 @@
 
 import static com.android.incallui.CallButtonFragment.Buttons.*;
 
-import android.app.AlertDialog;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
@@ -30,8 +28,6 @@
 import android.graphics.drawable.StateListDrawable;
 import android.os.Bundle;
 import android.telecom.AudioState;
-import android.telecom.TelecomManager;
-import android.telecom.VideoProfile;
 import android.util.SparseIntArray;
 import android.view.ContextThemeWrapper;
 import android.view.HapticFeedbackConstants;
@@ -43,12 +39,10 @@
 import android.widget.CompoundButton;
 import android.widget.ImageButton;
 import android.widget.PopupMenu;
-import android.widget.Toast;
 import android.widget.PopupMenu.OnDismissListener;
 import android.widget.PopupMenu.OnMenuItemClickListener;
 
 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
-import java.util.ArrayList;
 
 /**
  * Fragment for call control buttons
@@ -58,7 +52,7 @@
         implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, OnDismissListener,
         View.OnClickListener {
     private static final int INVALID_INDEX = -1;
-    private static final int BUTTON_MAX_VISIBLE = 5;
+    private int mButtonMaxVisible;
     // The button is currently visible in the UI
     private static final int BUTTON_VISIBLE = 1;
     // The button is hidden in the UI
@@ -127,6 +121,8 @@
         for (int i = 0; i < BUTTON_COUNT; i++) {
             mButtonVisibilityMap.put(i, BUTTON_HIDDEN);
         }
+
+        mButtonMaxVisible = getResources().getInteger(R.integer.call_card_max_buttons);
     }
 
     @Override
@@ -462,7 +458,7 @@
             final View button = getButtonById(i);
             if (visibility == BUTTON_VISIBLE) {
                 visibleCount++;
-                if (visibleCount <= BUTTON_MAX_VISIBLE) {
+                if (visibleCount <= mButtonMaxVisible) {
                     button.setVisibility(View.VISIBLE);
                     prevVisibleButton = button;
                     prevVisibleId = i;
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 43ee332..d788a10 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -322,6 +322,7 @@
             VideoProfile videoProfile = new VideoProfile(
                     mCall.getVideoState() | VideoProfile.VideoState.TX_ENABLED);
             videoCall.sendSessionModifyRequest(videoProfile);
+            mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
         }
         getUi().setVideoPaused(pause);
     }
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 783536e..16385cf 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -29,6 +29,8 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.telecom.DisconnectCause;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
@@ -63,6 +65,38 @@
 public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPresenter.CallCardUi>
         implements CallCardPresenter.CallCardUi {
 
+    /**
+     * Internal class which represents the call state label which is to be applied.
+     */
+    private class CallStateLabel {
+        private CharSequence mCallStateLabel;
+        private boolean mIsAutoDismissing;
+
+        public CallStateLabel(CharSequence callStateLabel, boolean isAutoDismissing) {
+            mCallStateLabel = callStateLabel;
+            mIsAutoDismissing = isAutoDismissing;
+        }
+
+        public CharSequence getCallStateLabel() {
+            return mCallStateLabel;
+        }
+
+        /**
+         * Determines if the call state label should auto-dismiss.
+         *
+         * @return {@code true} if the call state label should auto-dismiss.
+         */
+        public boolean isAutoDismissing() {
+            return mIsAutoDismissing;
+        }
+    };
+
+    /**
+     * The duration of time (in milliseconds) a call state label should remain visible before
+     * resetting to its previous value.
+     */
+    private static final long CALL_STATE_LABEL_RESET_DELAY_MS = 3000;
+
     private AnimatorSet mAnimatorSet;
     private int mRevealAnimationDuration;
     private int mShrinkAnimationDuration;
@@ -121,6 +155,13 @@
 
     private MaterialPalette mCurrentThemeColors;
 
+    /**
+     * Call state label to set when an auto-dismissing call state label is dismissed.
+     */
+    private CharSequence mPostResetCallStateLabel;
+    private boolean mCallStateLabelResetPending = false;
+    private Handler mHandler;
+
     @Override
     CallCardPresenter.CallCardUi getUi() {
         return this;
@@ -135,6 +176,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        mHandler = new Handler(Looper.getMainLooper());
         mRevealAnimationDuration = getResources().getInteger(R.integer.reveal_animation_duration);
         mShrinkAnimationDuration = getResources().getInteger(R.integer.shrink_animation_duration);
         mVideoAnimationDuration = getResources().getInteger(R.integer.video_animation_duration);
@@ -485,15 +527,16 @@
             boolean isWifi,
             boolean isConference) {
         boolean isGatewayCall = !TextUtils.isEmpty(gatewayNumber);
-        CharSequence callStateLabel = getCallStateLabelFromState(state, videoState,
+        CallStateLabel callStateLabel = getCallStateLabelFromState(state, videoState,
                 sessionModificationState, disconnectCause, connectionLabel, isGatewayCall, isWifi,
                 isConference);
 
-        Log.v(this, "setCallState " + callStateLabel);
+        Log.v(this, "setCallState " + callStateLabel.getCallStateLabel());
+        Log.v(this, "AutoDismiss " + callStateLabel.isAutoDismissing());
         Log.v(this, "DisconnectCause " + disconnectCause.toString());
         Log.v(this, "gateway " + connectionLabel + gatewayNumber);
 
-        if (TextUtils.equals(callStateLabel, mCallStateLabel.getText())) {
+        if (TextUtils.equals(callStateLabel.getCallStateLabel(), mCallStateLabel.getText())) {
             // Nothing to do if the labels are the same
             if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
                 mCallStateLabel.clearAnimation();
@@ -503,24 +546,13 @@
         }
 
         // Update the call state label and icon.
-        if (!TextUtils.isEmpty(callStateLabel)) {
-            mCallStateLabel.setText(callStateLabel);
-            mCallStateLabel.setAlpha(1);
-            mCallStateLabel.setVisibility(View.VISIBLE);
-
+        setCallStateLabel(callStateLabel);
+        if (!TextUtils.isEmpty(callStateLabel.getCallStateLabel())) {
             if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
                 mCallStateLabel.clearAnimation();
             } else {
                 mCallStateLabel.startAnimation(mPulseAnimation);
             }
-        } else {
-            Animation callStateLabelAnimation = mCallStateLabel.getAnimation();
-            if (callStateLabelAnimation != null) {
-                callStateLabelAnimation.cancel();
-            }
-            mCallStateLabel.setText(null);
-            mCallStateLabel.setAlpha(0);
-            mCallStateLabel.setVisibility(View.GONE);
         }
 
         if (callStateIcon != null) {
@@ -531,7 +563,7 @@
             mCallStateIcon.setImageDrawable(callStateIcon);
 
             if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED
-                    || TextUtils.isEmpty(callStateLabel)) {
+                    || TextUtils.isEmpty(callStateLabel.getCallStateLabel())) {
                 mCallStateIcon.clearAnimation();
             } else {
                 mCallStateIcon.startAnimation(mPulseAnimation);
@@ -562,7 +594,7 @@
 
         if (state == Call.State.INCOMING) {
             if (callStateLabel != null) {
-                getView().announceForAccessibility(callStateLabel);
+                getView().announceForAccessibility(callStateLabel.getCallStateLabel());
             }
             if (mPrimaryName.getText() != null) {
                 getView().announceForAccessibility(mPrimaryName.getText());
@@ -570,6 +602,50 @@
         }
     }
 
+    private void setCallStateLabel(CallStateLabel callStateLabel) {
+        Log.v(this, "setCallStateLabel : label = " + callStateLabel.getCallStateLabel());
+
+        if (callStateLabel.isAutoDismissing()) {
+            mCallStateLabelResetPending = true;
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    Log.v(this, "restoringCallStateLabel : label = " +
+                            mPostResetCallStateLabel);
+                    changeCallStateLabel(mPostResetCallStateLabel);
+                    mCallStateLabelResetPending = false;
+                }
+            }, CALL_STATE_LABEL_RESET_DELAY_MS);
+
+            changeCallStateLabel(callStateLabel.getCallStateLabel());
+        } else {
+            // Keep track of the current call state label; used when resetting auto dismissing
+            // call state labels.
+            mPostResetCallStateLabel = callStateLabel.getCallStateLabel();
+
+            if (!mCallStateLabelResetPending) {
+                changeCallStateLabel(callStateLabel.getCallStateLabel());
+            }
+        }
+    }
+
+    private void changeCallStateLabel(CharSequence callStateLabel) {
+        Log.v(this, "changeCallStateLabel : label = " + callStateLabel);
+        if (!TextUtils.isEmpty(callStateLabel)) {
+            mCallStateLabel.setText(callStateLabel);
+            mCallStateLabel.setAlpha(1);
+            mCallStateLabel.setVisibility(View.VISIBLE);
+        } else {
+            Animation callStateLabelAnimation = mCallStateLabel.getAnimation();
+            if (callStateLabelAnimation != null) {
+                callStateLabelAnimation.cancel();
+            }
+            mCallStateLabel.setText(null);
+            mCallStateLabel.setAlpha(0);
+            mCallStateLabel.setVisibility(View.GONE);
+        }
+    }
+
     @Override
     public void setCallbackNumber(String callbackNumber, boolean isEmergencyCall) {
         if (mInCallMessageLabel == null) {
@@ -659,7 +735,7 @@
      *
      * TODO: Move this to the CallCardPresenter.
      */
-    private CharSequence getCallStateLabelFromState(int state, int videoState,
+    private CallStateLabel getCallStateLabelFromState(int state, int videoState,
             int sessionModificationState, DisconnectCause disconnectCause, String label,
             boolean isGatewayCall, boolean isWifi, boolean isConference) {
         final Context context = getView().getContext();
@@ -667,6 +743,7 @@
 
         boolean hasSuggestedLabel = label != null;
         boolean isAccount = hasSuggestedLabel && !isGatewayCall;
+        boolean isAutoDismissing = false;
 
         switch  (state) {
             case Call.State.IDLE:
@@ -678,15 +755,20 @@
                 if ((isAccount || isWifi || isConference) && hasSuggestedLabel) {
                     callStateLabel = label;
                 } else if (sessionModificationState
+                        == Call.SessionModificationState.REQUEST_REJECTED) {
+                    callStateLabel = context.getString(R.string.card_title_video_call_rejected);
+                    isAutoDismissing = true;
+                } else if (sessionModificationState
                         == Call.SessionModificationState.REQUEST_FAILED) {
                     callStateLabel = context.getString(R.string.card_title_video_call_error);
+                    isAutoDismissing = true;
                 } else if (sessionModificationState
                         == Call.SessionModificationState.WAITING_FOR_RESPONSE) {
                     callStateLabel = context.getString(R.string.card_title_video_call_requesting);
-                } else if (CallUtils.isVideoCall(videoState) &&
-                        VideoProfile.VideoState.isPaused(videoState)) {
-                    callStateLabel = context.getString(R.string.card_title_video_call_paused);
-                } else if (VideoProfile.VideoState.isBidirectional(videoState)) {
+                } else if (sessionModificationState
+                        == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
+                    callStateLabel = context.getString(R.string.card_title_video_call_requesting);
+                } else if (CallUtils.isVideoCall(videoState)) {
                     callStateLabel = context.getString(R.string.card_title_video_call);
                 }
                 break;
@@ -710,7 +792,8 @@
                     callStateLabel = label;
                 } else if (isAccount) {
                     callStateLabel = context.getString(R.string.incoming_via_template, label);
-                } else if (VideoProfile.VideoState.isBidirectional(videoState)) {
+                } else if (VideoProfile.VideoState.isTransmissionEnabled(videoState) ||
+                        VideoProfile.VideoState.isReceptionEnabled(videoState)) {
                     callStateLabel = context.getString(R.string.notification_incoming_video_call);
                 } else {
                     callStateLabel = context.getString(R.string.card_title_incoming_call);
@@ -737,7 +820,7 @@
             default:
                 Log.wtf(this, "updateCallStateWidgets: unexpected call: " + state);
         }
-        return callStateLabel;
+        return new CallStateLabel(callStateLabel, isAutoDismissing);
     }
 
     private void showAndInitializeSecondaryCallInfo(boolean hasProvider) {
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 89cb659..c5407df 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -45,6 +45,7 @@
 import com.android.incalluibind.ObjectFactory;
 
 import java.lang.ref.WeakReference;
+import java.util.Objects;
 
 import com.google.common.base.Preconditions;
 
@@ -55,7 +56,7 @@
  */
 public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
         implements InCallStateListener, IncomingCallListener, InCallDetailsListener,
-        InCallEventListener {
+        InCallEventListener, CallList.CallUpdateListener {
 
     private static final String TAG = CallCardPresenter.class.getSimpleName();
     private static final long CALL_TIME_UPDATE_INTERVAL_MS = 1000;
@@ -65,8 +66,8 @@
     private ContactCacheEntry mPrimaryContactInfo;
     private ContactCacheEntry mSecondaryContactInfo;
     private CallTimer mCallTimer;
-
     private Context mContext;
+    private boolean mSpinnerShowing = false;
 
     public static class ContactLookupCallback implements ContactInfoCacheCallback {
         private final WeakReference<CallCardPresenter> mCallCardPresenter;
@@ -111,6 +112,7 @@
         // Call may be null if disconnect happened already.
         if (call != null) {
             mPrimary = call;
+            CallList.getInstance().addCallUpdateListener(call.getId(), this);
 
             // start processing lookups right away.
             if (!call.isConferenceCall()) {
@@ -146,6 +148,9 @@
         InCallPresenter.getInstance().removeIncomingCallListener(this);
         InCallPresenter.getInstance().removeDetailsListener(this);
         InCallPresenter.getInstance().removeInCallEventListener(this);
+        if (mPrimary != null) {
+            CallList.getInstance().removeCallUpdateListener(mPrimary.getId(), this);
+        }
 
         mPrimary = null;
         mPrimaryContactInfo = null;
@@ -192,6 +197,7 @@
         final boolean secondaryChanged = !Call.areSame(mSecondary, secondary);
 
         mSecondary = secondary;
+        Call previousPrimary = mPrimary;
         mPrimary = primary;
 
         // Refresh primary call information if either:
@@ -200,6 +206,11 @@
         if (mPrimary != null && (primaryChanged ||
                 ui.isManageConferenceVisible() != shouldShowManageConference())) {
             // primary call has changed
+            if (previousPrimary != null) {
+                CallList.getInstance().removeCallUpdateListener(previousPrimary.getId(), this);
+            }
+            CallList.getInstance().addCallUpdateListener(mPrimary.getId(), this);
+
             mPrimaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext, mPrimary,
                     mPrimary.getState() == Call.State.INCOMING);
             updatePrimaryDisplayInfo();
@@ -250,7 +261,6 @@
         }
 
         maybeShowManageConferenceCallButton();
-        maybeShowProgressSpinner();
 
         // Hide the end call button instantly if we're receiving an incoming call.
         getUi().setEndCallButtonEnabled(shouldShowEndCallButton(mPrimary, callState),
@@ -267,6 +277,32 @@
         }
     }
 
+    @Override
+    public void onCallChanged(Call call) {
+        // No-op; specific call updates handled elsewhere.
+    }
+
+    /**
+     * Handles a change to the session modification state for a call.  Triggers showing the progress
+     * spinner, as well as updating the call state label.
+     *
+     * @param sessionModificationState The new session modification state.
+     */
+    @Override
+    public void onSessionModificationStateChange(int sessionModificationState) {
+        Log.d(this, "onSessionModificationStateChange : sessionModificationState = " +
+                sessionModificationState);
+
+        if (mPrimary == null) {
+            return;
+        }
+        maybeShowProgressSpinner(mPrimary.getState(), sessionModificationState);
+        getUi().setEndCallButtonEnabled(sessionModificationState !=
+                        Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST,
+                true /* shouldAnimate */);
+        updatePrimaryCallState();
+    }
+
     private String getSubscriptionNumber() {
         // If it's an emergency call, and they're not populating the callback number,
         // then try to fall back to the phone sub info (to hopefully get the SIM's
@@ -310,11 +346,21 @@
         getUi().showManageConferenceCallButton(shouldShowManageConference());
     }
 
-    private void maybeShowProgressSpinner() {
-        final boolean show = mPrimary != null && mPrimary.getSessionModificationState()
-                == Call.SessionModificationState.WAITING_FOR_RESPONSE
-                && mPrimary.getState() == Call.State.ACTIVE;
-        getUi().setProgressSpinnerVisible(show);
+    /**
+     * Determines if a pending session modification exists for the current call.  If so, the
+     * progress spinner is shown, and the call state is updated.
+     *
+     * @param callState The call state.
+     * @param sessionModificationState The session modification state.
+     */
+    private void maybeShowProgressSpinner(int callState, int sessionModificationState) {
+        final boolean show = sessionModificationState ==
+                Call.SessionModificationState.WAITING_FOR_RESPONSE
+                && callState == Call.State.ACTIVE;
+        if (show != mSpinnerShowing) {
+            getUi().setProgressSpinnerVisible(show);
+            mSpinnerShowing = show;
+        }
     }
 
     /**
@@ -640,7 +686,7 @@
     }
 
     private boolean hasOutgoingGatewayCall() {
-        // We only display the gateway information while STATE_DIALING so return false for any othe
+        // We only display the gateway information while STATE_DIALING so return false for any other
         // call state.
         // TODO: mPrimary can be null because this is called from updatePrimaryDisplayInfo which
         // is also called after a contact search completes (call is not present yet).  Split the
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index df2a72e..02c8fbf 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -138,6 +138,21 @@
         notifyGenericListeners();
     }
 
+    /**
+     * Called when a single call has changed session modification state.
+     *
+     * @param call The call.
+     * @param sessionModificationState The new session modification state.
+     */
+    public void onSessionModificationStateChange(Call call, int sessionModificationState) {
+        final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
+        if (listeners != null) {
+            for (CallUpdateListener listener : listeners) {
+                listener.onSessionModificationStateChange(sessionModificationState);
+            }
+        }
+    }
+
     public void notifyCallUpdateListeners(Call call) {
         final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
         if (listeners != null) {
@@ -553,10 +568,19 @@
          * that will get called upon disconnection.
          */
         public void onDisconnect(Call call);
+
+
     }
 
     public interface CallUpdateListener {
         // TODO: refactor and limit arg to be call state.  Caller info is not needed.
         public void onCallChanged(Call call);
+
+        /**
+         * Notifies of a change to the session modification state for a call.
+         *
+         * @param sessionModificationState The new session modification state.
+         */
+        public void onSessionModificationStateChange(int sessionModificationState);
     }
 }
diff --git a/InCallUI/src/com/android/incallui/GlowPadWrapper.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
index 58a5f30..1776696 100644
--- a/InCallUI/src/com/android/incallui/GlowPadWrapper.java
+++ b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
@@ -49,6 +49,7 @@
     private AnswerListener mAnswerListener;
     private boolean mPingEnabled = true;
     private boolean mTargetTriggered = false;
+    private int mVideoState = VideoProfile.VideoState.BIDIRECTIONAL;
 
     public GlowPadWrapper(Context context) {
         super(context);
@@ -125,11 +126,11 @@
                 break;
             case R.drawable.ic_videocam:
             case R.drawable.ic_lockscreen_answer_video:
-                mAnswerListener.onAnswer(VideoProfile.VideoState.BIDIRECTIONAL, getContext());
+                mAnswerListener.onAnswer(mVideoState, getContext());
                 mTargetTriggered = true;
                 break;
-            case R.drawable.ic_toolbar_video_off:
-                InCallPresenter.getInstance().declineUpgradeRequest(getContext());
+            case R.drawable.ic_lockscreen_decline_video:
+                mAnswerListener.onDeclineUpgradeRequest(getContext());
                 mTargetTriggered = true;
                 break;
             default:
@@ -152,9 +153,19 @@
         mAnswerListener = listener;
     }
 
+    /**
+     * Sets the video state represented by the "video" icon on the glow pad.
+     *
+     * @param videoState The new video state.
+     */
+    public void setVideoState(int videoState) {
+        mVideoState = videoState;
+    }
+
     public interface AnswerListener {
         void onAnswer(int videoState, Context context);
         void onDecline(Context context);
+        void onDeclineUpgradeRequest(Context context);
         void onText();
     }
 }
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
index ba4ab66..ede1129 100644
--- a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
+++ b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
@@ -80,6 +80,19 @@
         Log.d(this, "onSessionModifyResponseReceived status=" + status + " requestedProfile="
                 + requestedProfile + " responseProfile=" + responseProfile);
         if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) {
+            // Report the reason the upgrade failed as the new session modification state.
+            if (status == VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT) {
+                mCall.setSessionModificationState(
+                        Call.SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT);
+            } else {
+                if (status == VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE) {
+                    mCall.setSessionModificationState(
+                            Call.SessionModificationState.REQUEST_REJECTED);
+                } else {
+                    mCall.setSessionModificationState(
+                            Call.SessionModificationState.REQUEST_FAILED);
+                }
+            }
             InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoFail(status, mCall);
         } else if (requestedProfile != null && responseProfile != null) {
             boolean modifySucceeded = requestedProfile.getVideoState() ==
@@ -95,6 +108,8 @@
         } else {
             Log.d(this, "onSessionModifyResponseReceived request and response Profiles are null");
         }
+        // Finally clear the outstanding request.
+        mCall.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
     }
 
     /**
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 2037caf..caca6d4 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -45,7 +45,9 @@
 /**
  * This class adds Notifications to the status bar for the in-call experience.
  */
-public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
+public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
+        CallList.CallUpdateListener {
+
     // notification types
     private static final int IN_CALL_NOTIFICATION = 1;
 
@@ -58,6 +60,8 @@
     private int mSavedContent = 0;
     private Bitmap mSavedLargeIcon;
     private String mSavedContentTitle;
+    private String mCallId = null;
+    private InCallState mInCallState;
 
     public StatusBarNotifier(Context context, ContactInfoCache contactInfoCache) {
         Preconditions.checkNotNull(context);
@@ -74,7 +78,7 @@
     @Override
     public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
         Log.d(this, "onStateChange");
-
+        mInCallState = newState;
         updateNotification(newState, callList);
     }
 
@@ -146,6 +150,12 @@
         final boolean isIncoming = (call.getState() == Call.State.INCOMING ||
                 call.getState() == Call.State.CALL_WAITING);
 
+        if (mCallId != null) {
+            CallList.getInstance().removeCallUpdateListener(mCallId, this);
+        }
+        mCallId = call.getId();
+        CallList.getInstance().addCallUpdateListener(call.getId(), this);
+
         // we make a call to the contact info cache to query for supplemental data to what the
         // call provides.  This includes the contact name and photo.
         // This callback will always get called immediately and synchronously with whatever data
@@ -575,4 +585,25 @@
         return PendingIntent.getBroadcast(context, 0, intent, 0);
     }
 
+    @Override
+    public void onCallChanged(Call call) {
+        // no-op
+    }
+
+    /**
+     * Responds to changes in the session modification state for the call by dismissing the
+     * status bar notification as required.
+     *
+     * @param sessionModificationState The new session modification state.
+     */
+    @Override
+    public void onSessionModificationStateChange(int sessionModificationState) {
+        if (sessionModificationState == Call.SessionModificationState.NO_REQUEST) {
+            if (mCallId != null) {
+                CallList.getInstance().removeCallUpdateListener(mCallId, this);
+            }
+
+            updateNotification(mInCallState, CallList.getInstance());
+        }
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java
index d43e163..8086559 100644
--- a/InCallUI/src/com/android/incallui/VideoCallFragment.java
+++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java
@@ -663,10 +663,12 @@
             params.height = height;
             preview.setLayoutParams(params);
 
-            ViewGroup.LayoutParams containerParams = mPreviewVideoContainer.getLayoutParams();
-            containerParams.width = width;
-            containerParams.height = height;
-            mPreviewVideoContainer.setLayoutParams(containerParams);
+            if (mPreviewVideoContainer != null) {
+                ViewGroup.LayoutParams containerParams = mPreviewVideoContainer.getLayoutParams();
+                containerParams.width = width;
+                containerParams.height = height;
+                mPreviewVideoContainer.setLayoutParams(containerParams);
+            }
 
             // The width and height are interchanged outside of this method based on the current
             // orientation, so we can transform using "width", which will be either the width or
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index 881fad9..ea64792 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -22,7 +22,6 @@
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.Handler;
 import android.provider.ContactsContract;
 import android.telecom.AudioState;
 import android.telecom.CameraCapabilities;
@@ -31,6 +30,7 @@
 import android.telecom.InCallService.VideoCall;
 import android.telecom.VideoProfile;
 import android.view.Surface;
+import android.view.View;
 import android.widget.ImageView;
 
 import com.android.contacts.common.CallUtil;
@@ -161,10 +161,6 @@
 
     private static boolean mIsVideoMode = false;
 
-    /** Handler which resets request state to NO_REQUEST after an interval. */
-    private Handler mSessionModificationResetHandler;
-    private static final long SESSION_MODIFICATION_RESET_DELAY_MS = 3000;
-
     /**
      * Contact photo manager to retrieve cached contact photo information.
      */
@@ -184,7 +180,6 @@
         mContext = context;
         mMinimumVideoDimension = mContext.getResources().getDimension(
                 R.dimen.video_preview_small_dimension);
-        mSessionModificationResetHandler = new Handler();
     }
 
     /**
@@ -931,8 +926,6 @@
         if (call == null) {
             return;
         }
-
-        call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
     }
 
     @Override
@@ -945,25 +938,6 @@
         if (call == null) {
             return;
         }
-
-        if (status == VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT) {
-            call.setSessionModificationState(
-                    Call.SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT);
-        } else {
-            call.setSessionModificationState(Call.SessionModificationState.REQUEST_FAILED);
-
-            final Call modifyCall = call;
-            // Start handler to change state from REQUEST_FAILED to NO_REQUEST after an interval.
-            mSessionModificationResetHandler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    if (modifyCall != null) {
-                        modifyCall
-                            .setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
-                    }
-                }
-            }, SESSION_MODIFICATION_RESET_DELAY_MS);
-        }
     }
 
     @Override
@@ -1160,7 +1134,11 @@
                         }
                     }
                 }
+                return null;
+            }
 
+            @Override
+            protected void onPostExecute(Void result) {
                 // If user profile information was found, issue an async request to load the user's
                 // profile photo.
                 if (mProfileInfo != null) {
@@ -1172,17 +1150,14 @@
                             new ContactPhotoManager.DefaultImageRequest(mProfileInfo.name,
                                     mProfileInfo.lookupKey, false /* isCircularPhoto */);
 
-                    mContactPhotoManager
-                            .loadDirectoryPhoto(ui.getPreviewPhotoView(),
+                    ImageView photoView = ui.getPreviewPhotoView();
+                    if (photoView == null) {
+                        return;
+                    }
+                    mContactPhotoManager.loadDirectoryPhoto(photoView,
                                     mProfileInfo.displayPhotoUri,
                                     false /* darkTheme */, false /* isCircular */, imageRequest);
                 }
-                return null;
-            }
-
-            @Override
-            protected void onPostExecute(Void result) {
-                // No-op
             }
         };