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

Commit 416269ee authored by Chris Li's avatar Chris Li
Browse files

Introduce DisplayAreaGroup

(7/n DisplayArea Sibling Roots)

Allow OEM to create DisplayAreaGroup when building DisplayAreaPolicy.

Bug: 157683117
Fix: 159144430
Test: atest WmTests:WindowTokenTests
Test: atest WmTests:DisplayAreaPolicyBuilderTest
Change-Id: I3cf5c10398f4ea4dec224633e17483fe7d063cf6
parent 8025c78d
Loading
Loading
Loading
Loading
+149 −24
Original line number Diff line number Diff line
@@ -23,20 +23,24 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;

import android.annotation.Nullable;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.policy.WindowManagerPolicy;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;

/**
 * A builder for instantiating a complex {@link DisplayAreaPolicy}
@@ -70,22 +74,93 @@ import java.util.Objects;
 */
class DisplayAreaPolicyBuilder {
    @Nullable private HierarchyBuilder mRootHierarchyBuilder;
    private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>();

    /** Defines the root hierarchy. */
    /**
     * When a window is created, the policy will use this function to select the
     * {@link RootDisplayArea} to place that window in. The selected root can be either the one of
     * the {@link #mRootHierarchyBuilder} or the one of any of the
     * {@link #mDisplayAreaGroupHierarchyBuilders}.
     **/
    @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;

    /** Defines the root hierarchy for the whole logical display. */
    DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) {
        // TODO(b/157683117): Add method to add sub root and root choosing func.
        mRootHierarchyBuilder = rootHierarchyBuilder;
        return this;
    }

    /**
     * Defines a DisplayAreaGroup hierarchy. Its root will be added as a child of the root
     * hierarchy.
     */
    DisplayAreaPolicyBuilder addDisplayAreaGroupHierarchy(
            HierarchyBuilder displayAreaGroupHierarchy) {
        mDisplayAreaGroupHierarchyBuilders.add(displayAreaGroupHierarchy);
        return this;
    }

    /** The policy will use this function to find the root to place windows in. */
    DisplayAreaPolicyBuilder setSelectRootForWindowFunc(
            BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) {
        mSelectRootForWindowFunc = selectRootForWindowFunc;
        return this;
    }

    /** Makes sure the setting meets the requirement. */
    private void validate() {
        if (mRootHierarchyBuilder == null) {
            throw new IllegalStateException("Root must be set for the display area policy.");
        }

        boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null;
        boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder);
        for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
            HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
            if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) {
                throw new IllegalStateException(
                        "DisplayAreaGroup must contain at least one TaskDisplayArea.");
            }

            containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null;
            containsDefaultTda = containsDefaultTda
                    || containsDefaultTaskDisplayArea(hierarchyBuilder);
        }

        if (!containsImeContainer) {
            throw new IllegalStateException("IME container must be set.");
        }

        if (!containsDefaultTda) {
            throw new IllegalStateException("There must be a default TaskDisplayArea.");
        }
    }

    /** Checks if the given hierarchy contains the default {@link TaskDisplayArea}. */
    private static boolean containsDefaultTaskDisplayArea(HierarchyBuilder displayAreaHierarchy) {
        for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) {
            if (displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId
                    == FEATURE_DEFAULT_TASK_CONTAINER) {
                return true;
            }
        }
        return false;
    }

    Result build(WindowManagerService wmService) {
        Objects.requireNonNull(mRootHierarchyBuilder,
                "Root must be set for the display area policy.");
        Objects.requireNonNull(mRootHierarchyBuilder.mImeContainer, "Ime must not be null");
        Preconditions.checkCollectionNotEmpty(mRootHierarchyBuilder.mTaskDisplayAreas,
                "TaskDisplayAreas must not be empty");
        mRootHierarchyBuilder.build();
        return new Result(wmService, mRootHierarchyBuilder.mRoot);
        validate();

        // Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
        mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
        List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
                mDisplayAreaGroupHierarchyBuilders.size());
        for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
            HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
            hierarchyBuilder.build();
            displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
        }
        return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
                mSelectRootForWindowFunc);
    }

    /**
@@ -131,6 +206,14 @@ class DisplayAreaPolicyBuilder {

        /** Builds the {@link DisplayArea} hierarchy below root. */
        private void build() {
            build(null /* displayAreaGroupHierarchyBuilders */);
        }

        /**
         * Builds the {@link DisplayArea} hierarchy below root. And adds the roots of those
         * {@link HierarchyBuilder} as children.
         */
        private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
            final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
            final int maxWindowLayerCount = policy.getMaxWindowLayer();
            final DisplayArea.Tokens[] displayAreaForLayer =
@@ -215,7 +298,9 @@ class DisplayAreaPolicyBuilder {
                    if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
                        // We use the passed in TaskDisplayAreas for task container type of layer.
                        // Skip creating Tokens even if there is no TDA.
                        addTaskDisplayAreasToLayer(areaForLayer[layer]);
                        addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
                        addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
                                displayAreaGroupHierarchyBuilders);
                        leafArea.mSkipTokens = true;
                    } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
                        // We use the passed in ImeContainer for ime container type of layer.
@@ -234,11 +319,11 @@ class DisplayAreaPolicyBuilder {

            // Notify the root that we have finished attaching all the DisplayAreas. Cache all the
            // feature related collections there for fast access.
            mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas, mTaskDisplayAreas);
            mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
        }

        /** Adds all {@link TaskDisplayArea} to the specified layer */
        private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea) {
        /** Adds all {@link TaskDisplayArea} to the application layer. */
        private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
            final int count = mTaskDisplayAreas.size();
            for (int i = 0; i < count; i++) {
                PendingArea leafArea =
@@ -249,6 +334,24 @@ class DisplayAreaPolicyBuilder {
            }
        }

        /** Adds roots of the DisplayAreaGroups to the application layer. */
        private void addDisplayAreaGroupsToApplicationLayer(
                DisplayAreaPolicyBuilder.PendingArea parentPendingArea,
                @Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
            if (displayAreaGroupHierarchyBuilders == null) {
                return;
            }
            final int count = displayAreaGroupHierarchyBuilders.size();
            for (int i = 0; i < count; i++) {
                DisplayAreaPolicyBuilder.PendingArea
                        leafArea = new DisplayAreaPolicyBuilder.PendingArea(
                        null /* feature */, APPLICATION_LAYER, parentPendingArea);
                leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot;
                leafArea.mMaxLayer = APPLICATION_LAYER;
                parentPendingArea.mChildren.add(leafArea);
            }
        }

        private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
            if (layer == APPLICATION_LAYER) {
                return LEAF_TYPE_TASK_CONTAINERS;
@@ -401,9 +504,20 @@ class DisplayAreaPolicyBuilder {
    }

    static class Result extends DisplayAreaPolicy {
        final List<RootDisplayArea> mDisplayAreaGroupRoots;
        final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;

        Result(WindowManagerService wmService, RootDisplayArea root) {
        Result(WindowManagerService wmService, RootDisplayArea root,
                List<RootDisplayArea> displayAreaGroupRoots,
                @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea>
                        selectRootForWindowFunc) {
            super(wmService, root);
            mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
            mSelectRootForWindowFunc = selectRootForWindowFunc == null
                    // Always return the highest level root of the logical display when the func is
                    // not specified.
                    ? (window, options) -> mRoot
                    : selectRootForWindowFunc;
        }

        @Override
@@ -414,27 +528,38 @@ class DisplayAreaPolicyBuilder {

        @VisibleForTesting
        DisplayArea.Tokens findAreaForToken(WindowToken token) {
            // TODO(b/157683117): Choose root/sub root from OEM provided func.
            return mRoot.findAreaForToken(token);
            return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token);
        }

        @VisibleForTesting
        List<Feature> getFeatures() {
            // TODO(b/157683117): Also get feature from sub root.
            return new ArrayList<>(mRoot.mFeatures);
            Set<Feature> features = new ArraySet<>();
            features.addAll(mRoot.mFeatures);
            for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) {
                features.addAll(mDisplayAreaGroupRoots.get(i).mFeatures);
            }
            return new ArrayList<>(features);
        }

        @Override
        public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId) {
            // TODO(b/157683117): Also get display areas from sub root.
            List<Feature> features = getFeatures();
            List<DisplayArea<? extends WindowContainer>> displayAreas = new ArrayList<>();
            getDisplayAreas(mRoot, featureId, displayAreas);
            for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) {
                getDisplayAreas(mDisplayAreaGroupRoots.get(i), featureId, displayAreas);
            }
            return displayAreas;
        }

        private static void getDisplayAreas(RootDisplayArea root, int featureId,
                List<DisplayArea<? extends WindowContainer>> displayAreas) {
            List<Feature> features = root.mFeatures;
            for (int i = 0; i < features.size(); i++) {
                Feature feature = features.get(i);
                if (feature.mId == featureId) {
                    return new ArrayList<>(mRoot.mFeatureToDisplayAreas.get(feature));
                    displayAreas.addAll(root.mFeatureToDisplayAreas.get(feature));
                }
            }
            return new ArrayList<>();
        }
    }

