diff options
3 files changed, 188 insertions, 41 deletions
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index a88a17283482..c8001198bdf6 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -486,7 +486,7 @@ public final class WindowContainerTransaction implements Parcelable { } /** - * Sets to containers adjacent to each other. Containers below two visible adjacent roots will + * Sets two containers adjacent to each other. Containers below two visible adjacent roots will * be made invisible. This currently only applies to TaskFragment containers created by * organizer. * @param root1 the first root. @@ -495,9 +495,64 @@ public final class WindowContainerTransaction implements Parcelable { @NonNull public WindowContainerTransaction setAdjacentRoots( @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) { - mHierarchyOps.add(HierarchyOp.createForAdjacentRoots( - root1.asBinder(), - root2.asBinder())); + if (!Flags.allowMultipleAdjacentTaskFragments()) { + mHierarchyOps.add(HierarchyOp.createForAdjacentRoots( + root1.asBinder(), + root2.asBinder())); + return this; + } + return setAdjacentRootSet(root1, root2); + } + + /** + * Sets multiple containers adjacent to each other. Containers below the visible adjacent roots + * will be made invisible. This currently only applies to Task containers created by organizer. + * + * To remove one container from the adjacent roots, one can call {@link #clearAdjacentRoots} + * with the target container. + * To remove all containers from the adjacent roots, one much call {@link #clearAdjacentRoots} + * on each container if there were more than two containers in the set. + * + * For non-Task TaskFragment, use {@link #setAdjacentTaskFragments} instead. + * + * @param roots the Tasks that should be adjacent to each other. + * @throws IllegalArgumentException if roots have size < 2. + * @hide // TODO(b/373709676) Rename to setAdjacentRoots and update CTS. + */ + @NonNull + public WindowContainerTransaction setAdjacentRootSet( + @NonNull WindowContainerToken... roots) { + if (!Flags.allowMultipleAdjacentTaskFragments()) { + throw new IllegalArgumentException("allowMultipleAdjacentTaskFragments is not enabled." + + " Use #setAdjacentRoots instead."); + } + if (roots.length < 2) { + throw new IllegalArgumentException("setAdjacentRootSet must have size >= 2"); + } + final IBinder[] rootTokens = new IBinder[roots.length]; + for (int i = 0; i < roots.length; i++) { + rootTokens[i] = roots[i].asBinder(); + } + mHierarchyOps.add( + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS) + .setContainers(rootTokens) + .build()); + return this; + } + + /** + * Clears container adjacent. + * If {@link #setAdjacentRootSet} is called with more than 2 roots, calling this will only + * remove the given root from the adjacent set. The rest of roots will stay adjacent to each + * other. + * + * @param root the root container to clear the adjacent roots for. + * @hide + */ + @NonNull + public WindowContainerTransaction clearAdjacentRoots( + @NonNull WindowContainerToken root) { + mHierarchyOps.add(HierarchyOp.createForClearAdjacentRoots(root.asBinder())); return this; } @@ -967,18 +1022,6 @@ public final class WindowContainerTransaction implements Parcelable { } /** - * Clears container adjacent. - * @param root the root container to clear the adjacent roots for. - * @hide - */ - @NonNull - public WindowContainerTransaction clearAdjacentRoots( - @NonNull WindowContainerToken root) { - mHierarchyOps.add(HierarchyOp.createForClearAdjacentRoots(root.asBinder())); - return this; - } - - /** * Sets/removes the reparent leaf task flag for this {@code windowContainer}. * When this is set, the server side will try to reparent the leaf task to task display area * if there is an existing activity in history during the activity launch. This operation only @@ -1520,6 +1563,9 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private IBinder mContainer; + @Nullable + private IBinder[] mContainers; + // If this is same as mContainer, then only change position, don't reparent. @Nullable private IBinder mReparent; @@ -1704,6 +1750,7 @@ public final class WindowContainerTransaction implements Parcelable { public HierarchyOp(@NonNull HierarchyOp copy) { mType = copy.mType; mContainer = copy.mContainer; + mContainers = copy.mContainers; mBounds = copy.mBounds; mIncludingParents = copy.mIncludingParents; mReparent = copy.mReparent; @@ -1729,6 +1776,7 @@ public final class WindowContainerTransaction implements Parcelable { protected HierarchyOp(Parcel in) { mType = in.readInt(); mContainer = in.readStrongBinder(); + mContainers = in.createBinderArray(); mBounds = in.readTypedObject(Rect.CREATOR); mIncludingParents = in.readBoolean(); mReparent = in.readStrongBinder(); @@ -1780,6 +1828,13 @@ public final class WindowContainerTransaction implements Parcelable { } @NonNull + public IBinder[] getContainers() { + return mContainers; + } + + /** @deprecated b/373709676 replace with {@link #getContainers()}. */ + @Deprecated + @NonNull public IBinder getAdjacentRoot() { return mReparent; } @@ -1869,7 +1924,7 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_REORDER: return "reorder"; case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: return "childrenTasksReparent"; case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: return "setLaunchRoot"; - case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "setAdjacentRoot"; + case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "setAdjacentRoots"; case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "launchTask"; case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "setAdjacentFlagRoot"; case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: @@ -1883,7 +1938,7 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: return "setAlwaysOnTop"; case HIERARCHY_OP_TYPE_REMOVE_TASK: return "removeTask"; case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: return "finishActivity"; - case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "clearAdjacentRoot"; + case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "clearAdjacentRoots"; case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH: return "setReparentLeafTaskIfRelaunch"; case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: @@ -1923,8 +1978,18 @@ public final class WindowContainerTransaction implements Parcelable { sb.append(mContainer).append(" to ").append(mToTop ? "top" : "bottom"); break; case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: - sb.append("container=").append(mContainer) - .append(" adjacentRoot=").append(mReparent); + if (Flags.allowMultipleAdjacentTaskFragments()) { + for (IBinder container : mContainers) { + if (container == mContainers[0]) { + sb.append("adjacentRoots=").append(container); + } else { + sb.append(", ").append(container); + } + } + } else { + sb.append("container=").append(mContainer) + .append(" adjacentRoot=").append(mReparent); + } break; case HIERARCHY_OP_TYPE_LAUNCH_TASK: sb.append(mLaunchOptions); @@ -1997,6 +2062,7 @@ public final class WindowContainerTransaction implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); dest.writeStrongBinder(mContainer); + dest.writeBinderArray(mContainers); dest.writeTypedObject(mBounds, flags); dest.writeBoolean(mIncludingParents); dest.writeStrongBinder(mReparent); @@ -2044,6 +2110,9 @@ public final class WindowContainerTransaction implements Parcelable { private IBinder mContainer; @Nullable + private IBinder[] mContainers; + + @Nullable private IBinder mReparent; @Nullable @@ -2104,6 +2173,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setContainers(@Nullable IBinder[] containers) { + mContainers = containers; + return this; + } + Builder setReparentContainer(@Nullable IBinder reparentContainer) { mReparent = reparentContainer; return this; @@ -2209,6 +2283,7 @@ public final class WindowContainerTransaction implements Parcelable { HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; + hierarchyOp.mContainers = mContainers; hierarchyOp.mReparent = mReparent; hierarchyOp.mWindowingModes = mWindowingModes != null ? Arrays.copyOf(mWindowingModes, mWindowingModes.length) diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 3381ae08c6b0..5fb0787fa0dc 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -2201,31 +2201,60 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { - final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer()); - if (wc1 == null || !wc1.isAttached()) { - Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1); - return TRANSACT_EFFECTS_NONE; - } - final TaskFragment root1 = wc1.asTaskFragment(); - final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot()); - if (wc2 == null || !wc2.isAttached()) { - Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2); - return TRANSACT_EFFECTS_NONE; + if (!Flags.allowMultipleAdjacentTaskFragments()) { + final WindowContainer wc1 = WindowContainer.fromBinder(hop.getContainer()); + if (wc1 == null || !wc1.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc1); + return TRANSACT_EFFECTS_NONE; + } + final TaskFragment root1 = wc1.asTaskFragment(); + final WindowContainer wc2 = WindowContainer.fromBinder(hop.getAdjacentRoot()); + if (wc2 == null || !wc2.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc2); + return TRANSACT_EFFECTS_NONE; + } + final TaskFragment root2 = wc2.asTaskFragment(); + if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) { + throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + + " organizer root1=" + root1 + " root2=" + root2); + } + if (root1.isAdjacentTo(root2)) { + return TRANSACT_EFFECTS_NONE; + } + root1.setAdjacentTaskFragment(root2); + return TRANSACT_EFFECTS_LIFECYCLE; } - final TaskFragment root2 = wc2.asTaskFragment(); - if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) { - throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" - + " organizer root1=" + root1 + " root2=" + root2); + + final IBinder[] containers = hop.getContainers(); + final ArraySet<TaskFragment> adjacentRoots = new ArraySet<>(); + for (IBinder container : containers) { + final WindowContainer wc = WindowContainer.fromBinder(container); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc); + return TRANSACT_EFFECTS_NONE; + } + final Task root = wc.asTask(); + if (root == null) { + // Only support Task. Use WCT#setAdjacentTaskFragments for non-Task TaskFragment. + throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not called with" + + " Task. wc=" + wc); + } + if (!root.mCreatedByOrganizer) { + throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + + " organizer root=" + root); + } + if (adjacentRoots.contains(root)) { + throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: called with same" + + " root twice=" + root); + } + adjacentRoots.add(root); } - if (root1.isAdjacentTo(root2)) { + final TaskFragment root0 = adjacentRoots.valueAt(0); + final TaskFragment.AdjacentSet adjacentSet = new TaskFragment.AdjacentSet(adjacentRoots); + if (adjacentSet.equals(root0.getAdjacentTaskFragments())) { return TRANSACT_EFFECTS_NONE; } - if (Flags.allowMultipleAdjacentTaskFragments()) { - // TODO(b/373709676): allow three roots. - root1.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(root1, root2)); - } else { - root1.setAdjacentTaskFragment(root2); - } + root0.setAdjacentTaskFragments(adjacentSet); return TRANSACT_EFFECTS_LIFECYCLE; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 410fa2879600..e1efed07fb97 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -924,6 +924,49 @@ public class WindowOrganizerTests extends WindowTestsBase { assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null); } + @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS) + @Test + public void testSetAdjacentLaunchRootSet() { + final DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY); + + final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( + dc, WINDOWING_MODE_MULTI_WINDOW, null); + final RunningTaskInfo info1 = task1.getTaskInfo(); + final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( + dc, WINDOWING_MODE_MULTI_WINDOW, null); + final RunningTaskInfo info2 = task2.getTaskInfo(); + final Task task3 = mWm.mAtmService.mTaskOrganizerController.createRootTask( + dc, WINDOWING_MODE_MULTI_WINDOW, null); + final RunningTaskInfo info3 = task3.getTaskInfo(); + + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setAdjacentRootSet(info1.token, info2.token, info3.token); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); + assertTrue(task1.hasAdjacentTaskFragment()); + assertTrue(task2.hasAdjacentTaskFragment()); + assertTrue(task3.hasAdjacentTaskFragment()); + assertTrue(task1.isAdjacentTo(task2)); + assertTrue(task1.isAdjacentTo(task3)); + assertTrue(task2.isAdjacentTo(task3)); + + wct = new WindowContainerTransaction(); + wct.clearAdjacentRoots(info1.token); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); + assertFalse(task1.hasAdjacentTaskFragment()); + assertTrue(task2.hasAdjacentTaskFragment()); + assertTrue(task3.hasAdjacentTaskFragment()); + assertFalse(task1.isAdjacentTo(task2)); + assertFalse(task1.isAdjacentTo(task3)); + assertTrue(task2.isAdjacentTo(task3)); + + wct = new WindowContainerTransaction(); + wct.clearAdjacentRoots(info2.token); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); + assertFalse(task2.hasAdjacentTaskFragment()); + assertFalse(task3.hasAdjacentTaskFragment()); + assertFalse(task2.isAdjacentTo(task3)); + } + @Test public void testTileAddRemoveChild() { final StubOrganizer listener = new StubOrganizer(); |