| /* |
| * 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 String toString() { |
| return mName + "@" + System.identityHashCode(this); |
| } |
| |
| @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; |
| } |
| } |
| } |