Misc fixes for window moving and resizing

- Do not skip resizeTask if we're starting or ending drag. We need
  the relayout because surface mode is changing.

- When we're changing the surface mode, need to wait for the first
  draw to finish before we can modify shown frame. Otherwise there
  could be 1 old frame displayed with new position, which makes the
  window position look a bit off.

- Clean up dragResizing/dragResizeChanged flags.

Change-Id: Ia396d6b88fd616ad57aa8cd24ca7e1161add7205
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d3cea8d..0a3391c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3061,7 +3061,12 @@
             return;
         }
 
-        if (task.mBounds != null && task.mBounds.equals(bounds)) {
+        // TODO: change resizedByUser to an enum (or bitmask?) to indicate the origin of
+        //       this resize (eg. systemResize, userResize, forcedResized).
+        // If the resize is a drag-resize by user, let it go through even if the bounds
+        // is not changing, as we might need a relayout due to surface size change
+        // (to/from fullscreen).
+        if (task.mBounds != null && task.mBounds.equals(bounds) && !resizedByUser) {
             // Nothing to do here...
             return;
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc9efdb..58f0448 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -72,6 +72,12 @@
     // For handling display rotations.
     private Rect mTmpRect2 = new Rect();
 
+    // Whether the task is currently being drag-resized
+    private boolean mDragResizing;
+
+    // Whether the task is starting or ending to be drag-resized
+    private boolean mDragResizeChanging;
+
     // The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer.
     WindowStateAnimator mDimWinAnimator;
     // Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
@@ -227,10 +233,34 @@
         return boundsChange;
     }
 
+    boolean resizeLocked(Rect bounds, Configuration configuration) {
+        int boundsChanged = setBounds(bounds, configuration);
+        if (mDragResizeChanging) {
+            boundsChanged |= BOUNDS_CHANGE_SIZE;
+            mDragResizeChanging = false;
+        }
+        if (boundsChanged == BOUNDS_CHANGE_NONE) {
+            return false;
+        }
+        if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
+            resizeWindows();
+        }
+        return true;
+    }
+
     void getBounds(Rect out) {
         out.set(mBounds);
     }
 
+    void setDragResizing(boolean dragResizing) {
+        mDragResizeChanging = mDragResizing != dragResizing;
+        mDragResizing = dragResizing;
+    }
+
+    boolean isDragResizing() {
+        return mDragResizing;
+    }
+
     void updateDisplayInfo(final DisplayContent displayContent) {
         if (displayContent == null) {
             return;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 17b56ba..5487349 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -86,8 +86,7 @@
     private int mMinVisibleWidth;
     private int mMinVisibleHeight;
 
-    private int mTaskId;
-    private TaskStack mStack;
+    private Task mTask;
     private boolean mResizing;
     private final Rect mWindowOriginalBounds = new Rect();
     private final Rect mWindowDragBounds = new Rect();
@@ -136,7 +135,7 @@
                         }
                         try {
                             mService.mActivityManager.resizeTask(
-                                    mTaskId, mWindowDragBounds, true /* resizedByUser */);
+                                    mTask.mTaskId, mWindowDragBounds, true /* resizedByUser */);
                         } catch(RemoteException e) {}
                     } break;
 
@@ -156,21 +155,29 @@
                 }
 
                 if (endDrag) {
-                    mResizing = false;
+                    synchronized (mService.mWindowMap) {
+                        endDragLocked();
+                    }
                     try {
-                        mService.mActivityManager.resizeTask(
-                                mTaskId, mWindowDragBounds, true /* resizedByUser */);
+                        if (mResizing) {
+                            // We were using fullscreen surface during resizing. Request
+                            // resizeTask() one last time to restore surface to window size.
+                            mService.mActivityManager.resizeTask(
+                                    mTask.mTaskId, mWindowDragBounds, true /* resizedByUser */);
+                        }
+
+                        if (mCurrentDimSide != CTRL_NONE) {
+                            final int createMode = mCurrentDimSide == CTRL_LEFT
+                                    ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+                                    : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+                            mService.mActivityManager.moveTaskToDockedStack(
+                                    mTask.mTaskId, createMode, true /*toTop*/);
+                        }
                     } catch(RemoteException e) {}
+
                     // Post back to WM to handle clean-ups. We still need the input
                     // event handler for the last finishInputEvent()!
                     mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
-                    if (mCurrentDimSide != CTRL_NONE) {
-                        final int createMode = mCurrentDimSide == CTRL_LEFT
-                                ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
-                                : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-                        mService.mActivityManager.moveTaskToDockedStack(
-                                mTaskId, createMode, true /*toTop*/);
-                    }
                 }
                 handled = true;
             } catch (Exception e) {
@@ -291,10 +298,6 @@
         mService.resumeRotationLocked();
     }
 
