Fixes for dim bounds and touch bounds

- Change dimming and touch related usage to use task's "max visible
  bounds", which is the app within a task that's covering the maximum
  visible area looking down from top. This solves the problem where
  an app pops up a dialog window. We should dimming (and allow touch)
  the entire task area, not just the dialog's visible area.

- Fix initial bounds used in drag moving/resizing, this should be
  visible bounds of the app main window, not the original task bounds.

- Fix touch region set up for freeform apps, even when task is not
  full screen, we should get the max visible bounds first (as freeform
  app could have dialogs too).

bug: 25494928
bug: 25487005
bug: 25613403

Change-Id: Ib1c6a1665fb83ded2fcb0a7ea92cf1def5372edd
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 85a12db..4b3620f 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -70,7 +70,7 @@
         /** Returns the display info. of the dim layer user. */
         DisplayInfo getDisplayInfo();
         /** Gets the bounds of the dim layer user. */
-        void getBounds(Rect outBounds);
+        void getDimBounds(Rect outBounds);
         String toShortString();
     }
     /** The user of this dim layer. */
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 921d27c..bd30bd5 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -43,7 +43,7 @@
 
     /** Updates the dim layer bounds, recreating it if needed. */
     void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
-        DimLayerState state = getOrCreateDimLayerState(dimLayerUser, false);
+        DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
         final boolean previousFullscreen = state.dimLayer != null
                 && state.dimLayer == mSharedFullScreenDimLayer;
         DimLayer newDimLayer;
@@ -63,7 +63,7 @@
                     // Create new full screen dim layer.
                     newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
                 }
-                dimLayerUser.getBounds(mTmpBounds);
+                dimLayerUser.getDimBounds(mTmpBounds);
                 newDimLayer.setBounds(mTmpBounds);
                 mSharedFullScreenDimLayer = newDimLayer;
             } else if (state.dimLayer != null) {
@@ -73,14 +73,13 @@
             newDimLayer = (state.dimLayer == null || previousFullscreen)
                     ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
                     : state.dimLayer;
-            dimLayerUser.getBounds(mTmpBounds);
+            dimLayerUser.getDimBounds(mTmpBounds);
             newDimLayer.setBounds(mTmpBounds);
         }
         state.dimLayer = newDimLayer;
     }
 
-    private DimLayerState getOrCreateDimLayerState(
-            DimLayer.DimLayerUser dimLayerUser, boolean aboveApp) {
+    private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
         if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
                 + dimLayerUser.toShortString());
         DimLayerState state = mState.get(dimLayerUser);
@@ -88,7 +87,6 @@
             state = new DimLayerState();
             mState.put(dimLayerUser, state);
         }
-        state.dimAbove = aboveApp;
         return state;
     }
 
