WM: Introduce DisplayArea (3/n)
Introduces the concept of a DisplayArea, corresponding to an area
on a logical display within which content can be placed.
DisplayAreas can contain more DisplayAreas, WindowTokens and ActivityStacks.
A future CL will expose an API which allows leashing a DisplayArea to transform
all its content simultaneously.
DisplayAreas are managed by a DisplayAreaPolicy, which creates and places the
areas, and decides which area a container is placed in.
This CL introduces the concept, but applies a no-op policy that maintains the
current orderings and does not apply any overrides.
Future work that remains:
- Writing the feature policies
- Adjusting Display & DisplayMetrics if the area is constraining its children.
- Moving the policy into an product-adjustable component
Bug: 147406652
Test: atest WmTests
Change-Id: If6fb1bba3b65ebf7ac9fdf99408c54bf77f602c9
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c0743e5..7c8f62c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -150,9 +150,9 @@
// Will be removed soon.
optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
/* non app windows */
- repeated WindowTokenProto above_app_windows = 6;
- repeated WindowTokenProto below_app_windows = 7;
- repeated WindowTokenProto ime_windows = 8;
+ repeated WindowTokenProto above_app_windows = 6 [deprecated=true];
+ repeated WindowTokenProto below_app_windows = 7 [deprecated=true];
+ repeated WindowTokenProto ime_windows = 8 [deprecated=true];
optional int32 dpi = 9;
optional .android.view.DisplayInfoProto display_info = 10;
optional int32 rotation = 11;
@@ -165,8 +165,33 @@
repeated IdentifierProto closing_apps = 18;
repeated IdentifierProto changing_apps = 19;
repeated WindowTokenProto overlay_windows = 20;
+ optional DisplayAreaProto root_display_area = 21;
}
+/* represents DisplayArea object */
+message DisplayAreaProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional WindowContainerProto window_container = 1;
+ optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
+ repeated DisplayAreaChildProto children = 3;
+}
+
+/* represents a generic child of a DisplayArea */
+message DisplayAreaChildProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ /* At most one of the following should be present: */
+
+ /* represents a DisplayArea child */
+ optional DisplayAreaProto display_area = 1;
+ /* represents a WindowToken child */
+ optional WindowTokenProto window = 2;
+ /* represents an unknown child - the class name is recorded */
+ repeated string unknown = 3;
+}
+
+
/* represents DisplayFrames */
message DisplayFramesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 91c5fbe..13dcb4c 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1573,12 +1573,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1004585481": {
- "message": "%s forcing orientation to %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"1051545910": {
"message": "Exit animation finished in %s: remove=%b",
"level": "VERBOSE",
@@ -1717,6 +1711,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "1389009035": {
+ "message": "NonAppWindowContainer cannot set orientation: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"1401700824": {
"message": "Window drawn win=%s",
"level": "DEBUG",
@@ -1891,6 +1891,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1674747211": {
+ "message": "%s forcing orientation to %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayArea.java"
+ },
"1677260366": {
"message": "Finish starting %s: first real window is shown, no animation",
"level": "VERBOSE",
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
new file mode 100644
index 0000000..b3edc91
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -0,0 +1,265 @@
+/*
+ * 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.wm.DisplayAreaChildProto.DISPLAY_AREA;
+import static com.android.server.wm.DisplayAreaChildProto.UNKNOWN;
+import static com.android.server.wm.DisplayAreaChildProto.WINDOW;
+import static com.android.server.wm.DisplayAreaProto.CHILDREN;
+import static com.android.server.wm.DisplayAreaProto.NAME;
+import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.Comparator;
+import java.util.function.Predicate;
+
+/**
+ * Container for grouping WindowContainer below DisplayContent.
+ *
+ * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and
+ * can be leashed.
+ *
+ * DisplayAreas can contain nested DisplayAreas.
+ *
+ * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order:
+ * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
+ * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
+ * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
+ * Cannot have a sibling that is of type ANY.
+ *
+ * @param <T> type of the children of the DisplayArea.
+ */
+public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
+
+ protected final Type mType;
+ private final String mName;
+
+ DisplayArea(WindowManagerService wms, Type type, String name) {
+ super(wms);
+ // TODO(display-area): move this up to ConfigurationContainer
+ mOrientation = SCREEN_ORIENTATION_UNSET;
+ mType = type;
+ mName = name;
+ }
+
+ @Override
+ void onChildPositionChanged(WindowContainer child) {
+ super.onChildPositionChanged(child);
+
+ // Verify that we have proper ordering
+ Type.checkChild(mType, Type.typeOf(child));
+
+ if (child instanceof ActivityStack) {
+ // TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings.
+ // They might need a separate type.
+ return;
+ }
+
+ for (int i = 1; i < getChildCount(); i++) {
+ final WindowContainer top = getChildAt(i - 1);
+ final WindowContainer bottom = getChildAt(i);
+ if (child == top || child == bottom) {
+ Type.checkSiblings(Type.typeOf(top), Type.typeOf(bottom));
+ }
+ }
+ }
+
+ @Override
+ boolean fillsParent() {
+ return true;
+ }
+
+ @Override
+ String getName() {
+ return mName;
+ }
+
+ @Override
+ public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
+ final long token = proto.start(fieldId);
+ super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+ proto.write(NAME, mName);
+ for (int i = 0; i < getChildCount(); i++) {
+ final long childToken = proto.start(CHILDREN);
+ final T child = getChildAt(i);
+ if (child instanceof ActivityStack) {
+ // TODO(display-area): Dump stacks & tasks here, instead of in DisplayContent's
+ // dumpDebug. For now, skip them here to avoid dumping them as UNKNOWN.
+ } else if (child instanceof WindowToken) {
+ ((WindowToken) child).dumpDebug(proto, WINDOW, logLevel);
+ } else if (child instanceof DisplayArea) {
+ child.dumpDebug(proto, DISPLAY_AREA, logLevel);
+ } else {
+ proto.write(UNKNOWN, child.getClass().getSimpleName());
+ }
+ proto.end(childToken);
+ }
+ proto.end(token);
+ }
+
+ /**
+ * DisplayArea that contains WindowTokens, and orders them according to their type.
+ */
+ public static class Tokens extends DisplayArea<WindowToken> {
+ int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+ private final Comparator<WindowToken> mWindowComparator =
+ Comparator.comparingInt(WindowToken::getWindowLayerFromType);
+
+ private final Predicate<WindowState> mGetOrientingWindow = w -> {
+ final WindowManagerPolicy policy = mWmService.mPolicy;
+ if (policy.isKeyguardHostWindow(w.mAttrs)) {
+ if (mWmService.mKeyguardGoingAway) {
+ return false;
+ }
+ // Consider unoccluding only when all unknown visibilities have been
+ // resolved, as otherwise we just may be starting another occluding activity.
+ final boolean isUnoccluding =
+ mDisplayContent.mAppTransition.getAppTransition()
+ == TRANSIT_KEYGUARD_UNOCCLUDE
+ && mDisplayContent.mUnknownAppVisibilityController.allResolved();
+ // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
+ // even if SystemUI hasn't updated the attrs yet.
+ if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
+ return true;
+ }
+ }
+ final int req = w.mAttrs.screenOrientation;
+ if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
+ || req == SCREEN_ORIENTATION_UNSET) {
+ return false;
+ }
+ return true;
+ };
+
+ Tokens(WindowManagerService wms, Type type, String name) {
+ super(wms, type, name);
+ }
+
+ void addChild(WindowToken token) {
+ addChild(token, mWindowComparator);
+ }
+
+ @Override
+ int getOrientation(int candidate) {
+ // Find a window requesting orientation.
+ final WindowState win = getWindow(mGetOrientingWindow);
+
+ if (win == null) {
+ return candidate;
+ }
+ int req = win.mAttrs.screenOrientation;
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "%s forcing orientation to %d for display id=%d",
+ win, req, mDisplayContent.getDisplayId());
+ if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+ // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
+ // stale. We record / use the last known override.
+ if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
+ mLastKeyguardForcedOrientation = req;
+ } else {
+ req = mLastKeyguardForcedOrientation;
+ }
+ }
+ return req;
+ }
+ }
+
+ /**
+ * Top-most DisplayArea under DisplayContent.
+ */
+ public static class Root extends DisplayArea<DisplayArea> {
+ private final Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
+ Root(WindowManagerService wms) {
+ super(wms, Type.ANY, "DisplayArea.Root");
+ }
+
+ @Override
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getBounds(mTmpDimBoundsRect);
+
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
+ }
+
+ enum Type {
+ /** Can only contain WindowTokens above the APPLICATION_LAYER. */
+ ABOVE_TASKS,
+ /** Can only contain WindowTokens below the APPLICATION_LAYER. */
+ BELOW_TASKS,
+ /** Can contain anything. */
+ ANY;
+
+ static void checkSiblings(Type bottom, Type top) {
+ checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY");
+ checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
+ bottom + " must be above BELOW_TASKS");
+ checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
+ top + " must be below ABOVE_TASKS");
+ }
+
+ static void checkChild(Type parent, Type child) {
+ switch (parent) {
+ case ABOVE_TASKS:
+ checkState(child == ABOVE_TASKS, "ABOVE_TASKS can only contain ABOVE_TASKS");
+ break;
+ case BELOW_TASKS:
+ checkState(child == BELOW_TASKS, "BELOW_TASKS can only contain BELOW_TASKS");
+ break;
+ }
+ }
+
+ static Type typeOf(WindowContainer c) {
+ if (c instanceof DisplayArea) {
+ return ((DisplayArea) c).mType;
+ } else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) {
+ return typeOf((WindowToken) c);
+ } else if (c instanceof ActivityStack) {
+ return ANY;
+ } else {
+ throw new IllegalArgumentException("Unknown container: " + c);
+ }
+ }
+
+ private static Type typeOf(WindowToken c) {
+ return c.getWindowLayerFromType() < APPLICATION_LAYER ? BELOW_TASKS : ABOVE_TASKS;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
new file mode 100644
index 0000000..06e7b48
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -0,0 +1,123 @@
+/*
+ * 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_INPUT_METHOD;
+
+import com.android.server.wm.DisplayContent.TaskContainers;
+
+/**
+ * Policy that manages DisplayAreas.
+ */
+public abstract class DisplayAreaPolicy {
+ protected final WindowManagerService mWmService;
+ protected final DisplayContent mContent;
+
+ /**
+ * The root DisplayArea. Attach all DisplayAreas to this area (directly or indirectly).
+ */
+ protected final DisplayArea.Root mRoot;
+
+ /**
+ * The IME container. The IME's windows are automatically added to this container.
+ */
+ protected final DisplayArea<? extends WindowContainer> mImeContainer;
+
+ /**
+ * The Tasks container. Tasks etc. are automatically added to this container.
+ */
+ protected final TaskContainers mTaskContainers;
+
+ DisplayAreaPolicy(WindowManagerService wmService,
+ DisplayContent content, DisplayArea.Root root,
+ DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
+ mWmService = wmService;
+ mContent = content;
+ mRoot = root;
+ mImeContainer = imeContainer;
+ mTaskContainers = taskContainers;
+ }
+
+ /**
+ * Called to ask the policy to set up the DisplayArea hierarchy. At a minimum this must:
+ *
+ * - attach mImeContainer to mRoot (or one of its descendants)
+ * - attach mTaskStacks to mRoot (or one of its descendants)
+ *
+ * Additionally, this is the right place to set up any other DisplayAreas as desired.
+ */
+ public abstract void attachDisplayAreas();
+
+ /**
+ * Called to ask the policy to attach the given WindowToken to the DisplayArea hierarchy.
+ *
+ * This must attach the token to mRoot (or one of its descendants).
+ */
+ 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);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6e479b2..7073707 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -80,7 +80,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
@@ -98,9 +97,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS;
import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
@@ -109,9 +106,9 @@
import static com.android.server.wm.DisplayContentProto.DPI;
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.ID;
-import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
@@ -295,14 +292,7 @@
/** The containers below are the only child containers {@link #mWindowContainers} can have. */
// Contains all window containers that are related to apps (Activities)
private final TaskContainers mTaskContainers = new TaskContainers(mWmService);
- // Contains all non-app window containers that should be displayed above the app containers
- // (e.g. Status bar)
- private final AboveAppWindowContainers mAboveAppWindowsContainers =
- new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService);
- // Contains all non-app window containers that should be displayed below the app containers
- // (e.g. Wallpaper).
- private final NonAppWindowContainers mBelowAppWindowsContainers =
- new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService);
+
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
@@ -310,6 +300,11 @@
// TODO(display-area): is "no magnification" in the comment still true?
private final ImeContainer mImeWindowsContainers = new ImeContainer(mWmService);
+ private final DisplayArea.Root mRootDisplayArea = new DisplayArea.Root(mWmService);
+
+ private final DisplayAreaPolicy mDisplayAreaPolicy = new DisplayAreaPolicy.Default(
+ mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+
private WindowState mTmpWindow;
private WindowState mTmpWindow2;
private boolean mUpdateImeTarget;
@@ -401,14 +396,6 @@
private int mCurrentOverrideConfigurationChanges;
/**
- * Last orientation forced by the keyguard. It is applied when keyguard is shown and is not
- * occluded.
- *
- * @see NonAppWindowContainers#getOrientation()
- */
- private int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-
- /**
* The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. The
* orientation requests from apps would be ignored if the display is close-to-square.
*/
@@ -1104,18 +1091,16 @@
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
switch (token.windowType) {
- case TYPE_WALLPAPER:
- mBelowAppWindowsContainers.addChild(token);
- break;
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainers.addChild(token);
break;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+ // TODO(display-area): Migrate to DisplayArea
mOverlayContainers.addChild(token);
break;
default:
- mAboveAppWindowsContainers.addChild(token);
+ mDisplayAreaPolicy.addWindow(token);
break;
}
}
@@ -2129,13 +2114,7 @@
return getLastOrientation();
}
}
- final int orientation = mAboveAppWindowsContainers.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
- }
-
- // Top system windows are not requesting an orientation. Start searching from apps.
- return mTaskContainers.getOrientation();
+ return mRootDisplayArea.getOrientation();
}
void updateDisplayInfo() {
@@ -2782,23 +2761,12 @@
final long token = proto.start(fieldId);
super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
proto.write(ID, mDisplayId);
+ mRootDisplayArea.dumpDebug(proto, ROOT_DISPLAY_AREA, logLevel);
for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
stack.dumpDebugInnerStackOnly(proto, STACKS, logLevel);
}
mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
- for (int i = mAboveAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mAboveAppWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, ABOVE_APP_WINDOWS, logLevel);
- }
- for (int i = mBelowAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mBelowAppWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, BELOW_APP_WINDOWS, logLevel);
- }
- for (int i = mImeWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, IME_WINDOWS, logLevel);
- }
for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
final WindowToken windowToken = mOverlayContainers.getChildAt(i);
windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
@@ -4212,7 +4180,7 @@
* Window container class that contains all containers on this display relating to Apps.
* I.e Activities.
*/
- private final class TaskContainers extends DisplayChildWindowContainer<ActivityStack> {
+ final class TaskContainers extends DisplayArea<ActivityStack> {
/**
* A control placed at the appropriate level for transitions to occur.
*/
@@ -4222,16 +4190,15 @@
/**
* Given that the split-screen divider does not have an AppWindowToken, it
- * will have to live inside of a "NonAppWindowContainer", in particular
- * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order
+ * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
* it will need to be interleaved with some of our children, appearing on top of
* both docked stacks but underneath any assistant stacks.
*
* To solve this problem we have this anchor control, which will always exist so
* we can always assign it the correct value in our {@link #assignChildLayers}.
- * Likewise since it always exists, {@link AboveAppWindowContainers} can always
+ * Likewise since it always exists, we can always
* assign the divider a layer relative to it. This way we prevent linking lifecycle
- * events between the two containers.
+ * events between tasks and the divider window.
*/
SurfaceControl mSplitScreenDividerAnchor = null;
@@ -4242,7 +4209,7 @@
private ActivityStack mRootSplitScreenPrimaryTask = null;
TaskContainers(WindowManagerService service) {
- super(service);
+ super(service, Type.ANY, "TaskContainers");
}
/**
@@ -4737,32 +4704,6 @@
}
}
- private final class AboveAppWindowContainers extends NonAppWindowContainers {
- AboveAppWindowContainers(String name, WindowManagerService service) {
- super(name, service);
- }
-
- @Override
- void assignChildLayers(SurfaceControl.Transaction t) {
- boolean needAssignIme = mImeWindowsContainers.getSurfaceControl() != null;
- for (int j = 0; j < mChildren.size(); ++j) {
- final WindowToken wt = mChildren.get(j);
-
- wt.assignLayer(t, j);
- wt.assignChildLayers(t);
-
- int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(
- wt.windowType, wt.mOwnerCanManageAppTokens);
-
- if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
- TYPE_INPUT_METHOD_DIALOG, true)) {
- mImeWindowsContainers.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
- needAssignIme = false;
- }
- }
- }
- }
-
private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
private final String mName;
@@ -4774,13 +4715,10 @@
@Override
void assignChildLayers(SurfaceControl.Transaction t) {
mImeWindowsContainers.setNeedsLayer();
- mBelowAppWindowsContainers.assignLayer(t, 0);
- mTaskContainers.assignLayer(t, 1);
- mAboveAppWindowsContainers.assignLayer(t, 2);
+
+ mRootDisplayArea.assignLayer(t, 0);
final WindowState imeTarget = mInputMethodTarget;
- boolean needAssignIme = true;
-
// In the case where we have an IME target that is not in split-screen mode IME
// assignment is easy. We just need the IME to go directly above the target. This way
// children of the target will naturally go above the IME and everyone is happy.
@@ -4813,11 +4751,7 @@
// Above we have assigned layers to our children, now we ask them to assign
// layers to their children.
- mBelowAppWindowsContainers.assignChildLayers(t);
- mTaskContainers.assignChildLayers(t);
- mAboveAppWindowsContainers.assignChildLayers(t);
- mImeWindowsContainers.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
- mImeWindowsContainers.assignChildLayers(t);
+ mRootDisplayArea.assignChildLayers(t);
}
@Override
@@ -4826,10 +4760,8 @@
}
void addChildren() {
- addChild(mBelowAppWindowsContainers, null);
- addChild(mTaskContainers, null);
- addChild(mAboveAppWindowsContainers, null);
- addChild(mImeWindowsContainers, null);
+ addChild(mRootDisplayArea, 0);
+ mDisplayAreaPolicy.attachDisplayAreas();
}
@Override
@@ -4856,32 +4788,6 @@
< mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
token2.mOwnerCanManageAppTokens) ? -1 : 1;
- private final Predicate<WindowState> mGetOrientingWindow = w -> {
- final WindowManagerPolicy policy = mWmService.mPolicy;
- if (policy.isKeyguardHostWindow(w.mAttrs)) {
- if (mWmService.mKeyguardGoingAway) {
- return false;
- }
- // Consider unoccluding only when all unknown visibilities have been
- // resolved, as otherwise we just may be starting another occluding activity.
- final boolean isUnoccluding =
- mDisplayContent.mAppTransition.getAppTransition()
- == TRANSIT_KEYGUARD_UNOCCLUDE
- && mDisplayContent.mUnknownAppVisibilityController.allResolved();
- // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
- // even if SystemUI hasn't updated the attrs yet.
- if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
- return true;
- }
- }
- final int req = w.mAttrs.screenOrientation;
- if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
- || req == SCREEN_ORIENTATION_UNSET) {
- return false;
- }
- return true;
- };
-
private final String mName;
private final Dimmer mDimmer = new Dimmer(this);
private final Rect mTmpDimBoundsRect = new Rect();
@@ -4903,26 +4809,9 @@
@Override
int getOrientation(int candidate) {
- // Find a window requesting orientation.
- final WindowState win = getWindow(mGetOrientingWindow);
-
- if (win != null) {
- int req = win.mAttrs.screenOrientation;
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "%s forcing orientation to %d for display id=%d", win, req,
- mDisplayId);
- if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
- // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
- // stale. We record / use the last known override.
- if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
- mDisplayContent.mLastKeyguardForcedOrientation = req;
- } else {
- req = mDisplayContent.mLastKeyguardForcedOrientation;
- }
- }
- return req;
- }
- return candidate;
+ ProtoLog.w(WM_DEBUG_ORIENTATION, "NonAppWindowContainer cannot set orientation: %s",
+ this);
+ return SCREEN_ORIENTATION_UNSET;
}
@Override
@@ -4957,11 +4846,11 @@
* - the container doesn't always participate in window traversal, according to
* {@link #skipImeWindowsDuringTraversal()}
*/
- private class ImeContainer extends NonAppWindowContainers {
+ private static class ImeContainer extends DisplayArea.Tokens {
boolean mNeedsLayer = false;
ImeContainer(WindowManagerService wms) {
- super("ImeContainer", wms);
+ super(wms, Type.ABOVE_TASKS, "ImeContainer");
}
public void setNeedsLayer() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
new file mode 100644
index 0000000..618e608
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
+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.DisplayArea.Type.BELOW_TASKS;
+import static com.android.server.wm.DisplayArea.Type.checkChild;
+import static com.android.server.wm.DisplayArea.Type.checkSiblings;
+import static com.android.server.wm.DisplayArea.Type.typeOf;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@Presubmit
+public class DisplayAreaTest {
+
+ @Rule
+ public SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+
+ @Test
+ public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+ DisplayArea<WindowContainer> parent = new DisplayArea<>(wms, BELOW_TASKS, "Parent");
+ DisplayArea<WindowContainer> child = new DisplayArea<>(wms, ANY, "Child");
+
+ assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0));
+ }
+
+ @Test
+ public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+ DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent");
+ DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1");
+ DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2");
+
+ parent.addChild(child1, 0);
+ assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0));
+ }
+
+ @Test
+ public void testType_typeOf() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+
+ assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(wms, ABOVE_TASKS, "test")));
+ assertEquals(ANY, typeOf(new DisplayArea<>(wms, ANY, "test")));
+ assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(wms, BELOW_TASKS, "test")));
+
+ assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY)));
+ assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION)));
+ assertEquals(BELOW_TASKS, typeOf(createWindowToken(TYPE_WALLPAPER)));
+
+ assertThrows(IllegalArgumentException.class, () -> typeOf(mock(ActivityRecord.class)));
+ assertThrows(IllegalArgumentException.class, () -> typeOf(mock(WindowContainer.class)));
+ }
+
+ @Test
+ public void testType_checkSiblings() {
+ checkSiblings(BELOW_TASKS, BELOW_TASKS);
+ checkSiblings(BELOW_TASKS, ANY);
+ checkSiblings(BELOW_TASKS, ABOVE_TASKS);
+ checkSiblings(ANY, ABOVE_TASKS);
+ checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
+
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
+ }
+
+ @Test
+ public void testType_checkChild() {
+ checkChild(ANY, ANY);
+ checkChild(ANY, ABOVE_TASKS);
+ checkChild(ANY, BELOW_TASKS);
+ checkChild(ABOVE_TASKS, ABOVE_TASKS);
+ checkChild(BELOW_TASKS, BELOW_TASKS);
+
+ assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, BELOW_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, ANY));
+ assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ABOVE_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY));
+ }
+
+ private WindowToken createWindowToken(int type) {
+ return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
+ type, false /* persist */, null /* displayContent */,
+ false /* canManageTokens */);
+ }
+
+ private static class SurfacelessDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
+
+ SurfacelessDisplayArea(WindowManagerService wms, Type type, String name) {
+ super(wms, type, name);
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ return new MockSurfaceControlBuilder();
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1637370..2ea00ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -139,11 +139,11 @@
mAppWindow,
mChildAppWindowAbove,
mDockedDividerWindow,
+ mImeWindow,
+ mImeDialogWindow,
mStatusBarWindow,
mNotificationShadeWindow,
- mNavBarWindow,
- mImeWindow,
- mImeDialogWindow));
+ mNavBarWindow));
}
@Test
@@ -232,11 +232,11 @@
mChildAppWindowAbove,
mDockedDividerWindow,
voiceInteractionWindow,
+ mImeWindow,
+ mImeDialogWindow,
mStatusBarWindow,
mNotificationShadeWindow,
- mNavBarWindow,
- mImeWindow,
- mImeDialogWindow));
+ mNavBarWindow));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
new file mode 100644
index 0000000..1e98277
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
@@ -0,0 +1,48 @@
+/*
+ * 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.testing;
+
+/**
+ * Assertions for WM tests.
+ */
+public class Assert {
+
+ /**
+ * Runs {@code r} and asserts that an exception of type {@code expectedThrowable} is thrown.
+ * @param expectedThrowable the type of throwable that is expected to be thrown
+ * @param r the {@link Runnable} which is run and expected to throw.
+ * @throws AssertionError if {@code r} does not throw, or throws a runnable that is not an
+ * instance of {@code expectedThrowable}.
+ */
+ // TODO: remove once Android migrates to JUnit 4.13, which provides assertThrows
+ public static void assertThrows(Class<? extends Throwable> expectedThrowable, Runnable r) {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ if (expectedThrowable.isInstance(t)) {
+ return;
+ } else if (t instanceof Exception) {
+ throw new AssertionError("Expected " + expectedThrowable
+ + ", but got " + t.getClass(), t);
+ } else {
+ // Re-throw Errors and other non-Exception throwables.
+ throw t;
+ }
+ }
+ throw new AssertionError("Expected " + expectedThrowable + ", but nothing was thrown");
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
new file mode 100644
index 0000000..df12761
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.testing;
+
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class AssertTest {
+
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
+ @Test
+ public void assertThrows_runsRunnable() {
+ boolean[] ran = new boolean[] { false };
+ assertThrows(TestException.class, () -> {
+ ran[0] = true;
+ throw new TestException();
+ });
+ assertTrue(ran[0]);
+ }
+
+ @Test
+ public void assertThrows_failsIfNothingThrown() {
+ mExpectedException.expect(AssertionError.class);
+ assertThrows(TestException.class, () -> {
+ });
+ }
+
+ @Test
+ public void assertThrows_failsIfWrongExceptionThrown() {
+ mExpectedException.expect(AssertionError.class);
+ assertThrows(TestException.class, () -> {
+ throw new RuntimeException();
+ });
+ }
+
+ @Test
+ public void assertThrows_succeedsIfGivenExceptionThrown() {
+ assertThrows(TestException.class, () -> {
+ throw new TestException();
+ });
+ }
+
+ @Test
+ public void assertThrows_succeedsIfSubExceptionThrown() {
+ assertThrows(RuntimeException.class, () -> {
+ throw new TestException();
+ });
+ }
+
+ @Test
+ public void assertThrows_rethrowsUnexpectedErrors() {
+ mExpectedException.expect(TestError.class);
+ assertThrows(TestException.class, () -> {
+ throw new TestError();
+ });
+ }
+
+ static class TestException extends RuntimeException {
+ }
+
+ static class TestError extends Error {
+ }
+
+}