-    boolean isTaskResizing(final Task task) {
-        return mResizing && task != null && mTaskId == task.mTaskId;
-    }
-
     void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
         if (DEBUG_TASK_POSITIONING) {
             Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
@@ -318,13 +321,16 @@
             mResizing = true;
         }
 
-        final Task task = win.getTask();
-        mTaskId = task.mTaskId;
-        mStack = task.mStack;
+        mTask = win.getTask();
         mStartDragX = startX;
         mStartDragY = startY;
 
-        mService.getTaskBounds(mTaskId, mWindowOriginalBounds);
+        mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
+    }
+
+    private void endDragLocked() {
+        mResizing = false;
+        mTask.setDragResizing(false);
     }
 
     /** Returns true if the move operation should be ended. */
@@ -354,11 +360,12 @@
                 bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
             }
             mWindowDragBounds.set(left, top, right, bottom);
+            mTask.setDragResizing(true);
             return false;
         }
 
         // This is a moving operation.
-        mStack.getBounds(mTmpRect);
+        mTask.mStack.getBounds(mTmpRect);
         mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
         if (!mTmpRect.contains((int) x, (int) y)) {
             // We end the moving operation if position is outside the stack bounds.
@@ -397,13 +404,13 @@
      * shouldn't be shown.
      */
     private int getDimSide(int x) {
-        if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
-                || !mStack.isFullscreen()
+        if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+                || !mTask.mStack.isFullscreen()
                 || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
             return CTRL_NONE;
         }
 
-        mStack.getBounds(mTmpRect);
+        mTask.mStack.getBounds(mTmpRect);
         if (x - mSideMargin <= mTmpRect.left) {
             return CTRL_LEFT;
         }
@@ -415,7 +422,7 @@
     }
 
     private void showDimLayer() {
-        mStack.getBounds(mTmpRect);
+        mTask.mStack.getBounds(mTmpRect);
         if (mCurrentDimSide == CTRL_LEFT) {
             mTmpRect.right = mTmpRect.centerX();
         } else if (mCurrentDimSide == CTRL_RIGHT) {
@@ -433,7 +440,7 @@
 
     @Override /** {@link DimLayer.DimLayerUser} */
     public DisplayInfo getDisplayInfo() {
-        return mStack.getDisplayInfo();
+        return mTask.mStack.getDisplayInfo();
     }
 
     private int getDragLayerLocked() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5bf296e..ffc848d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2589,9 +2589,13 @@
                     }
                 }
 
