Remove some hard-coded fullscreen-specific hierarchy logic

There were a couple issues where evaluation was hard-coded:
1. detecting window movement was always relative to the display.
   Because animations are moving to be more "leashed", this
   detection method can cause double-animations.
2. The Task-level configuration resolution was hard-coded to
   assume that null-bounds would inherit from the display. This
   breaks down when we try to influence parent configs (eg.
   Stack or, in the future, parent Tasks) and causes the app
   to receive configs that don't match its window size.

For (1) this CL changes WindowState to only animation movement
if the windows have moved relative to the root-windowstate's
parent. This works because movements above Activity-level
are done by WM and have animations already (eg. transitions
or task-org leashes) while movements below activity-level
are triggered by app (eg. relayout) and don't have intrinsic
animations.

For (2) this CL adjusts the task-level resolution to always
calculate relative to its parent, even with null bounds.

Bug: 133381692
Bug: 133381284
Test: Added to TaskRecordTests and updated WindowFrameTests
Change-Id: I172ca65465bbfdfe5e2022c5a4bfb6b6f0140b24
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 91c909d..afcde95 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -421,8 +421,6 @@
     /** Stores the override windowing-mode from before a transient mode change (eg. split) */
     private int mRestoreOverrideWindowingMode = WINDOWING_MODE_UNDEFINED;
 
-    private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
-
     /** List for processing through a set of activities */
     private final ArrayList<ActivityRecord> mTmpActivities = new ArrayList<>();
 
@@ -749,7 +747,6 @@
             // Leaving a transient mode. Interpret UNDEFINED as "restore"
             windowingMode = mRestoreOverrideWindowingMode;
         }
-        mTmpOptions.setLaunchWindowingMode(windowingMode);
 
         // Need to make sure windowing mode is supported. If we in the process of creating the stack
         // no need to resolve the windowing mode again as it is already resolved to the right mode.
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 0c68084..c76d03c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -541,8 +541,8 @@
             }
             mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
             mIsWaitingForRemoteRotation = false;
-            mService.mAtmService.applyContainerTransaction(t);
             mDisplayContent.sendNewConfiguration();
+            mService.mAtmService.applyContainerTransaction(t);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b229a1d..8992be2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -60,9 +60,7 @@
 import static com.android.server.EventLogTags.WM_TASK_REMOVED;
 import static com.android.server.am.TaskRecordProto.ACTIVITIES;
 import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
-import static com.android.server.am.TaskRecordProto.BOUNDS;
 import static com.android.server.am.TaskRecordProto.FULLSCREEN;
-import static com.android.server.am.TaskRecordProto.ID;
 import static com.android.server.am.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS;
 import static com.android.server.am.TaskRecordProto.MIN_HEIGHT;
 import static com.android.server.am.TaskRecordProto.MIN_WIDTH;
@@ -89,10 +87,8 @@
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
-import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
-import static com.android.server.wm.TaskProto.ID;
 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
 import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
@@ -314,6 +310,7 @@
     private final Rect mTmpNonDecorBounds = new Rect();
     private final Rect mTmpBounds = new Rect();
     private final Rect mTmpInsets = new Rect();
+    private final Rect mTmpFullBounds = new Rect();
 
     // Last non-fullscreen bounds the task was launched in or resized to.
     // The information is persisted and used to determine the appropriate stack to launch the
@@ -1673,7 +1670,9 @@
     }
 
     void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
-        if (bounds == null) {
+        final Rect parentBounds = getParent() != null ? getParent().getBounds() : null;
+        if (bounds == null
+                || (bounds.isEmpty() && (parentBounds == null || parentBounds.isEmpty()))) {
             return;
         }
         int minWidth = mMinWidth;
@@ -1697,6 +1696,14 @@
                 minHeight = defaultMinSize;
             }
         }
+        if (bounds.isEmpty()) {
+            // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
+            // do, we can just skip.
+            if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
+                return;
+            }
+            bounds.set(parentBounds);
+        }
         final boolean adjustWidth = minWidth > bounds.width();
         final boolean adjustHeight = minHeight > bounds.height();
         if (!(adjustWidth || adjustHeight)) {
@@ -1948,10 +1955,19 @@
         }
         density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
 
-        final Rect bounds = inOutConfig.windowConfiguration.getBounds();
+        final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
+        if (resolvedBounds == null) {
+            mTmpFullBounds.setEmpty();
+        } else {
+            mTmpFullBounds.set(resolvedBounds);
+        }
+        if (mTmpFullBounds.isEmpty()) {
+            mTmpFullBounds.set(parentConfig.windowConfiguration.getBounds());
+        }
+
         Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
         if (outAppBounds == null || outAppBounds.isEmpty()) {
-            inOutConfig.windowConfiguration.setAppBounds(bounds);
+            inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
             outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
         }
         // Non-null compatibility insets means the activity prefers to keep its original size, so