+2 −1
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;

import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
@@ -910,7 +911,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     * @param root {@link RootWindowContainer}
     */
    DisplayContent(Display display, RootWindowContainer root) {
        super(root.mWindowManager);
        super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
        if (mWmService.mRoot.getDisplayContent(display.getDisplayId()) != null) {
            throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                    + " already exists="
+3 −14
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.wm;

import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;

import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;

@@ -44,19 +43,11 @@ class RootDisplayArea extends DisplayArea<DisplayArea> {
    /** Mapping from window layer to {@link DisplayArea.Tokens} that holds windows on that layer. */
    private DisplayArea.Tokens[] mAreaForLayer;

    /**
     * List of {@link TaskDisplayArea} that are attached to this {@link DisplayArea} hierarchy. The
     * order is the same as their z-order.
     *
     * TODO(b/157683117): Instead of caching the TDAs, always traverse the hierarchy to get them.
     */
    ArrayList<TaskDisplayArea> mTaskDisplayAreas;

    /** Whether the hierarchy has been built. */
    private boolean mHasBuiltHierarchy;

    RootDisplayArea(WindowManagerService wms) {
        super(wms, Type.ANY, "RootDisplayArea", FEATURE_ROOT);
    RootDisplayArea(WindowManagerService wms, String name, int featureId) {
        super(wms, Type.ANY, name, featureId);
    }

    /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */
@@ -73,15 +64,13 @@ class RootDisplayArea extends DisplayArea<DisplayArea> {

    /** Callback after {@link DisplayArea} hierarchy has been built. */
    void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer,
            Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas,
            ArrayList<TaskDisplayArea> taskDisplayAreas) {
            Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas) {
        if (mHasBuiltHierarchy) {
            throw new IllegalStateException("Root should only build the hierarchy once");
        }
        mHasBuiltHierarchy = true;
        mFeatures = Collections.unmodifiableList(features);
        mAreaForLayer = areaForLayer;
        mTaskDisplayAreas = taskDisplayAreas;
        mFeatureToDisplayAreas = featureToDisplayAreas;
    }
}
+9 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.wm;

import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;

import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -24,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIG
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.annotation.Nullable;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -44,7 +46,13 @@ class WallpaperWindowToken extends WindowToken {

    WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
            DisplayContent dc, boolean ownerCanManageAppTokens) {
        super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens);
        this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */);
    }

    WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
            DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) {
        super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID,
                false /* roundedCornerOverlay */, false /* fromClientToken */, options);
        dc.mWallpaperController.addWallpaperToken(this);
        setWindowingMode(WINDOWING_MODE_FULLSCREEN);
    }
+3 −2
Original line number Diff line number Diff line
@@ -2670,10 +2670,11 @@ public class WindowManagerService extends IWindowManager.Stub
                }
                // TODO(window-container): Clean up dead tokens
                if (type == TYPE_WALLPAPER) {
                    new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
                    new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens,
                            options);
                } else {
                    new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
                            callingUid, false /* roundedCornerOverlay */, fromClientToken);
                            callingUid, false /* roundedCornerOverlay */, fromClientToken, options);
                }
            }
        } finally {
Loading