@@ -127,7 +125,8 @@
             WindowStateAnimator newWinAnimator, boolean aboveApp) {
         // Only set dim params on the highest dimmed layer.
         // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
-        DimLayerState state = getOrCreateDimLayerState(dimLayerUser, aboveApp);
+        DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
+        state.dimAbove = aboveApp;
         if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
                 + " dimLayerUser=" + dimLayerUser.toShortString()
                 + " newWinAnimator=" + newWinAnimator
@@ -161,7 +160,7 @@
                 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
         if (!state.continueDimming && state.dimLayer.isDimming()) {
             state.animator = null;
-            dimLayerUser.getBounds(mTmpBounds);
+            dimLayerUser.getDimBounds(mTmpBounds);
             state.dimLayer.setBounds(mTmpBounds);
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8c00a57..f54fd83 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -297,7 +297,7 @@
                 // windows frames when the app window is the IME target.
                 final WindowState win = task.getTopAppMainWindow();
                 if (win != null) {
-                    win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+                    win.getVisibleBounds(mTmpRect);
                     if (mTmpRect.contains(x, y)) {
                         return task.mTaskId;
                     }
@@ -332,7 +332,7 @@
                 // start at (0,0) after it's adjusted for the status bar.)
                 final WindowState win = task.getTopAppMainWindow();
                 if (win != null) {
-                    win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+                    win.getVisibleBounds(mTmpRect);
                     mTmpRect.inset(-delta, -delta);
                     if (mTmpRect.contains(x, y)) {
                         mTmpRect.inset(delta, delta);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index c1e0481..6b62467 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -87,7 +87,7 @@
             frame.set(mLastRect);
             return;
         } else {
-            stack.getBounds(mTmpRect);
+            stack.getDimBounds(mTmpRect);
         }
         int side = stack.getDockSide();
         switch (side) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 400cc5e..2be7ab8 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -435,7 +435,7 @@
                 continue;
             }
 
-            child.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+            child.getVisibleBounds(mTmpRect);
             if (!mTmpRect.contains(x, y)) {
                 // outside of this window's activity stack == don't tell about drags
                 continue;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6aaf3c7..bb99787 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -252,8 +252,7 @@
         return false;
     }
 
-    /** Bounds of the task with other system factors taken into consideration. */
-    @Override
+    /** Original bounds of the task if applicable, otherwise fullscreen rect. */
     public void getBounds(Rect out) {
         if (useCurrentBounds()) {
             // No need to adjust the output bounds if fullscreen or the docked stack is visible
@@ -268,6 +267,65 @@
         mStack.getDisplayContent().getLogicalDisplayRect(out);
     }
 
+
+    /**
+     * Calculate the maximum visible area of this task. If the task has only one app,
+     * the result will be visible frame of that app. If the task has more than one apps,
+     * we search from top down if the next app got different visible area.
+     *
+     * This effort is to handle the case where some task (eg. GMail composer) might pop up
+     * a dialog that's different in size from the activity below, in which case we should
+     * be dimming the entire task area behind the dialog.
+     *
+     * @param out Rect containing the max visible bounds.
+     * @return true if the task has some visible app windows; false otherwise.
+     */
+    boolean getMaxVisibleBounds(Rect out) {
+        boolean foundTop = false;
+        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+            final WindowState win = mAppTokens.get(i).findMainWindow();
+            if (win == null) {
+                continue;
+            }
+            if (!foundTop) {
+                out.set(win.mVisibleFrame);
+                foundTop = true;
+                continue;
+            }
+            if (win.mVisibleFrame.left < out.left) {
+                out.left = win.mVisibleFrame.left;
+            }
+            if (win.mVisibleFrame.top < out.top) {
+                out.top = win.mVisibleFrame.top;
+            }
+            if (win.mVisibleFrame.right > out.right) {
+                out.right = win.mVisibleFrame.right;
+            }
+            if (win.mVisibleFrame.bottom > out.bottom) {
+                out.bottom = win.mVisibleFrame.bottom;
+            }
+        }
+        return foundTop;
+    }
+
+    /** Bounds of the task to be used for dimming, as well as touch related tests. */
+    @Override
+    public void getDimBounds(Rect out) {
+        if (useCurrentBounds()) {
+            if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
+                return;
+            }
+
+            out.set(mBounds);
+            return;
+        }
+
+        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
+        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
+        // system.
+        mStack.getDisplayContent().getLogicalDisplayRect(out);
+    }
+
     void setDragResizing(boolean dragResizing) {
         mDragResizing = dragResizing;
     }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index aae3bd2..dd47e7a 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -149,7 +149,7 @@
                         }
                         synchronized (mService.mWindowMap) {
                             mDragEnded = notifyMoveLocked(newX, newY);
-                            mTask.getBounds(mTmpRect);
+                            mTask.getDimBounds(mTmpRect);
                         }
                         if (!mTmpRect.equals(mWindowDragBounds)) {
                             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
@@ -353,7 +353,9 @@
         mStartDragX = startX;
         mStartDragY = startY;
 
-        mService.getTaskBounds(mTask.mTaskId, mWindowOriginalBounds);
+        // Use the visible bounds, not the original task bounds. The cursor
+        // movement should be calculated relative to the visible bounds.
+        mWindowOriginalBounds.set(win.mVisibleFrame);
     }
 
     private void endDragLocked() {
@@ -393,7 +395,7 @@
         }
 
         // This is a moving operation.
-        mTask.mStack.getBounds(mTmpRect);
+        mTask.mStack.getDimBounds(mTmpRect);
         mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
         if (!mTmpRect.contains((int) x, (int) y)) {
             // We end the moving operation if position is outside the stack bounds.
@@ -438,7 +440,7 @@
             return CTRL_NONE;
         }
 
-        mTask.mStack.getBounds(mTmpRect);
+        mTask.mStack.getDimBounds(mTmpRect);
         if (x - mSideMargin <= mTmpRect.left) {
             return CTRL_LEFT;
         }
@@ -450,7 +452,7 @@
     }
 
     private void showDimLayer() {
-        mTask.mStack.getBounds(mTmpRect);
+        mTask.mStack.getDimBounds(mTmpRect);
         if (mCurrentDimSide == CTRL_LEFT) {
             mTmpRect.right = mTmpRect.centerX();
         } else if (mCurrentDimSide == CTRL_RIGHT) {
@@ -473,7 +475,7 @@
     }
 
     @Override
-    public void getBounds(Rect out) {
+    public void getDimBounds(Rect out) {
         // This dim layer user doesn't need this.
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7c02b43..8085f13 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -189,8 +189,6 @@
         return false;
     }
 
-    /** Bounds of the stack with other system factors taken into consideration. */
-    @Override
     public void getBounds(Rect out) {
         if (useCurrentBounds()) {
             // No need to adjust the output bounds if fullscreen or the docked stack is visible
@@ -205,6 +203,12 @@
         mDisplayContent.getLogicalDisplayRect(out);
     }
 
+    /** Bounds of the stack with other system factors taken into consideration. */
+    @Override
+    public void getDimBounds(Rect out) {
+        getBounds(out);
+    }
+
     void updateDisplayInfo(Rect bounds) {
         if (mDisplayContent != null) {
             for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index a33fb13..1fe359e 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -94,7 +94,7 @@
                 if (window == null) {
                     break;
                 }
-                window.getVisibleBounds(mTmpRect, false);
+                window.getVisibleBounds(mTmpRect);
                 if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
                     int iconShape = STYLE_DEFAULT;
                     if (x < mTmpRect.left) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6ad5b93..4175edd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5849,7 +5849,7 @@
                         int right = wf.right - cr.right;
                         int bottom = wf.bottom - cr.bottom;
                         frame.union(left, top, right, bottom);
-                        ws.getVisibleBounds(stackBounds, !BOUNDS_FOR_TOUCH);
+                        ws.getVisibleBounds(stackBounds);
                         if (!frame.intersect(stackBounds)) {
                             // Set frame empty if there's no intersection.
                             frame.setEmpty();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 080a2d1..93b40c0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -962,17 +962,15 @@
     /**
      * Retrieves the visible bounds of the window.
      * @param bounds The rect which gets the bounds.
-     * @param forTouch Pass in BOUNDS_FOR_TOUCH to get touch related bounds, otherwise visible
-     *        bounds will be returned.
      */
-    void getVisibleBounds(Rect bounds, boolean forTouch) {
+    void getVisibleBounds(Rect bounds) {
         boolean intersectWithStackBounds = mAppToken != null && mAppToken.mCropWindowsToStack;
         bounds.setEmpty();
         mTmpRect.setEmpty();
         if (intersectWithStackBounds) {
             final TaskStack stack = getStack();
             if (stack != null) {
-                stack.getBounds(mTmpRect);
+                stack.getDimBounds(mTmpRect);
             } else {
                 intersectWithStackBounds = false;
             }
@@ -990,12 +988,6 @@
             }
             return;
         }
-        if (forTouch && inFreeformWorkspace()) {
-            final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
-            final int delta = WindowManagerService.dipToPixel(
-                    RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
-            bounds.inset(-delta, -delta);
-        }
     }
 
     public long getInputDispatchingTimeoutNanos() {
@@ -1415,22 +1407,24 @@
         if (modal && mAppToken != null) {
             // Limit the outer touch to the activity stack region.
             flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-            if (!inFreeformWorkspace()) {
-                // If this is a modal window we need to dismiss it if it's not full screen and the
-                // touch happens outside of the frame that displays the content. This means we
-                // need to intercept touches outside of that window. The dim layer user
-                // associated with the window (task or stack) will give us the good bounds, as
-                // they would be used to display the dim layer.
-                final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
-                if (dimLayerUser != null) {
-                    dimLayerUser.getBounds(mTmpRect);
-                } else {
-                    getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
-                }
+            // If this is a modal window we need to dismiss it if it's not full screen and the
+            // touch happens outside of the frame that displays the content. This means we
+            // need to intercept touches outside of that window. The dim layer user
+            // associated with the window (task or stack) will give us the good bounds, as
+            // they would be used to display the dim layer.
+            final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+            if (dimLayerUser != null) {
+                dimLayerUser.getDimBounds(mTmpRect);
             } else {
+                getVisibleBounds(mTmpRect);
+            }
+            if (inFreeformWorkspace()) {
                 // For freeform windows we the touch region to include the whole surface for the
                 // shadows.
-                getVisibleBounds(mTmpRect, BOUNDS_FOR_TOUCH);
+                final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+                final int delta = WindowManagerService.dipToPixel(
+                        RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+                mTmpRect.inset(-delta, -delta);
             }
             region.set(mTmpRect);
         } else {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 10f737f..a3bb320 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1133,7 +1133,7 @@
         // gets removed. The window that will replace it will abide them.
         if (task != null && appToken.mCropWindowsToStack && !appToken.mWillReplaceWindow) {
             TaskStack stack = task.mStack;
-            stack.getBounds(mTmpStackBounds);
+            stack.getDimBounds(mTmpStackBounds);
             // When we resize we use the big surface approach, which means we can't trust the
             // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
             // hardcoding it, we use surface coordinates.
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4b9a538..3b57634 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -932,6 +932,13 @@
                     win.prelayout();
                     mService.mPolicy.layoutWindowLw(win, null);
                     win.mLayoutSeq = seq;
+
+                    // Window frames may have changed. Update dim layer with the new bounds.
+                    final Task task = win.getTask();
+                    if (task != null) {
+                        displayContent.mDimLayerController.updateDimLayer(task);
+                    }
+
                     if (DEBUG_LAYOUT) Slog.v(TAG,
                             "  LAYOUT: mFrame="
                             + win.mFrame + " mContainingFrame="
@@ -986,7 +993,7 @@
             }
         }
 
-        // Window frames may have changed.  Tell the input dispatcher about it.
+        // Window frames may have changed. Tell the input dispatcher about it.
         mService.mInputMonitor.setUpdateInputWindowsNeededLw();
         if (updateInputWindows) {
             mService.mInputMonitor.updateInputWindowsLw(false /*force*/);