@@ -1974,7 +1990,7 @@
                 // area, i.e. the screen area without the system bars.
                 // The non decor inset are areas that could never be removed in Honeycomb. See
                 // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
-                calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, bounds, di);
+                calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
             } else {
                 // Apply the given non-decor and stable insets to calculate the corresponding bounds
                 // for screen size of configuration.
@@ -1983,8 +1999,8 @@
                     rotation = parentConfig.windowConfiguration.getRotation();
                 }
                 if (rotation != ROTATION_UNDEFINED && compatInsets != null) {
-                    mTmpNonDecorBounds.set(bounds);
-                    mTmpStableBounds.set(bounds);
+                    mTmpNonDecorBounds.set(mTmpFullBounds);
+                    mTmpStableBounds.set(mTmpFullBounds);
                     compatInsets.getDisplayBoundsByRotation(mTmpBounds, rotation);
                     intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
                             compatInsets.mNonDecorInsets[rotation]);
@@ -2016,13 +2032,13 @@
                 if (WindowConfiguration.isFloating(windowingMode)) {
                     // For floating tasks, calculate the smallest width from the bounds of the task
                     inOutConfig.smallestScreenWidthDp = (int) (
-                            Math.min(bounds.width(), bounds.height()) / density);
+                            Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
                 } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
                     // Iterating across all screen orientations, and return the minimum of the task
                     // width taking into account that the bounds might change because the snap
                     // algorithm snaps to a different value
                     inOutConfig.smallestScreenWidthDp =
-                            getSmallestScreenWidthDpForDockedBounds(bounds);
+                            getSmallestScreenWidthDpForDockedBounds(mTmpFullBounds);
                 }
                 // otherwise, it will just inherit
             }
@@ -2067,11 +2083,6 @@
                     newParentConfig.orientation);
         }
 
-        if (outOverrideBounds.isEmpty()) {
-            // If the task fills the parent, just inherit all the other configs from parent.
-            return;
-        }
-
         adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
         if (windowingMode == WINDOWING_MODE_FREEFORM) {
             // by policy, make sure the window remains within parent somewhere
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index d7bc072..8ad8972 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -284,7 +284,8 @@
                     && outParams.mBounds.isEmpty()
                     && source.getDisplayId() == display.mDisplayId) {
                 // Set bounds to be not very far from source activity.
-                cascadeBounds(source.getBounds(), display, outParams.mBounds);
+                cascadeBounds(source.getConfiguration().windowConfiguration.getBounds(),
+                        display, outParams.mBounds);
             }
             getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds);
         }
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 84fcfbd..bafa38c 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -106,6 +106,16 @@
      */
     final Rect mLastFrame = new Rect();
 
+    /**
+     * mFrame but relative to the parent container.
+     */
+    final Rect mRelFrame = new Rect();
+
+    /**
+     * mLastFrame but relative to the parent container
+     */
+    final Rect mLastRelFrame = new Rect();
+
     private boolean mFrameSizeChanged = false;
 
     // Frame that is scaled to the application's coordinate space when in
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f98e307..db0f3bc 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -212,7 +212,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
-import com.android.server.wm.utils.InsetUtils;
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
@@ -1129,6 +1128,22 @@
             }
         }
 
+        // Calculate relative frame
+        mWindowFrames.mRelFrame.set(mWindowFrames.mFrame);
+        WindowContainer parent = getParent();
+        int parentLeft = 0;
+        int parentTop = 0;
+        if (mIsChildWindow) {
+            parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
+            parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
+        } else if (parent != null) {
+            final Rect parentBounds = parent.getDisplayedBounds();
+            parentLeft = parentBounds.left;
+            parentTop = parentBounds.top;
+        }
+        mWindowFrames.mRelFrame.offsetTo(mWindowFrames.mFrame.left - parentLeft,
+                mWindowFrames.mFrame.top - parentTop);
+
         if (DEBUG_LAYOUT || DEBUG) {
             Slog.v(TAG, "Resolving (mRequestedWidth="
                             + mRequestedWidth + ", mRequestedheight="
@@ -1154,6 +1169,11 @@
         return mWindowFrames.mFrame;
     }
 
+    /** Accessor for testing */
+    Rect getRelativeFrameLw() {
+        return mWindowFrames.mRelFrame;
+    }
+
     @Override
     public Rect getDisplayFrameLw() {
         return mWindowFrames.mDisplayFrame;
@@ -1289,6 +1309,7 @@
         // We update mLastFrame always rather than in the conditional with the last inset
         // variables, because mFrameSizeChanged only tracks the width and height changing.
         mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
+        mWindowFrames.mLastRelFrame.set(mWindowFrames.mRelFrame);
 
         if (didFrameInsetsChange
                 || winAnimator.mSurfaceResized
@@ -1859,8 +1880,8 @@
     private boolean hasMoved() {
         return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize)
                 && !mAnimatingExit
-                && (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
-                    || mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
+                && (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top
+                    || mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left)
                 && (!mIsChildWindow || !getParentWindow().hasMoved());
     }