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

Commit a8fde159 authored by Evan Rosky's avatar Evan Rosky
Browse files

Add hierarchy operations to container transaction

Also add a way to query for child containers of a container.

Combined, these provide enough flexibility to re-arrange
containers to system-ui. For now, it is locked-down a bit so
it only provides children of TaskTiles and can only reparent
ActivityStacks. Eventually, this can be expanded for nested
tasks or display areas.

Bug: 133381284
Test: Added TaskTileTest#testHierarchyTransaction
Change-Id: Id0e89a4e4b9352d9392349c4e1fe7d69b4f9f212
parent bf395db0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -51,6 +51,9 @@ interface ITaskOrganizerController {
    /** Deletes a persistent root task in WM */
    boolean deleteRootTask(IWindowContainer task);

    /** Gets direct child tasks (ordered from top-to-bottom) */
    List<ActivityManager.RunningTaskInfo> getChildTasks(in IWindowContainer parent);

    /** Get the root task which contains the current ime target */
    IWindowContainer getImeTarget(int display);

+122 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -25,6 +27,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
@@ -36,10 +40,14 @@ import java.util.Map;
public class WindowContainerTransaction implements Parcelable {
    private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();

    // Flat list because re-order operations are order-dependent
    private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();

    public WindowContainerTransaction() {}

    protected WindowContainerTransaction(Parcel in) {
        in.readMap(mChanges, null /* loader */);
        in.readList(mHierarchyOps, null /* loader */);
    }

    private Change getOrCreateChange(IBinder token) {
@@ -97,10 +105,39 @@ public class WindowContainerTransaction implements Parcelable {
        return this;
    }

    /**
     * Reparents a container into another one. The effect of a {@code null} parent can vary. For
     * example, reparenting a stack to {@code null} will reparent it to its display.
     *
     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
     *              the bottom.
     */
    public WindowContainerTransaction reparent(@NonNull IWindowContainer child,
            @Nullable IWindowContainer parent, boolean onTop) {
        mHierarchyOps.add(new HierarchyOp(child.asBinder(),
                parent == null ? null : parent.asBinder(), onTop));
        return this;
    }

    /**
     * Reorders a container within its parent.
     *
     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
     *              the bottom.
     */
    public WindowContainerTransaction reorder(@NonNull IWindowContainer child, boolean onTop) {
        mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop));
        return this;
    }

    public Map<IBinder, Change> getChanges() {
        return mChanges;
    }

    public List<HierarchyOp> getHierarchyOps() {
        return mHierarchyOps;
    }

    @Override
    public String toString() {
        return "WindowContainerTransaction { changes = " + mChanges + " }";
@@ -109,6 +146,7 @@ public class WindowContainerTransaction implements Parcelable {
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeMap(mChanges);
        dest.writeList(mHierarchyOps);
    }

    @Override
@@ -249,4 +287,88 @@ public class WindowContainerTransaction implements Parcelable {
            }
        };
    }

    /**
     * Holds information about a reparent/reorder operation in the hierarchy. This is separate from
     * Changes because they must be executed in the same order that they are added.
     */
    public static class HierarchyOp implements Parcelable {
        private final IBinder mContainer;

        // If this is same as mContainer, then only change position, don't reparent.
        private final IBinder mReparent;

        // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
        private final boolean mToTop;

        public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
            mContainer = container;
            mReparent = reparent;
            mToTop = toTop;
        }

        public HierarchyOp(@NonNull IBinder container, boolean toTop) {
            mContainer = container;
            mReparent = container;
            mToTop = toTop;
        }

        protected HierarchyOp(Parcel in) {
            mContainer = in.readStrongBinder();
            mReparent = in.readStrongBinder();
            mToTop = in.readBoolean();
        }

        public boolean isReparent() {
            return mContainer != mReparent;
        }

        @Nullable
        public IBinder getNewParent() {
            return mReparent;
        }

        @NonNull
        public IBinder getContainer() {
            return mContainer;
        }

        public boolean getToTop() {
            return mToTop;
        }

        @Override
        public String toString() {
            if (isReparent()) {
                return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
                        + mReparent + "}";
            } else {
                return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
            }
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeStrongBinder(mContainer);
            dest.writeStrongBinder(mReparent);
            dest.writeBoolean(mToTop);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() {
            @Override
            public HierarchyOp createFromParcel(Parcel in) {
                return new HierarchyOp(in);
            }

            @Override
            public HierarchyOp[] newArray(int size) {
                return new HierarchyOp[size];
            }
        };
    }
}
+5 −7
Original line number Diff line number Diff line
@@ -79,11 +79,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -125,7 +120,6 @@ import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
import android.view.RemoteAnimationTarget;
@@ -3195,12 +3189,16 @@ class Task extends WindowContainer<WindowContainer> {
        info.lastActiveTime = lastActiveTime;
        info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
        info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
        info.resizeMode = mResizeMode;
        info.configuration.setTo(getConfiguration());
        info.token = mRemoteToken;
        // Get's the first non-undefined activity type among this and children. Can't use
        // configuration.windowConfiguration because that would only be this level.
        info.topActivityType = getActivityType();

        //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
        //                    order changes.
        final Task top = getTopMostTask();
        info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
    }

    /**
+98 −2
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;

import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;

import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
@@ -47,6 +49,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

@@ -375,6 +378,45 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
        }
    }

    @Override
    public List<RunningTaskInfo> getChildTasks(IWindowContainer parent) {
        enforceStackPermission("getChildTasks()");
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                if (parent == null) {
                    throw new IllegalArgumentException("Can't get children of null parent");
                }
                final WindowContainer container = WindowContainer.fromBinder(parent.asBinder());
                if (container == null) {
                    Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
                    return null;
                }
                // For now, only support returning children of persistent root tasks (of which the
                // only current implementation is TaskTile).
                if (!(container instanceof TaskTile)) {
                    Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
                    return null;
                }
                ArrayList<RunningTaskInfo> out = new ArrayList<>();
                // Tiles aren't real parents, so we need to go through stacks on the display to
                // ensure correct ordering.
                final DisplayContent dc = container.getDisplayContent();
                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
                    final ActivityStack as = dc.getStackAt(i);
                    if (as.getTile() == container) {
                        final RunningTaskInfo info = new RunningTaskInfo();
                        as.fillTaskInfo(info);
                        out.add(info);
                    }
                }
                return out;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private int sanitizeAndApplyChange(WindowContainer container,
            WindowContainerTransaction.Change change) {
        if (!(container instanceof Task)) {
@@ -405,6 +447,54 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
        return effects;
    }

    private int sanitizeAndApplyHierarchyOp(WindowContainer container,
            WindowContainerTransaction.HierarchyOp hop) {
        if (!(container instanceof Task)) {
            throw new IllegalArgumentException("Invalid container in hierarchy op");
        }
        if (hop.isReparent()) {
            // special case for tiles since they are "virtual" parents
            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
                ActivityStack as = (ActivityStack) container;
                TaskTile newParent = hop.getNewParent() == null ? null
                        : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
                if (as.getTile() != newParent) {
                    if (as.getTile() != null) {
                        as.getTile().removeChild(as);
                    }
                    if (newParent != null) {
                        if (!as.affectedBySplitScreenResize()) {
                            return 0;
                        }
                        newParent.addChild(as, POSITION_TOP);
                    }
                }
                if (hop.getToTop()) {
                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
                } else {
                    as.getDisplay().positionStackAtBottom(as);
                }
            } else if (container instanceof Task) {
                throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
            }
        } else {
            // Ugh, of course ActivityStack has its own special reorder logic...
            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
                ActivityStack as = (ActivityStack) container;
                if (hop.getToTop()) {
                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
                } else {
                    as.getDisplay().positionStackAtBottom(as);
                }
            } else {
                container.getParent().positionChildAt(
                        hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
                        container, false /* includingParents */);
            }
        }
        return TRANSACT_EFFECTS_LIFECYCLE;
    }

    private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
            int windowMask, Configuration config) {
        if ((container instanceof ActivityStack)
@@ -470,8 +560,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
                    while (entries.hasNext()) {
                        final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
                                entries.next();
                        final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
                                entry.getKey()).getContainer();
                        final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
                        int containerEffect = applyWindowContainerChange(wc, entry.getValue());
                        effects |= containerEffect;

@@ -484,6 +573,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
                            mBLASTSyncEngine.addToSyncSet(syncId, wc);
                        }
                    }
                    // Hierarchy changes
                    final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
                    for (int i = 0, n = hops.size(); i < n; ++i) {
                        final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
                        final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
                        effects |= sanitizeAndApplyHierarchyOp(wc, hop);
                    }
                    if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
                        // Already calls ensureActivityConfig
                        mService.mRootWindowContainer.ensureActivitiesVisible(
+4 −0
Original line number Diff line number Diff line
@@ -2296,6 +2296,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
        return mRemoteToken;
    }

    static WindowContainer fromBinder(IBinder binder) {
        return RemoteToken.fromBinder(binder).getContainer();
    }

    static class RemoteToken extends IWindowContainer.Stub {
        final WeakReference<WindowContainer> mWeakRef;

Loading