-                dragResizing = win.isDragResizing();
-                if (win.mDragResizing != dragResizing) {
-                    win.mDragResizing = dragResizing;
+                // If we're starting a drag-resize, we'll be changing the surface size as well as
+                // notifying the client to render to with an offset from the surface's top-left.
+                // Do a screen freeze, and keep the old surface until the the first frame drawn to
+                // the new surface comes back, so that we avoid a flash due to mismatching surface
+                // setups on window manager side and client side.
+                if (win.isDragResizeChanged()) {
+                    win.setDragResizing();
                     if (win.mHasSurface) {
                         winAnimator.mDestroyPendingSurfaceUponRedraw = true;
                         winAnimator.mSurfaceDestroyDeferred = true;
@@ -2600,6 +2604,7 @@
                         toBeDisplayed = true;
                     }
                 }
+                dragResizing = win.isDragResizing();
                 try {
                     if (!win.mHasSurface) {
                         surfaceChanged = true;
@@ -4688,16 +4693,10 @@
                 throw new IllegalArgumentException("resizeTask: taskId " + taskId
                         + " not found.");
             }
-            final int boundsChanged = task.setBounds(bounds, configuration);
-            if (boundsChanged != Task.BOUNDS_CHANGE_NONE) {
-                if ((boundsChanged & Task.BOUNDS_CHANGE_SIZE) == Task.BOUNDS_CHANGE_SIZE) {
-                    task.resizeWindows();
-                }
 
-                if (relayout) {
-                    task.getDisplayContent().layoutNeeded = true;
-                    mWindowPlacerLocked.performSurfacePlacement();
-                }
+            if (task.resizeLocked(bounds, configuration) && relayout) {
+                task.getDisplayContent().layoutNeeded = true;
+                mWindowPlacerLocked.performSurfacePlacement();
             }
         }
     }
@@ -8480,7 +8479,7 @@
                 Slog.v(TAG, "Win " + w + " config changed: "
                         + mCurConfiguration);
             }
-            final boolean dragResizingChanged = w.mDragResizing != w.isDragResizing();
+            final boolean dragResizingChanged = w.isDragResizeChanged();
             if (localLOGV) Slog.v(TAG, "Resizing " + w
                     + ": configChanged=" + configChanged
                     + " dragResizingChanged=" + dragResizingChanged
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fa28eba..7ed5249 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -129,6 +129,7 @@
     boolean mAttachedHidden;    // is our parent window hidden?
     boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
     boolean mDragResizing;
+    boolean mDragResizeChanging;
 
     RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
 
@@ -1691,9 +1692,18 @@
         return task != null && task.inFreeformWorkspace();
     }
 
-    boolean isDragResizing() {
+    boolean isDragResizeChanged() {
         final Task task = getTask();
-        return mService.mTaskPositioner != null && mService.mTaskPositioner.isTaskResizing(task);
+        return task != null && mDragResizing != task.isDragResizing();
+    }
+
+    void setDragResizing() {
+        final Task task = getTask();
+        mDragResizing = task != null && task.isDragResizing();
+    }
+
+    boolean isDragResizing() {
+        return mDragResizing;
     }
 
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e6fef2f..07e1fce 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -818,7 +818,7 @@
                 // so that we don't need to reallocate during the process. This also prevents
                 // buffer drops due to size mismatch.
                 final DisplayInfo displayInfo = w.getDisplayInfo();
-                if (displayInfo != null && w.mDragResizing) {
+                if (displayInfo != null && w.isDragResizing()) {
                     left = 0;
                     top = 0;
                     width = displayInfo.logicalWidth;
@@ -1211,6 +1211,13 @@
             return;
         } else if (mIsWallpaper && mService.mWindowPlacerLocked.mWallpaperActionPending) {
             return;
+        } else if (mWin.isDragResizeChanged()) {
+            // This window is awaiting a relayout because user just started (or ended)
+            // drag-resizing. The shown frame (which affects surface size and pos)
+            // should not be updated until we get next finished draw with the new surface.
+            // Otherwise one or two frames rendered with old settings would be displayed
+            // with new geometry.
+            return;
         }
 
         if (WindowManagerService.localLOGV) Slog.v(
@@ -1333,7 +1340,7 @@
 
         final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight);
         final Rect clipRect = mTmpClipRect;
-        if (w.mDragResizing) {
+        if (w.isDragResizing()) {
             // When we're doing a drag-resizing, the surface is set up to cover full screen.
             // Set the clip rect to be the same size so that we don't get any scaling.
             clipRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
@@ -1423,7 +1430,7 @@
             // so that we don't need to reallocate during the process. This also prevents
             // buffer drops due to size mismatch.
             final DisplayInfo displayInfo = w.getDisplayInfo();
-            if (displayInfo != null && w.mDragResizing) {
+            if (displayInfo != null && w.isDragResizing()) {
                 left = 0;
                 top = 0;
                 width = displayInfo.logicalWidth;