diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 51e90d28abdc9..ec4970da781f6 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3776,6 +3776,7 @@ package android.window { 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 dc60eddaf7dbd..a881a05a1054f 100644 --- a/core/java/android/window/TaskFragmentInfo.java +++ b/core/java/android/window/TaskFragmentInfo.java @@ -23,7 +23,6 @@ import static java.util.Objects.requireNonNull; 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 @@ public final class TaskFragmentInfo implements Parcelable { 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 @@ public final class TaskFragmentInfo implements Parcelable { /** * 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 @@ public final class TaskFragmentInfo implements Parcelable { /** * 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 0d6a58b166ae8..413f0ccbc2755 100644 --- a/core/java/android/window/TaskFragmentOperation.java +++ b/core/java/android/window/TaskFragmentOperation.java @@ -38,8 +38,8 @@ import java.util.Objects; 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 @@ public final class TaskFragmentOperation implements Parcelable { /** 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 @@ public final class TaskFragmentOperation implements Parcelable { 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 a2fa16255a1f0..a3209f68acf97 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -308,7 +308,6 @@ public final class WindowContainerTransaction implements Parcelable { /** * 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 3faf9e005c5ea..83526ecadaf88 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_REQUEST_FOCUS_ON_TASK 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.IWindowOrganizerController; 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } /** - * 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub t.getChanges().entrySet().iterator(); while (entries.hasNext()) { final Map.Entry 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); } @@ -1763,27 +1758,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } - /** - * 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } /** - * 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 060f9d87db7f7..378e8bed35246 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 class TaskFragmentOrganizerControllerTest extends WindowTestsBase { 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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { 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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } /** - * 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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { 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 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test - public void testMinDimensionViolation_SetBounds() { + public void testMinDimensionViolation_setRelativeBounds() { final Task task = createTask(mDisplayContent); mTaskFragment = new TaskFragmentBuilder(mAtm) .setParentTask(task) @@ -1472,12 +1440,14 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // 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