Merge "Cleanup TaskFragment WindowContainerTransaction Change"
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7f1a4d1..474e148 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3829,6 +3829,7 @@
     method @NonNull public android.window.WindowContainerTransaction setHidden(@NonNull android.window.WindowContainerToken, boolean);
     method @NonNull public android.window.WindowContainerTransaction setLaunchAdjacentFlagRoot(@NonNull android.window.WindowContainerToken);
     method @NonNull public android.window.WindowContainerTransaction setLaunchRoot(@NonNull android.window.WindowContainerToken, @Nullable int[], @Nullable int[]);
+    method @NonNull public android.window.WindowContainerTransaction setRelativeBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
     method @NonNull public android.window.WindowContainerTransaction setScreenSizeDp(@NonNull android.window.WindowContainerToken, int, int);
     method @NonNull public android.window.WindowContainerTransaction setSmallestScreenWidthDp(@NonNull android.window.WindowContainerToken, int);
     method @NonNull public android.window.WindowContainerTransaction setWindowingMode(@NonNull android.window.WindowContainerToken, int);
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
index dc60edd..a881a05 100644
--- a/core/java/android/window/TaskFragmentInfo.java
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -89,9 +88,9 @@
     private final boolean mIsClearedForReorderActivityToFront;
 
     /**
-     * The maximum {@link ActivityInfo.WindowLayout#minWidth} and
-     * {@link ActivityInfo.WindowLayout#minHeight} aggregated from the TaskFragment's child
-     * activities.
+     * The maximum {@link android.content.pm.ActivityInfo.WindowLayout#minWidth} and
+     * {@link android.content.pm.ActivityInfo.WindowLayout#minHeight} aggregated from the
+     * TaskFragment's child activities.
      */
     @NonNull
     private final Point mMinimumDimensions = new Point();
@@ -179,7 +178,7 @@
 
     /**
      * Returns the minimum width this TaskFragment can be resized to.
-     * Client side must not {@link WindowContainerTransaction#setBounds(WindowContainerToken, Rect)}
+     * Client side must not {@link WindowContainerTransaction#setRelativeBounds}
      * that {@link Rect#width()} is shorter than the reported value.
      * @hide pending unhide
      */
