Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f4840a01 authored by Chris Li's avatar Chris Li
Browse files

Allow multiple adjacent TFs (2/n)

Add WCT#setAdjacentRootSet to take multiple adjacent TFs.

Bug: 373709676
Test: atest WmTests:WindowOrganizerTests
Flag: com.android.window.flags.allow_multiple_adjacent_task_fragments
Change-Id: I5bee7272b6e14aa9bbf49ffa5c8c1deca652d50a
parent b6f85095
Loading
Loading
Loading
Loading
+95 −20
Original line number Diff line number Diff line
@@ -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,11 +495,66 @@ public final class WindowContainerTransaction implements Parcelable {
    @NonNull
    public WindowContainerTransaction setAdjacentRoots(
            @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
        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;
    }

    /**
     * Sets the container as launch adjacent flag root. Task starting with
@@ -966,18 +1021,6 @@ public final class WindowContainerTransaction implements Parcelable {
        return this;
    }

    /**
     * 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
@@ -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();
@@ -1779,6 +1827,13 @@ public final class WindowContainerTransaction implements Parcelable {
            return mContainer;
        }

        @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:
                    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);
@@ -2043,6 +2109,9 @@ public final class WindowContainerTransaction implements Parcelable {
            @Nullable
            private IBinder mContainer;

            @Nullable
            private IBinder[] mContainers;

            @Nullable
            private IBinder mReparent;

@@ -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)
+50 −21
Original line number Diff line number Diff line
@@ -2201,6 +2201,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
    }

    private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
        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);
@@ -2220,12 +2221,40 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
            if (root1.isAdjacentTo(root2)) {
                return TRANSACT_EFFECTS_NONE;
            }
        if (Flags.allowMultipleAdjacentTaskFragments()) {
            // TODO(b/373709676): allow three roots.
            root1.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(root1, root2));
        } else {
            root1.setAdjacentTaskFragment(root2);
            return TRANSACT_EFFECTS_LIFECYCLE;
        }

        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);
        }
        final TaskFragment root0 = adjacentRoots.valueAt(0);
        final TaskFragment.AdjacentSet adjacentSet = new TaskFragment.AdjacentSet(adjacentRoots);
        if (adjacentSet.equals(root0.getAdjacentTaskFragments())) {
            return TRANSACT_EFFECTS_NONE;
        }
        root0.setAdjacentTaskFragments(adjacentSet);
        return TRANSACT_EFFECTS_LIFECYCLE;
    }

+43 −0
Original line number Diff line number Diff line
@@ -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();