diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 67a22d3e477c8..39093aec07c07 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -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. diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index b3edc91a41291..0d365b16e228e 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -101,6 +101,11 @@ public class DisplayArea extends WindowContainer { 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); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 9e93e1455f2c4..0ec0c7b53875a 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -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 mTaskContainers; /** * Construct a new {@link DisplayAreaPolicy} @@ -58,7 +56,8 @@ public abstract class DisplayAreaPolicy { */ protected DisplayAreaPolicy(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, - DisplayArea imeContainer, TaskContainers taskContainers) { + DisplayArea imeContainer, + DisplayArea 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, + /** 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 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 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 { - @Override - public DisplayAreaPolicy instantiate(WindowManagerService wmService, - DisplayContent content, DisplayArea.Root root, - DisplayArea 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(); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java new file mode 100644 index 0000000000000..885456a8488cd --- /dev/null +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -0,0 +1,371 @@ +/* + * 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} + * + *

Given a set of features (that each target a set of window types), it builds the necessary + * DisplayArea hierarchy. + * + *

Example:
+ * + *

+ *     // 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)
+ *
+ * 
+ * + * // TODO(display-area): document more complex scenarios where we need multiple areas per feature. + */ +class DisplayAreaPolicyBuilder { + + private final ArrayList 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. + * + *

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 mFeatures; + private final Map>> mAreas; + private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer]; + + Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, + DisplayArea imeContainer, + DisplayArea taskStacks, ArrayList 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> 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 imeContainer, + DisplayArea taskContainers) { + + return new Result(wmService, content, root, imeContainer, taskContainers, new ArrayList<>( + mFeatures)); + } + + static class PendingArea { + final int mMinLayer; + final ArrayList 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 parent, + DisplayArea.Tokens[] areaForLayer, int level, Map>> 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 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); + } + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java new file mode 100644 index 0000000000000..8ac1d24333be9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -0,0 +1,214 @@ +/* + * 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_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; + +import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; +import static com.android.server.wm.DisplayArea.Type.ANY; +import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import static java.util.stream.Collectors.toList; + +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; + +import org.hamcrest.CustomTypeSafeMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Rule; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Presubmit +public class DisplayAreaPolicyBuilderTest { + + @Rule + public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule(); + + private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null); + + @Test + public void testBuilder() { + WindowManagerService wms = mSystemServices.getWindowManagerService(); + DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms); + DisplayArea ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); + DisplayArea tasks = new DisplayArea<>(wms, ANY, "Tasks"); + + final Feature foo; + final Feature bar; + + DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .addFeature(foo = new Feature.Builder(mPolicy, "Foo") + .upTo(TYPE_STATUS_BAR) + .and(TYPE_NAVIGATION_BAR) + .build()) + .addFeature(bar = new Feature.Builder(mPolicy, "Bar") + .all() + .except(TYPE_STATUS_BAR) + .build()) + .build(wms, mock(DisplayContent.class), root, ime, tasks); + + policy.attachDisplayAreas(); + + assertThat(policy.getDisplayAreas(foo), is(not(empty()))); + assertThat(policy.getDisplayAreas(bar), is(not(empty()))); + + assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)), + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)), + is(not(decendantOfOneOf(policy.getDisplayAreas(bar))))); + + assertThat(tasks, + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(tasks, + is(decendantOfOneOf(policy.getDisplayAreas(bar)))); + + assertThat(ime, + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(ime, + is(decendantOfOneOf(policy.getDisplayAreas(bar)))); + + List> actualOrder = collectLeafAreas(root); + Map, Set> zSets = calculateZSets(policy, root, ime, tasks); + actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList()); + + Map, Integer> expectedByMinLayer = mapValues(zSets, + v -> v.stream().min(Integer::compareTo).get()); + Map, Integer> expectedByMaxLayer = mapValues(zSets, + v -> v.stream().max(Integer::compareTo).get()); + + assertThat(expectedByMinLayer, is(equalTo(expectedByMaxLayer))); + assertThat(actualOrder, is(equalTo(expectedByMaxLayer))); + } + + private Map mapValues(Map zSets, Function f) { + return zSets.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + e -> f.apply(e.getValue()))); + } + + private List> collectLeafAreas(DisplayArea root) { + ArrayList> leafs = new ArrayList<>(); + traverseLeafAreas(root, leafs::add); + return leafs; + } + + private Map, Set> calculateZSets( + DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root, + DisplayArea ime, + DisplayArea tasks) { + Map, Set> zSets = new HashMap<>(); + int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION, + TYPE_APPLICATION_OVERLAY}; + for (int type : types) { + WindowToken token = tokenOfType(type); + recordLayer(policy.findAreaForToken(token), token.getWindowLayerFromType(), zSets); + } + recordLayer(tasks, APPLICATION_LAYER, zSets); + recordLayer(ime, mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD), zSets); + return zSets; + } + + private void recordLayer(DisplayArea area, int layer, + Map, Set> zSets) { + zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer); + } + + private Matcher decendantOfOneOf(List expected) { + return new CustomTypeSafeMatcher("descendant of one of " + expected) { + @Override + protected boolean matchesSafely(WindowContainer actual) { + for (WindowContainer expected : expected) { + WindowContainer candidate = actual; + while (candidate != null && candidate.getParent() != candidate) { + if (candidate.getParent() == expected) { + return true; + } + candidate = candidate.getParent(); + } + } + return false; + } + + @Override + protected void describeMismatchSafely(WindowContainer item, + Description description) { + description.appendText("was ").appendValue(item); + while (item != null && item.getParent() != item) { + item = item.getParent(); + description.appendText(", child of ").appendValue(item); + } + } + }; + } + + private WindowToken tokenOfType(int type) { + WindowToken m = mock(WindowToken.class); + when(m.getWindowLayerFromType()).thenReturn(mPolicy.getWindowLayerFromTypeLw(type)); + return m; + } + + private static void traverseLeafAreas(DisplayArea root, Consumer> consumer) { + boolean leaf = true; + for (int i = 0; i < root.getChildCount(); i++) { + WindowContainer child = root.getChildAt(i); + if (child instanceof DisplayArea) { + traverseLeafAreas((DisplayArea) child, consumer); + leaf = false; + } + } + if (leaf) { + consumer.accept(root); + } + } + + private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root { + + SurfacelessDisplayAreaRoot(WindowManagerService wms) { + super(wms); + } + + @Override + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + return new MockSurfaceControlBuilder(); + } + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java index c1a1d5ecd3c82..31206315618e7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java @@ -34,13 +34,13 @@ public class DisplayAreaProviderTest { @Test public void testFromResources_emptyProvider() { Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider("")), - Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class)); + Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class)); } @Test public void testFromResources_nullProvider() { Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(null)), - Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class)); + Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index f517881d835bd..8ad75053060f0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -56,6 +56,7 @@ import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.StrictMode; import android.os.UserHandle; +import android.util.Log; import android.view.InputChannel; import android.view.Surface; import android.view.SurfaceControl; @@ -120,11 +121,22 @@ public class SystemServicesTestRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { + Throwable throwable = null; try { runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp); base.evaluate(); + } catch (Throwable t) { + throwable = t; } finally { - tearDown(); + try { + tearDown(); + } catch (Throwable t) { + if (throwable != null) { + Log.e("SystemServicesTestRule", "Suppressed: ", throwable); + t.addSuppressed(throwable); + } + throw t; + } } } };