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

Commit 8116358a authored by Adrian Roos's avatar Adrian Roos
Browse files

WM: Build policy with DisplayAreaPolicyBuilder (5/n)

Add a builder to simplify building complex policies.

Bug: 147406652
Test: atest WmTests DisplayAreaPolicyBuilderTest
Change-Id: I4ebc00a1f16b7915b7d926d9e0a43e7a79fc22c0
parent 29aa0e81
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -866,6 +866,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
        }
    }

    default int getMaxWindowLayer() {
        return 35;
    }

    /**
     * Return how to Z-order sub-windows in relation to the window they are attached to.
     * Return positive to have them ordered in front, negative for behind.
+5 −0
Original line number Diff line number Diff line
@@ -101,6 +101,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
        return mName;
    }

    @Override
    public String toString() {
        return mName + "@" + System.identityHashCode(this);
    }

    @Override
    public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
        final long token = proto.start(fieldId);
+11 −64
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.server.wm;

import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;

import android.content.res.Resources;
import android.text.TextUtils;

@@ -43,7 +41,7 @@ public abstract class DisplayAreaPolicy {
    /**
     * The Tasks container. Tasks etc. are automatically added to this container.
     */
    protected final TaskContainers mTaskContainers;
    protected final DisplayArea<? extends ActivityStack> mTaskContainers;

    /**
     * Construct a new {@link DisplayAreaPolicy}
@@ -58,7 +56,8 @@ public abstract class DisplayAreaPolicy {
     */
    protected DisplayAreaPolicy(WindowManagerService wmService,
            DisplayContent content, DisplayArea.Root root,
            DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
            DisplayArea<? extends WindowContainer> imeContainer,
            DisplayArea<? extends ActivityStack> taskContainers) {
        mWmService = wmService;
        mContent = content;
        mRoot = root;
@@ -83,67 +82,15 @@ public abstract class DisplayAreaPolicy {
     */
    public abstract void addWindow(WindowToken token);

    /**
     * Default policy that has no special features.
     */
    public static class Default extends DisplayAreaPolicy {

        public Default(WindowManagerService wmService, DisplayContent content,
                DisplayArea.Root root,
                DisplayArea<? extends WindowContainer> imeContainer,
                TaskContainers taskContainers) {
            super(wmService, content, root, imeContainer, taskContainers);
        }

        private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService,
                DisplayArea.Type.BELOW_TASKS, "BelowTasks");
        private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService,
                DisplayArea.Type.ABOVE_TASKS, "AboveTasks");
        private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService,
                DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme");
        private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService,
                DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme");

        @Override
        public void attachDisplayAreas() {
            mRoot.addChild(mBelow, 0);
            mRoot.addChild(mTaskContainers, 1);
            mRoot.addChild(mAbove, 2);

            mAbove.addChild(mAboveBelowIme, 0);
            mAbove.addChild(mImeContainer, 1);
            mAbove.addChild(mAboveAboveIme, 2);
        }

        @Override
        public void addWindow(WindowToken token) {
            switch (DisplayArea.Type.typeOf(token)) {
                case ABOVE_TASKS:
                    if (token.getWindowLayerFromType()
                            < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) {
                        mAboveBelowIme.addChild(token);
                    } else {
                        mAboveAboveIme.addChild(token);
                    }
                    break;
                case BELOW_TASKS:
                    mBelow.addChild(token);
                    break;
                default:
                    throw new IllegalArgumentException("don't know how to sort " + token);
            }
        }

        /** Provider for {@link DisplayAreaPolicy.Default platform-default display area policy}. */
        static class Provider implements DisplayAreaPolicy.Provider {
    /** Provider for platform-default display area policy. */
    static final class DefaultProvider implements DisplayAreaPolicy.Provider {
        @Override
        public DisplayAreaPolicy instantiate(WindowManagerService wmService,
                DisplayContent content, DisplayArea.Root root,
                DisplayArea<? extends WindowContainer> imeContainer,
                TaskContainers taskContainers) {
                return new DisplayAreaPolicy.Default(wmService, content, root, imeContainer,
                        taskContainers);
            }
            return new DisplayAreaPolicyBuilder()
                    .build(wmService, content, root, imeContainer, taskContainers);
        }
    }

@@ -172,7 +119,7 @@ public abstract class DisplayAreaPolicy {
            String name = res.getString(
                    com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
            if (TextUtils.isEmpty(name)) {
                return new DisplayAreaPolicy.Default.Provider();
                return new DisplayAreaPolicy.DefaultProvider();
            }
            try {
                return (Provider) Class.forName(name).newInstance();
+371 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
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 com.android.internal.annotations.VisibleForTesting;
import com.android.server.policy.WindowManagerPolicy;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A builder for instantiating a complex {@link DisplayAreaPolicy}
 *
 * <p>Given a set of features (that each target a set of window types), it builds the necessary
 * DisplayArea hierarchy.
 *
 * <p>Example: <br />
 *
 * <pre>
 *     // Feature for targeting everything below the magnification overlay:
 *     new DisplayAreaPolicyBuilder(...)
 *             .addFeature(new Feature.Builder(..., "Magnification")
 *                     .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
 *                     .build())
 *             .build(...)
 *
 *     // Builds a policy with the following hierarchy:
 *      - DisplayArea.Root
 *        - Magnification
 *          - DisplayArea.Tokens (Wallpapers are attached here)
 *          - TaskContainers
 *          - DisplayArea.Tokens (windows above Tasks up to IME are attached here)
 *          - ImeContainers
 *          - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here)
 *        - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up are attached here)
 *
 * </pre>
 *
 * // TODO(display-area): document more complex scenarios where we need multiple areas per feature.
 */
class DisplayAreaPolicyBuilder {

    private final ArrayList<Feature> mFeatures = new ArrayList<>();

    /**
     * A feature that requires {@link DisplayArea DisplayArea(s)}.
     */
    static class Feature {
        private final String mName;
        private final boolean[] mWindowLayers;

        private Feature(String name, boolean[] windowLayers) {
            mName = name;
            mWindowLayers = windowLayers;
        }

        static class Builder {
            private final WindowManagerPolicy mPolicy;
            private final String mName;
            private final boolean[] mLayers;

            /**
             * Build a new feature that applies to a set of window types as specified by the builder
             * methods.
             *
             * <p>The set of types is updated iteratively in the order of the method invocations.
             * For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
             * apply to all types except TYPE_STATUS_BAR.
             *
             * The builder starts out with the feature not applying to any types.
             *
             * @param name the name of the feature.
             */
            Builder(WindowManagerPolicy policy, String name) {
                mPolicy = policy;
                mName = name;
                mLayers = new boolean[mPolicy.getMaxWindowLayer()];
            }

            /**
             * Set that the feature applies to all window types.
             */
            Builder all() {
                Arrays.fill(mLayers, true);
                return this;
            }

            /**
             * Set that the feature applies to the given window types.
             */
            Builder and(int... types) {
                for (int i = 0; i < types.length; i++) {
                    int type = types[i];
                    set(type, true);
                }
                return this;
            }

            /**
             * Set that the feature does not apply to the given window types.
             */
            Builder except(int... types) {
                for (int i = 0; i < types.length; i++) {
                    int type = types[i];
                    set(type, false);
                }
                return this;
            }

            /**
             * Set that the feature applies window types that are layerd at or below the layer of
             * the given window type.
             */
            Builder upTo(int typeInclusive) {
                final int max = layerFromType(typeInclusive, false);
                for (int i = 0; i < max; i++) {
                    mLayers[i] = true;
                }
                set(typeInclusive, true);
                return this;
            }

            Feature build() {
                return new Feature(mName, mLayers.clone());
            }

            private void set(int type, boolean value) {
                mLayers[layerFromType(type, true)] = value;
                if (type == TYPE_APPLICATION_OVERLAY) {
                    mLayers[layerFromType(type, true)] = value;
                    mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value;
                    mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value;
                    mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value;
                }
            }

            private int layerFromType(int type, boolean internalWindows) {
                return mPolicy.getWindowLayerFromTypeLw(type, internalWindows);
            }
        }
    }

    static class Result extends DisplayAreaPolicy {
        private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
        private static final int LEAF_TYPE_IME_CONTAINERS = 2;
        private static final int LEAF_TYPE_TOKENS = 0;

        private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer();

        private final ArrayList<Feature> mFeatures;
        private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas;
        private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer];

        Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
                DisplayArea<? extends WindowContainer> imeContainer,
                DisplayArea<? extends ActivityStack> taskStacks, ArrayList<Feature> features) {
            super(wmService, content, root, imeContainer, taskStacks);
            mFeatures = features;
            mAreas = new HashMap<>(features.size());
            for (int i = 0; i < mFeatures.size(); i++) {
                mAreas.put(mFeatures.get(i), new ArrayList<>());
            }
        }

        @Override
        public void attachDisplayAreas() {
            // This method constructs the layer hierarchy with the following properties:
            // (1) Every feature maps to a set of DisplayAreas
            // (2) After adding a window, for every feature the window's type belongs to,
            //     it is a descendant of one of the corresponding DisplayAreas of the feature.
            // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
            //     within a DisplayArea:
            //      for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
            //      max(z-range(a)) <= min(z-range(b))
            //
            // The algorithm below iteratively creates such a hierarchy:
            //  - Initially, all windows are attached to the root.
            //  - For each feature we create a set of DisplayAreas, by looping over the layers
            //    - if the feature does apply to the current layer, we need to find a DisplayArea
            //      for it to satisfy (2)
            //      - we can re-use the previous layer's area if:
            //         the current feature also applies to the previous layer, (to satisfy (3))
            //         and the last feature that applied to the previous layer is the same as
            //           the last feature that applied to the current layer (to satisfy (2))
            //      - otherwise we create a new DisplayArea below the last feature that applied
            //        to the current layer


            PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer];
            final PendingArea root = new PendingArea(null, 0, null);
            Arrays.fill(areaForLayer, root);

            final int size = mFeatures.size();
            for (int i = 0; i < size; i++) {
                PendingArea featureArea = null;
                for (int layer = 0; layer < mMaxWindowLayer; layer++) {
                    final Feature feature = mFeatures.get(i);
                    if (feature.mWindowLayers[layer]) {
                        if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
                            // No suitable DisplayArea - create a new one under the previous area
                            // for this layer.
                            featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
                            areaForLayer[layer].mChildren.add(featureArea);
                        }
                        areaForLayer[layer] = featureArea;
                    } else {
                        featureArea = null;
                    }
                }
            }

            PendingArea leafArea = null;
            int leafType = LEAF_TYPE_TOKENS;
            for (int layer = 0; layer < mMaxWindowLayer; layer++) {
                int type = typeOfLayer(mWmService.mPolicy, layer);
                if (leafArea == null || leafArea.mParent != areaForLayer[layer]
                        || type != leafType) {
                    leafArea = new PendingArea(null, layer, areaForLayer[layer]);
                    areaForLayer[layer].mChildren.add(leafArea);
                    leafType = type;
                    if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
                        leafArea.mExisting = mTaskContainers;
                    } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
                        leafArea.mExisting = mImeContainer;
                    }
                }
                leafArea.mMaxLayer = layer;
            }
            root.computeMaxLayer();
            root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
        }

        @Override
        public void addWindow(WindowToken token) {
            DisplayArea.Tokens area = findAreaForToken(token);
            area.addChild(token);
        }

        @VisibleForTesting
        DisplayArea.Tokens findAreaForToken(WindowToken token) {
            int windowLayerFromType = token.getWindowLayerFromType();
            if (windowLayerFromType == APPLICATION_LAYER) {
                // TODO(display-area): Better handle AboveAppWindows in APPLICATION_LAYER
                windowLayerFromType += 1;
            } else if (token.mRoundedCornerOverlay) {
                windowLayerFromType = mMaxWindowLayer - 1;
            }
            return mAreaForLayer[windowLayerFromType];
        }

        public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) {
            return mAreas.get(feature);
        }

        private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
            if (layer == APPLICATION_LAYER) {
                return LEAF_TYPE_TASK_CONTAINERS;
            } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
                    || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
                return LEAF_TYPE_IME_CONTAINERS;
            } else {
                return LEAF_TYPE_TOKENS;
            }
        }
    }

    DisplayAreaPolicyBuilder addFeature(Feature feature) {
        mFeatures.add(feature);
        return this;
    }

    Result build(WindowManagerService wmService,
            DisplayContent content, DisplayArea.Root root,
            DisplayArea<? extends WindowContainer> imeContainer,
            DisplayArea<? extends ActivityStack> taskContainers) {

        return new Result(wmService, content, root, imeContainer, taskContainers, new ArrayList<>(
                mFeatures));
    }

    static class PendingArea {
        final int mMinLayer;
        final ArrayList<PendingArea> mChildren = new ArrayList<>();
        final Feature mFeature;
        final PendingArea mParent;
        int mMaxLayer;
        DisplayArea mExisting;

        PendingArea(Feature feature,
                int minLayer,
                PendingArea parent) {
            mMinLayer = minLayer;
            mFeature = feature;
            mParent = parent;
        }

        int computeMaxLayer() {
            for (int i = 0; i < mChildren.size(); i++) {
                mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer());
            }
            return mMaxLayer;
        }

        void instantiateChildren(DisplayArea<DisplayArea> parent,
                DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<?
                extends WindowContainer>>> areas) {
            mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
            for (int i = 0; i < mChildren.size(); i++) {
                final PendingArea child = mChildren.get(i);
                final DisplayArea area = child.createArea(parent, areaForLayer);
                parent.addChild(area, WindowContainer.POSITION_TOP);
                if (mFeature != null) {
                    areas.get(mFeature).add(area);
                }
                child.instantiateChildren(area, areaForLayer, level + 1, areas);
            }
        }

        private DisplayArea createArea(DisplayArea<DisplayArea> parent,
                DisplayArea.Tokens[] areaForLayer) {
            if (mExisting != null) {
                return mExisting;
            }
            DisplayArea.Type type;
            if (mMinLayer > APPLICATION_LAYER) {
                type = DisplayArea.Type.ABOVE_TASKS;
            } else if (mMaxLayer < APPLICATION_LAYER) {
                type = DisplayArea.Type.BELOW_TASKS;
            } else {
                type = DisplayArea.Type.ANY;
            }
            if (mFeature == null) {
                final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
                        "Leaf:" + mMinLayer + ":" + mMaxLayer);
                for (int i = mMinLayer; i <= mMaxLayer; i++) {
                    areaForLayer[i] = leaf;
                }
                return leaf;
            } else {
                return new DisplayArea(parent.mWmService, type, mFeature.mName + ":"
                        + mMinLayer + ":" + mMaxLayer);
            }
        }
    }
}
+214 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading