Merge "Cleanup TaskFragment WindowContainerTransaction Change"

This commit is contained in:
Chris Li
2023-02-10 01:01:07 +00:00
committed by Android (Google) Code Review
6 changed files with 76 additions and 161 deletions

View File

@@ -3829,6 +3829,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);

View File

@@ -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
*/

View File

@@ -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 {}

View File

@@ -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(

View File

@@ -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<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);
}
@@ -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);
}

View File

@@ -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