Merge "WM: Build policy with DisplayAreaPolicyBuilder (5/n)"
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
/** 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) {
|
||||
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 {
|
||||
@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();
|
||||
|
||||
@@ -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}
|
||||
*
|
||||
* <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
|
||||
DisplayArea<ActivityStack> 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<DisplayArea<?>> actualOrder = collectLeafAreas(root);
|
||||
Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks);
|
||||
actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
|
||||
|
||||
Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets,
|
||||
v -> v.stream().min(Integer::compareTo).get());
|
||||
Map<DisplayArea<?>, Integer> expectedByMaxLayer = mapValues(zSets,
|
||||
v -> v.stream().max(Integer::compareTo).get());
|
||||
|
||||
assertThat(expectedByMinLayer, is(equalTo(expectedByMaxLayer)));
|
||||
assertThat(actualOrder, is(equalTo(expectedByMaxLayer)));
|
||||
}
|
||||
|
||||
private <K, V, R> Map<K, R> mapValues(Map<K, V> zSets, Function<V, R> f) {
|
||||
return zSets.entrySet().stream().collect(Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
e -> f.apply(e.getValue())));
|
||||
}
|
||||
|
||||
private List<DisplayArea<?>> collectLeafAreas(DisplayArea<?> root) {
|
||||
ArrayList<DisplayArea<?>> leafs = new ArrayList<>();
|
||||
traverseLeafAreas(root, leafs::add);
|
||||
return leafs;
|
||||
}
|
||||
|
||||
private Map<DisplayArea<?>, Set<Integer>> calculateZSets(
|
||||
DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root,
|
||||
DisplayArea<WindowContainer> ime,
|
||||
DisplayArea<ActivityStack> tasks) {
|
||||
Map<DisplayArea<?>, Set<Integer>> 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<DisplayArea<?>, Set<Integer>> zSets) {
|
||||
zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer);
|
||||
}
|
||||
|
||||
private Matcher<WindowContainer> decendantOfOneOf(List<? extends WindowContainer> expected) {
|
||||
return new CustomTypeSafeMatcher<WindowContainer>("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<DisplayArea<?>> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user