@@ -189,7 +188,7 @@
 
     /**
      * Returns the minimum width this TaskFragment can be resized to.
-     * Client side must not {@link WindowContainerTransaction#setBounds(WindowContainerToken, Rect)}
+     * Client side must not {@link WindowContainerTransaction#setRelativeBounds}
      * that {@link Rect#height()} is shorter than the reported value.
      * @hide pending unhide
      */
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 0d6a58b..413f0cc 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -38,8 +38,8 @@
 public final class TaskFragmentOperation implements Parcelable {
 
     /**
-     * Type for tracking other {@link WindowContainerTransaction} to TaskFragment that is not set
-     * through {@link TaskFragmentOperation}, such as {@link WindowContainerTransaction#setBounds}.
+     * Type for tracking other unknown TaskFragment operation that is not set through
+     * {@link TaskFragmentOperation}, such as invalid request.
      */
     public static final int OP_TYPE_UNKNOWN = -1;
 
@@ -70,6 +70,9 @@
     /** Sets the {@link TaskFragmentAnimationParams} for the given TaskFragment. */
     public static final int OP_TYPE_SET_ANIMATION_PARAMS = 8;
 
+    /** Sets the relative bounds with {@link WindowContainerTransaction#setRelativeBounds}. */
+    public static final int OP_TYPE_SET_RELATIVE_BOUNDS = 9;
+
     @IntDef(prefix = { "OP_TYPE_" }, value = {
             OP_TYPE_UNKNOWN,
             OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -80,7 +83,8 @@
             OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS,
             OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT,
             OP_TYPE_SET_COMPANION_TASK_FRAGMENT,
-            OP_TYPE_SET_ANIMATION_PARAMS
+            OP_TYPE_SET_ANIMATION_PARAMS,
+            OP_TYPE_SET_RELATIVE_BOUNDS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface OperationType {}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index a2fa162..a3209f6 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -308,7 +308,6 @@
     /**
      * Resizes a container by providing a bounds in its parent coordinate.
      * This is only used by {@link TaskFragmentOrganizer}.
-     * @hide
      */
     @NonNull
     public WindowContainerTransaction setRelativeBounds(
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 957d99a..f9592cc 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -29,6 +29,7 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
 import static android.window.TaskFragmentOperation.OP_TYPE_SET_COMPANION_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
 import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS;
@@ -94,6 +95,7 @@
 import android.window.TaskFragmentAnimationParams;
 import android.window.TaskFragmentCreationParams;
 import android.window.TaskFragmentOperation;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -784,8 +786,8 @@
         taskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
         final Rect relBounds = c.getRelativeBounds();
         if (relBounds != null) {
-            // Make sure the TaskFragment bounds satisfied the min dimensions requirement.
-            adjustTaskFragmentBoundsForMinDimensionsIfNeeded(taskFragment, relBounds,
+            // Make sure the requested bounds satisfied the min dimensions requirement.
+            adjustTaskFragmentRelativeBoundsForMinDimensionsIfNeeded(taskFragment, relBounds,
                     errorCallbackToken);
 
             // For embedded TaskFragment, the organizer set the bounds in parent coordinate to
@@ -797,13 +799,6 @@
                     parentBounds);
             c.getConfiguration().windowConfiguration.setBounds(absBounds);
             taskFragment.setRelativeEmbeddedBounds(relBounds);
-        } else if ((c.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0) {
-            // TODO(b/265271880): remove after we drop support to setBounds for TaskFragment in next
-            // release.
-            adjustTaskFragmentBoundsForMinDimensionsIfNeeded(taskFragment, c.getConfiguration()
-                    .windowConfiguration.getBounds(), errorCallbackToken);
-            // Reset the relative embedded bounds if WCT#setBounds is used instead for CTS compat.
-            taskFragment.setRelativeEmbeddedBounds(new Rect());
         }
         final int effects = applyChanges(taskFragment, c);
         if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
@@ -814,25 +809,27 @@
     }
 
     /**
-     * Adjusts the override absolute bounds on {@link TaskFragment} to make sure it satisfies the
+     * Adjusts the requested relative bounds on {@link TaskFragment} to make sure it satisfies the
      * activity min dimensions.
      */
-    private void adjustTaskFragmentBoundsForMinDimensionsIfNeeded(
-            @NonNull TaskFragment taskFragment, @NonNull Rect inOutBounds,
+    private void adjustTaskFragmentRelativeBoundsForMinDimensionsIfNeeded(
+            @NonNull TaskFragment taskFragment, @NonNull Rect inOutRelativeBounds,
             @Nullable IBinder errorCallbackToken) {
-        if (inOutBounds.isEmpty()) {
+        if (inOutRelativeBounds.isEmpty()) {
             return;
         }
         final Point minDimensions = taskFragment.calculateMinDimension();
-        if (inOutBounds.width() < minDimensions.x || inOutBounds.height() < minDimensions.y) {
-            // Reset to match parent bounds.
-            inOutBounds.setEmpty();
+        if (inOutRelativeBounds.width() < minDimensions.x
+                || inOutRelativeBounds.height() < minDimensions.y) {
             // Notify organizer about the request failure.
-            final Throwable exception = new SecurityException("The task fragment's bounds:"
-                    + taskFragment.getBounds() + " does not satisfy minimum dimensions:"
+            final Throwable exception = new SecurityException("The requested relative bounds:"
+                    + inOutRelativeBounds + " does not satisfy minimum dimensions:"
                     + minDimensions);
             sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
-                    errorCallbackToken, taskFragment, OP_TYPE_UNKNOWN, exception);
+                    errorCallbackToken, taskFragment, OP_TYPE_SET_RELATIVE_BOUNDS, exception);
+
+            // Reset to match parent bounds.
+            inOutRelativeBounds.setEmpty();
         }
     }
 
@@ -1726,9 +1723,7 @@
                 t.getChanges().entrySet().iterator();
         while (entries.hasNext()) {
             final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
-            // Only allow to apply changes to TaskFragment that is created by this organizer.
             final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
-            enforceTaskFragmentOrganized(func, wc, organizer);
             enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), organizer);
         }
 
@@ -1764,27 +1759,6 @@
     }
 
     /**
-     * Makes sure that the given {@link WindowContainer} is a {@link TaskFragment} organized by the
-     * given {@link ITaskFragmentOrganizer}.
-     */
-    private void enforceTaskFragmentOrganized(@NonNull String func, @Nullable WindowContainer wc,
-            @NonNull ITaskFragmentOrganizer organizer) {
-        if (wc == null) {
-            Slog.e(TAG, "Attempt to operate on window that no longer exists");
-            return;
-        }
-
-        final TaskFragment tf = wc.asTaskFragment();
-        if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) {
-            String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid() + " trying to modify window container not"
-                    + " belonging to the TaskFragmentOrganizer=" + organizer;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-    }
-
-    /**
      * Makes sure that the {@link TaskFragment} of the given fragment token is created and organized
      * by the given {@link ITaskFragmentOrganizer}.
      */
@@ -1805,81 +1779,49 @@
     }
 
     /**
-     * Makes sure that SurfaceControl transactions and the ability to set bounds outside of the
-     * parent bounds are not allowed for embedding without full trust between the host and the
-     * target.
+     * For config change on {@link TaskFragment}, we only support the following operations:
+     * {@link WindowContainerTransaction#setRelativeBounds(WindowContainerToken, Rect)},
+     * {@link WindowContainerTransaction#setWindowingMode(WindowContainerToken, int)}.
      */
-    private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc,
-            WindowContainerTransaction.Change change, ITaskFragmentOrganizer organizer) {
+    private void enforceTaskFragmentConfigChangeAllowed(@NonNull String func,
+            @Nullable WindowContainer wc, @NonNull WindowContainerTransaction.Change change,
+            @NonNull ITaskFragmentOrganizer organizer) {
         if (wc == null) {
             Slog.e(TAG, "Attempt to operate on task fragment that no longer exists");
             return;
         }
-        if (change == null) {
+        final TaskFragment tf = wc.asTaskFragment();
+        if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) {
+            // Only allow to apply changes to TaskFragment that is organized by this organizer.
+            String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid() + " trying to modify window container"
+                    + " not belonging to the TaskFragmentOrganizer=" + organizer;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+
+        final int changeMask = change.getChangeMask();
+        final int configSetMask = change.getConfigSetMask();
+        final int windowSetMask = change.getWindowSetMask();
+        if (changeMask == 0 && configSetMask == 0 && windowSetMask == 0
+                && change.getWindowingMode() >= 0) {
+            // The change contains only setWindowingMode, which is allowed.
             return;
         }
-        final int changeMask = change.getChangeMask();
-        if (changeMask != 0 && changeMask != CHANGE_RELATIVE_BOUNDS) {
+        if (changeMask != CHANGE_RELATIVE_BOUNDS
+                || configSetMask != ActivityInfo.CONFIG_WINDOW_CONFIGURATION
+                || windowSetMask != WindowConfiguration.WINDOW_CONFIG_BOUNDS) {
             // None of the change should be requested from a TaskFragment organizer except
-            // setRelativeBounds.
+            // setRelativeBounds and setWindowingMode.
             // For setRelativeBounds, we don't need to check whether it is outside of the Task
             // bounds, because it is possible that the Task is also resizing, for which we don't
             // want to throw an exception. The bounds will be adjusted in
             // TaskFragment#translateRelativeBoundsToAbsoluteBounds.
             String msg = "Permission Denial: " + func + " from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " trying to apply changes of " + changeMask + " to TaskFragment"
-                    + " TaskFragmentOrganizer=" + organizer;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-        // Check if TaskFragment is embedded in fully trusted mode.
-        if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
-            // Fully trusted, no need to check further
-            return;
-        }
-        final WindowContainer wcParent = wc.getParent();
-        if (wcParent == null) {
-            Slog.e(TAG, "Attempt to apply config change on task fragment that has no parent");
-            return;
-        }
-        // TODO(b/265271880): we can remove those and only support WCT#setRelativeBounds.
-        final Configuration requestedConfig = change.getConfiguration();
-        final Configuration parentConfig = wcParent.getConfiguration();
-        if (parentConfig.screenWidthDp < requestedConfig.screenWidthDp
-                || parentConfig.screenHeightDp < requestedConfig.screenHeightDp
-                || parentConfig.smallestScreenWidthDp < requestedConfig.smallestScreenWidthDp) {
-            String msg = "Permission Denial: " + func + " from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " trying to apply screen width/height greater than parent's for non-trusted"
-                    + " host, TaskFragmentOrganizer=" + organizer;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-        if (change.getWindowSetMask() == 0) {
-            // No bounds change.
-            return;
-        }
-        final WindowConfiguration requestedWindowConfig = requestedConfig.windowConfiguration;
-        final WindowConfiguration parentWindowConfig = parentConfig.windowConfiguration;
-        if (!requestedWindowConfig.getBounds().isEmpty()
-                && !parentWindowConfig.getBounds().contains(requestedWindowConfig.getBounds())) {
-            String msg = "Permission Denial: " + func + " from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " trying to apply bounds outside of parent for non-trusted host,"
-                    + " TaskFragmentOrganizer=" + organizer;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-        if (requestedWindowConfig.getAppBounds() != null
-                && !requestedWindowConfig.getAppBounds().isEmpty()
-                && parentWindowConfig.getAppBounds() != null
-                && !parentWindowConfig.getAppBounds().contains(
-                        requestedWindowConfig.getAppBounds())) {
-            String msg = "Permission Denial: " + func + " from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " trying to apply app bounds outside of parent for non-trusted host,"
-                    + " TaskFragmentOrganizer=" + organizer;
+                    + " trying to apply changes of changeMask=" + changeMask
+                    + " configSetMask=" + configSetMask + " windowSetMask=" + windowSetMask
+                    + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 060f9d8..378e8be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -511,7 +511,7 @@
     public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment() {
         // Throw exception if the transaction is trying to change a window that is not organized by
         // the organizer.
-        mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
+        mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
 
         assertApplyTransactionDisallowed(mTransaction);
 
@@ -1154,10 +1154,12 @@
         setupTaskFragmentInPip();
         spyOn(mWindowOrganizerController);
 
-        // Set bounds is ignored on a TaskFragment that is in a PIP Task.
-        mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
+        // Set relative bounds is ignored on a TaskFragment that is in a PIP Task.
+        mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
 
-        verify(mTaskFragment, never()).setBounds(any());
+        assertApplyTransactionAllowed(mTransaction);
+
+        verify(mTaskFragment, never()).setRelativeEmbeddedBounds(any());
     }
 
     @Test
@@ -1368,7 +1370,7 @@
     }
 
     /**
-     * For config change to untrusted embedded TaskFragment, we only allow bounds change within
+     * For config change to untrusted embedded TaskFragment, the bounds should be always within
      * its parent bounds.
      */
     @Test
@@ -1378,51 +1380,17 @@
         doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode();
         final Task task = createTask(mDisplayContent);
         final Rect taskBounds = new Rect(task.getBounds());
-        final Rect taskAppBounds = new Rect(task.getWindowConfiguration().getAppBounds());
-        final int taskScreenWidthDp = task.getConfiguration().screenWidthDp;
-        final int taskScreenHeightDp = task.getConfiguration().screenHeightDp;
-        final int taskSmallestScreenWidthDp = task.getConfiguration().smallestScreenWidthDp;
         task.addChild(mTaskFragment, POSITION_TOP);
 
-        // Throw exception if the transaction is trying to change bounds of an untrusted outside of
-        // its parent's.
-
-        // setBounds
+        // When set a relative bounds outside of its parent's, it is allowed, but the actual
+        // TaskFragment bounds will be updated to be fit the parent's bounds.
         final Rect tfBounds = new Rect(taskBounds);
         tfBounds.right++;
-        mTransaction.setBounds(mFragmentWindowToken, tfBounds);
-        assertApplyTransactionDisallowed(mTransaction);
-
-        mTransaction.setBounds(mFragmentWindowToken, taskBounds);
+        mTransaction.setRelativeBounds(mFragmentWindowToken, tfBounds);
         assertApplyTransactionAllowed(mTransaction);
 
-        // setAppBounds
-        final Rect tfAppBounds = new Rect(taskAppBounds);
-        tfAppBounds.right++;
-        mTransaction.setAppBounds(mFragmentWindowToken, tfAppBounds);
-        assertApplyTransactionDisallowed(mTransaction);
-
-        mTransaction.setAppBounds(mFragmentWindowToken, taskAppBounds);
-        assertApplyTransactionAllowed(mTransaction);
-
-        // setScreenSizeDp
-        mTransaction.setScreenSizeDp(mFragmentWindowToken, taskScreenWidthDp + 1,
-                taskScreenHeightDp + 1);
-        assertApplyTransactionDisallowed(mTransaction);
-
-        mTransaction.setScreenSizeDp(mFragmentWindowToken, taskScreenWidthDp, taskScreenHeightDp);
-        assertApplyTransactionAllowed(mTransaction);
-
-        // setSmallestScreenWidthDp
-        mTransaction.setSmallestScreenWidthDp(mFragmentWindowToken, taskSmallestScreenWidthDp + 1);
-        assertApplyTransactionDisallowed(mTransaction);
-
-        mTransaction.setSmallestScreenWidthDp(mFragmentWindowToken, taskSmallestScreenWidthDp);
-        assertApplyTransactionAllowed(mTransaction);
-
-        // Any of the change mask is not allowed.
-        mTransaction.setFocusable(mFragmentWindowToken, false);
-        assertApplyTransactionDisallowed(mTransaction);
+        assertEquals(tfBounds, mTaskFragment.getRelativeEmbeddedBounds());
+        assertEquals(taskBounds, mTaskFragment.getBounds());
     }
 
     // TODO(b/232871351): add test for minimum dimension violation in startActivityInTaskFragment
@@ -1454,7 +1422,7 @@
     }
 
     @Test
-    public void testMinDimensionViolation_SetBounds() {
+    public void testMinDimensionViolation_setRelativeBounds() {
         final Task task = createTask(mDisplayContent);
         mTaskFragment = new TaskFragmentBuilder(mAtm)
                 .setParentTask(task)
@@ -1472,12 +1440,14 @@
 
         // Shrink the TaskFragment to mTaskFragBounds to make its bounds smaller than activity's
         // minimum dimensions.
-        mTransaction.setBounds(mTaskFragment.mRemoteToken.toWindowContainerToken(), mTaskFragBounds)
+        mTransaction.setRelativeBounds(mTaskFragment.mRemoteToken.toWindowContainerToken(),
+                        mTaskFragBounds)
                 .setErrorCallbackToken(mErrorToken);
         assertApplyTransactionAllowed(mTransaction);
 
-        assertWithMessage("setBounds must not be performed.")
-                .that(mTaskFragment.getBounds()).isEqualTo(task.getBounds());
+        // When the requested bounds do not satisfy the min dimension, it will be reset to empty.
+        assertWithMessage("setRelativeBounds must not be performed.")
+                .that(mTaskFragment.getRelativeEmbeddedBounds()).isEqualTo(new Rect());
     }
 
     @Test