1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
22 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
23 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
24 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
25 
26 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
27 import static com.android.internal.util.Preconditions.checkState;
28 import static com.android.server.wm.DisplayAreaProto.FEATURE_ID;
29 import static com.android.server.wm.DisplayAreaProto.IS_ORGANIZED;
30 import static com.android.server.wm.DisplayAreaProto.IS_ROOT_DISPLAY_AREA;
31 import static com.android.server.wm.DisplayAreaProto.IS_TASK_DISPLAY_AREA;
32 import static com.android.server.wm.DisplayAreaProto.NAME;
33 import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
34 import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
35 
36 import android.annotation.Nullable;
37 import android.content.res.Configuration;
38 import android.graphics.Rect;
39 import android.util.proto.ProtoOutputStream;
40 import android.window.DisplayAreaInfo;
41 import android.window.IDisplayAreaOrganizer;
42 
43 import com.android.internal.protolog.common.ProtoLog;
44 import com.android.server.policy.WindowManagerPolicy;
45 
46 import java.io.PrintWriter;
47 import java.util.Comparator;
48 import java.util.function.BiFunction;
49 import java.util.function.Consumer;
50 import java.util.function.Function;
51 import java.util.function.Predicate;
52 
53 /**
54  * Container for grouping WindowContainer below DisplayContent.
55  *
56  * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and
57  * can be leashed.
58  *
59  * DisplayAreas can contain nested DisplayAreas.
60  *
61  * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order:
62  * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
63  * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
64  * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
65  *
66  * @param <T> type of the children of the DisplayArea.
67  */
68 public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
69 
70     protected final Type mType;
71     private final String mName;
72     final int mFeatureId;
73     private final DisplayAreaOrganizerController mOrganizerController;
74     IDisplayAreaOrganizer mOrganizer;
75     private final Configuration mTmpConfiguration = new Configuration();
76 
77     /**
78      * Whether this {@link DisplayArea} should ignore fixed-orientation request. If {@code true}, it
79      * can never specify orientation, but shows the fixed-orientation apps below it in the
80      * letterbox; otherwise, it rotates based on the fixed-orientation request.
81      */
82     protected boolean mIgnoreOrientationRequest;
83 
DisplayArea(WindowManagerService wms, Type type, String name)84     DisplayArea(WindowManagerService wms, Type type, String name) {
85         this(wms, type, name, FEATURE_UNDEFINED);
86     }
87 
DisplayArea(WindowManagerService wms, Type type, String name, int featureId)88     DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
89         super(wms);
90         // TODO(display-area): move this up to ConfigurationContainer
91         mOrientation = SCREEN_ORIENTATION_UNSET;
92         mType = type;
93         mName = name;
94         mFeatureId = featureId;
95         mRemoteToken = new RemoteToken(this);
96         mOrganizerController =
97                 wms.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
98     }
99 
100     @Override
onChildPositionChanged(WindowContainer child)101     void onChildPositionChanged(WindowContainer child) {
102         super.onChildPositionChanged(child);
103 
104         // Verify that we have proper ordering
105         Type.checkChild(mType, Type.typeOf(child));
106 
107         if (child instanceof Task) {
108             // TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings.
109             //                     They might need a separate type.
110             return;
111         }
112 
113         for (int i = 1; i < getChildCount(); i++) {
114             final WindowContainer top = getChildAt(i - 1);
115             final WindowContainer bottom = getChildAt(i);
116             if (child == top || child == bottom) {
117                 Type.checkSiblings(Type.typeOf(top), Type.typeOf(bottom));
118             }
119         }
120     }
121 
122     @Override
positionChildAt(int position, T child, boolean includingParents)123     void positionChildAt(int position, T child, boolean includingParents) {
124         if (child.asDisplayArea() == null) {
125             // Reposition other window containers as normal.
126             super.positionChildAt(position, child, includingParents);
127             return;
128         }
129 
130         final int targetPosition = findPositionForChildDisplayArea(position, child.asDisplayArea());
131         super.positionChildAt(targetPosition, child, false /* includingParents */);
132 
133         final WindowContainer parent = getParent();
134         if (includingParents && parent != null
135                 && (position == POSITION_TOP || position == POSITION_BOTTOM)) {
136             parent.positionChildAt(position, this /* child */, true /* includingParents */);
137         }
138     }
139 
140     @Override
getOrientation(int candidate)141     int getOrientation(int candidate) {
142         mLastOrientationSource = null;
143         if (mIgnoreOrientationRequest) {
144             return SCREEN_ORIENTATION_UNSET;
145         }
146 
147         return super.getOrientation(candidate);
148     }
149 
150     @Override
handlesOrientationChangeFromDescendant()151     boolean handlesOrientationChangeFromDescendant() {
152         return !mIgnoreOrientationRequest && super.handlesOrientationChangeFromDescendant();
153     }
154 
155     @Override
onDescendantOrientationChanged(WindowContainer requestingContainer)156     boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
157         // If this is set to ignore the orientation request, we don't propagate descendant
158         // orientation request.
159         return !mIgnoreOrientationRequest
160                 && super.onDescendantOrientationChanged(requestingContainer);
161     }
162 
163     /**
164      * Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and
165      * windows below it.
166      *
167      * @return Whether the display orientation changed after calling this method.
168      */
setIgnoreOrientationRequest(boolean ignoreOrientationRequest)169     boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
170         if (mIgnoreOrientationRequest == ignoreOrientationRequest) {
171             return false;
172         }
173         mIgnoreOrientationRequest = ignoreOrientationRequest;
174 
175         // Check whether we should notify Display to update orientation.
176         if (mDisplayContent == null) {
177             return false;
178         }
179 
180         if (mDisplayContent.mFocusedApp != null) {
181             // We record the last focused TDA that respects orientation request, check if this
182             // change may affect it.
183             mDisplayContent.onLastFocusedTaskDisplayAreaChanged(
184                     mDisplayContent.mFocusedApp.getDisplayArea());
185         }
186 
187         // The orientation request from this DA may now be respected.
188         if (!ignoreOrientationRequest) {
189             return mDisplayContent.updateOrientation();
190         }
191 
192         final int lastOrientation = mDisplayContent.getLastOrientation();
193         final WindowContainer lastOrientationSource = mDisplayContent.getLastOrientationSource();
194         if (lastOrientation == SCREEN_ORIENTATION_UNSET
195                 || lastOrientation == SCREEN_ORIENTATION_UNSPECIFIED) {
196             // Orientation won't be changed.
197             return false;
198         }
199         if (lastOrientationSource == null || lastOrientationSource.isDescendantOf(this)) {
200             // Try update if the orientation may be affected.
201             return mDisplayContent.updateOrientation();
202         }
203         return false;
204     }
205 
getIgnoreOrientationRequest()206     boolean getIgnoreOrientationRequest() {
207         return mIgnoreOrientationRequest;
208     }
209 
210     /**
211      * When a {@link DisplayArea} is repositioned, it should only be moved among its siblings of the
212      * same {@link Type}.
213      * For example, when a {@link DisplayArea} of {@link Type#ANY} is repositioned, it shouldn't be
214      * moved above any {@link Type#ABOVE_TASKS} siblings, or below any {@link Type#BELOW_TASKS}
215      * siblings.
216      */
findPositionForChildDisplayArea(int requestPosition, DisplayArea child)217     private int findPositionForChildDisplayArea(int requestPosition, DisplayArea child) {
218         if (child.getParent() != this) {
219             throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
220                     + " is not a child of container=" + getName()
221                     + " current parent=" + child.getParent());
222         }
223 
224         // The max possible position we can insert the child at.
225         int maxPosition = findMaxPositionForChildDisplayArea(child);
226         // The min possible position we can insert the child at.
227         int minPosition = findMinPositionForChildDisplayArea(child);
228 
229         return Math.max(Math.min(requestPosition, maxPosition), minPosition);
230     }
231 
findMaxPositionForChildDisplayArea(DisplayArea child)232     private int findMaxPositionForChildDisplayArea(DisplayArea child) {
233         final Type childType = Type.typeOf(child);
234         for (int i = mChildren.size() - 1; i > 0; i--) {
235             if (Type.typeOf(getChildAt(i)) == childType) {
236                 return i;
237             }
238         }
239         return 0;
240     }
241 
findMinPositionForChildDisplayArea(DisplayArea child)242     private int findMinPositionForChildDisplayArea(DisplayArea child) {
243         final Type childType = Type.typeOf(child);
244         for (int i = 0; i < mChildren.size(); i++) {
245             if (Type.typeOf(getChildAt(i)) == childType) {
246                 return i;
247             }
248         }
249         return mChildren.size() - 1;
250     }
251 
252     @Override
needsZBoost()253     boolean needsZBoost() {
254         // Z Boost should only happen at or below the ActivityStack level.
255         return false;
256     }
257 
258     @Override
fillsParent()259     boolean fillsParent() {
260         return true;
261     }
262 
263     @Override
getName()264     String getName() {
265         return mName;
266     }
267 
268     @Override
toString()269     public String toString() {
270         return mName + "@" + System.identityHashCode(this);
271     }
272 
273     @Override
dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel)274     public void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
275         final long token = proto.start(fieldId);
276         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
277         proto.write(NAME, mName);
278         proto.write(IS_TASK_DISPLAY_AREA, isTaskDisplayArea());
279         proto.write(IS_ROOT_DISPLAY_AREA, asRootDisplayArea() != null);
280         proto.write(FEATURE_ID, mFeatureId);
281         proto.write(IS_ORGANIZED, isOrganized());
282         proto.end(token);
283     }
284 
285     @Override
dump(PrintWriter pw, String prefix, boolean dumpAll)286     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
287         super.dump(pw, prefix, dumpAll);
288         if (mIgnoreOrientationRequest) {
289             pw.println(prefix + "mIgnoreOrientationRequest=true");
290         }
291         if (hasRequestedOverrideConfiguration()) {
292             pw.println(prefix + "overrideConfig=" + getRequestedOverrideConfiguration());
293         }
294     }
295 
dumpChildDisplayArea(PrintWriter pw, String prefix, boolean dumpAll)296     void dumpChildDisplayArea(PrintWriter pw, String prefix, boolean dumpAll) {
297         final String doublePrefix = prefix + "  ";
298         for (int i = getChildCount() - 1; i >= 0; i--) {
299             final DisplayArea<?> childArea = getChildAt(i).asDisplayArea();
300             if (childArea == null) {
301                 continue;
302             }
303             pw.println(prefix + "* " + childArea.getName());
304             if (childArea.isTaskDisplayArea()) {
305                 // TaskDisplayArea can only contain task. And it is already printed by display.
306                 continue;
307             }
308             childArea.dump(pw, doublePrefix, dumpAll);
309             childArea.dumpChildDisplayArea(pw, doublePrefix, dumpAll);
310         }
311     }
312 
313     @Override
getProtoFieldId()314     long getProtoFieldId() {
315         return DISPLAY_AREA;
316     }
317 
318     @Override
asDisplayArea()319     final DisplayArea asDisplayArea() {
320         return this;
321     }
322 
323     /** Cheap way of doing cast and instanceof. */
asTokens()324     DisplayArea.Tokens asTokens() {
325         return null;
326     }
327 
328     @Override
forAllDisplayAreas(Consumer<DisplayArea> callback)329     void forAllDisplayAreas(Consumer<DisplayArea> callback) {
330         super.forAllDisplayAreas(callback);
331         callback.accept(this);
332     }
333 
334     @Override
forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback, boolean traverseTopToBottom)335     boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
336             boolean traverseTopToBottom) {
337         // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
338         if (mType != DisplayArea.Type.ANY) {
339             return false;
340         }
341 
342         int childCount = mChildren.size();
343         int i = traverseTopToBottom ? childCount - 1 : 0;
344         while (i >= 0 && i < childCount) {
345             T child = mChildren.get(i);
346             // Only traverse if the child is a DisplayArea.
347             if (child.asDisplayArea() != null && child.asDisplayArea()
348                     .forAllTaskDisplayAreas(callback, traverseTopToBottom)) {
349                 return true;
350             }
351             i += traverseTopToBottom ? -1 : 1;
352         }
353         return false;
354     }
355 
356     @Override
forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom)357     void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) {
358         // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
359         if (mType != DisplayArea.Type.ANY) {
360             return;
361         }
362 
363         int childCount = mChildren.size();
364         int i = traverseTopToBottom ? childCount - 1 : 0;
365         while (i >= 0 && i < childCount) {
366             T child = mChildren.get(i);
367             // Only traverse if the child is a DisplayArea.
368             if (child.asDisplayArea() != null) {
369                 child.asDisplayArea().forAllTaskDisplayAreas(callback, traverseTopToBottom);
370             }
371             i += traverseTopToBottom ? -1 : 1;
372         }
373     }
374 
375     @Nullable
376     @Override
reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator, @Nullable R initValue, boolean traverseTopToBottom)377     <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
378             @Nullable R initValue, boolean traverseTopToBottom) {
379         // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
380         if (mType != DisplayArea.Type.ANY) {
381             return initValue;
382         }
383 
384         int childCount = mChildren.size();
385         int i = traverseTopToBottom ? childCount - 1 : 0;
386         R result = initValue;
387         while (i >= 0 && i < childCount) {
388             T child = mChildren.get(i);
389             // Only traverse if the child is a DisplayArea.
390             if (child.asDisplayArea() != null) {
391                 result = (R) child.asDisplayArea()
392                         .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom);
393             }
394             i += traverseTopToBottom ? -1 : 1;
395         }
396         return result;
397     }
398 
399     @Nullable
400     @Override
getItemFromDisplayAreas(Function<DisplayArea, R> callback)401     <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) {
402         final R item = super.getItemFromDisplayAreas(callback);
403         return item != null ? item : callback.apply(this);
404     }
405 
406     @Nullable
407     @Override
getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean traverseTopToBottom)408     <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
409             boolean traverseTopToBottom) {
410         // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
411         if (mType != DisplayArea.Type.ANY) {
412             return null;
413         }
414 
415         int childCount = mChildren.size();
416         int i = traverseTopToBottom ? childCount - 1 : 0;
417         while (i >= 0 && i < childCount) {
418             T child = mChildren.get(i);
419             // Only traverse if the child is a DisplayArea.
420             if (child.asDisplayArea() != null) {
421                 R result = (R) child.asDisplayArea()
422                         .getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
423                 if (result != null) {
424                     return result;
425                 }
426             }
427             i += traverseTopToBottom ? -1 : 1;
428         }
429         return null;
430     }
431 
setOrganizer(IDisplayAreaOrganizer organizer)432     void setOrganizer(IDisplayAreaOrganizer organizer) {
433         setOrganizer(organizer, false /* skipDisplayAreaAppeared */);
434     }
435 
setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared)436     void setOrganizer(IDisplayAreaOrganizer organizer, boolean skipDisplayAreaAppeared) {
437         if (mOrganizer == organizer) return;
438         if (mDisplayContent == null || !mDisplayContent.isTrusted()) {
439             throw new IllegalStateException(
440                     "Don't organize or trigger events for unavailable or untrusted display.");
441         }
442         IDisplayAreaOrganizer lastOrganizer = mOrganizer;
443         // Update the new display area organizer before calling sendDisplayAreaVanished since it
444         // could result in a new SurfaceControl getting created that would notify the old organizer
445         // about it.
446         mOrganizer = organizer;
447         sendDisplayAreaVanished(lastOrganizer);
448         if (!skipDisplayAreaAppeared) {
449             sendDisplayAreaAppeared();
450         }
451     }
452 
sendDisplayAreaAppeared()453     void sendDisplayAreaAppeared() {
454         if (mOrganizer == null) return;
455         mOrganizerController.onDisplayAreaAppeared(mOrganizer, this);
456     }
457 
sendDisplayAreaVanished(IDisplayAreaOrganizer organizer)458     void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) {
459         if (organizer == null) return;
460         migrateToNewSurfaceControl(getSyncTransaction());
461         mOrganizerController.onDisplayAreaVanished(organizer, this);
462     }
463 
464     @Override
onConfigurationChanged(Configuration newParentConfig)465     public void onConfigurationChanged(Configuration newParentConfig) {
466         mTmpConfiguration.setTo(getConfiguration());
467         super.onConfigurationChanged(newParentConfig);
468 
469         if (mOrganizer != null && getConfiguration().diff(mTmpConfiguration) != 0) {
470             mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this);
471         }
472     }
473 
474     @Override
resolveOverrideConfiguration(Configuration newParentConfiguration)475     void resolveOverrideConfiguration(Configuration newParentConfiguration) {
476         super.resolveOverrideConfiguration(newParentConfiguration);
477         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
478         final Rect overrideBounds = resolvedConfig.windowConfiguration.getBounds();
479         final Rect overrideAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
480         final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
481 
482         // If there is no override of appBounds, restrict appBounds to the override bounds.
483         if (!overrideBounds.isEmpty() && (overrideAppBounds == null || overrideAppBounds.isEmpty())
484                 && parentAppBounds != null && !parentAppBounds.isEmpty()) {
485             final Rect appBounds = new Rect(overrideBounds);
486             appBounds.intersect(parentAppBounds);
487             resolvedConfig.windowConfiguration.setAppBounds(appBounds);
488         }
489     }
490 
491     @Override
isOrganized()492     boolean isOrganized() {
493         return mOrganizer != null;
494     }
495 
496 
getDisplayAreaInfo()497     DisplayAreaInfo getDisplayAreaInfo() {
498         final DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
499                 getDisplayContent().getDisplayId(), mFeatureId);
500         final RootDisplayArea root = getRootDisplayArea();
501         info.rootDisplayAreaId = root == null ? getDisplayContent().mFeatureId : root.mFeatureId;
502         info.configuration.setTo(getConfiguration());
503         return info;
504     }
505 
506     /**
507      * Gets the stable bounds of the DisplayArea, which is the bounds excluding insets for
508      * navigation bar, cutout, and status bar.
509      */
getStableRect(Rect out)510     void getStableRect(Rect out) {
511         if (mDisplayContent == null) {
512             getBounds(out);
513             return;
514         }
515 
516         // Intersect with the display stable bounds to get the DisplayArea stable bounds.
517         mDisplayContent.getStableRect(out);
518         out.intersect(getBounds());
519     }
520 
521     @Override
providesMaxBounds()522     public boolean providesMaxBounds() {
523         return true;
524     }
525 
isTaskDisplayArea()526     boolean isTaskDisplayArea() {
527         return false;
528     }
529 
530     @Override
removeImmediately()531     void removeImmediately() {
532         setOrganizer(null);
533         super.removeImmediately();
534     }
535 
536     @Override
getDisplayArea()537     DisplayArea getDisplayArea() {
538         return this;
539     }
540 
541     /**
542      * DisplayArea that contains WindowTokens, and orders them according to their type.
543      */
544     public static class Tokens extends DisplayArea<WindowToken> {
545         int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
546 
547         private final Comparator<WindowToken> mWindowComparator =
548                 Comparator.comparingInt(WindowToken::getWindowLayerFromType);
549 
550         private final Predicate<WindowState> mGetOrientingWindow = w -> {
551             if (!w.isVisible() || !w.mLegacyPolicyVisibilityAfterAnim) {
552                 return false;
553             }
554             final WindowManagerPolicy policy = mWmService.mPolicy;
555             if (policy.isKeyguardHostWindow(w.mAttrs)) {
556                 // Ignore the orientation of keyguard if it is going away or is not showing while
557                 // the device is fully awake. In other words, use the orientation of keyguard if
558                 // its window is visible while the device is going to sleep or is sleeping.
559                 if (!mWmService.mAtmService.isKeyguardLocked()
560                         && mDisplayContent.getDisplayPolicy().isAwake()
561                         // Device is not going to sleep.
562                         && policy.okToAnimate(true /* ignoreScreenOn */)) {
563                     return false;
564                 }
565                 // Consider unoccluding only when all unknown visibilities have been
566                 // resolved, as otherwise we just may be starting another occluding activity.
567                 final boolean isUnoccluding =
568                         mDisplayContent.mAppTransition.isUnoccluding()
569                                 && mDisplayContent.mUnknownAppVisibilityController.allResolved();
570                 // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
571                 // even if SystemUI hasn't updated the attrs yet.
572                 if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
573                     return true;
574                 }
575             }
576             final int req = w.mAttrs.screenOrientation;
577             if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
578                     || req == SCREEN_ORIENTATION_UNSET) {
579                 return false;
580             }
581             return true;
582         };
583 
Tokens(WindowManagerService wms, Type type, String name)584         Tokens(WindowManagerService wms, Type type, String name) {
585             super(wms, type, name, FEATURE_WINDOW_TOKENS);
586         }
587 
addChild(WindowToken token)588         void addChild(WindowToken token) {
589             addChild(token, mWindowComparator);
590         }
591 
592         @Override
getOrientation(int candidate)593         int getOrientation(int candidate) {
594             mLastOrientationSource = null;
595             if (mIgnoreOrientationRequest) {
596                 return SCREEN_ORIENTATION_UNSET;
597             }
598 
599             // Find a window requesting orientation.
600             final WindowState win = getWindow(mGetOrientingWindow);
601 
602             if (win == null) {
603                 return candidate;
604             }
605             int req = win.mAttrs.screenOrientation;
606             ProtoLog.v(WM_DEBUG_ORIENTATION, "%s forcing orientation to %d for display id=%d",
607                     win, req, mDisplayContent.getDisplayId());
608             if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
609                 // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
610                 // stale. We record / use the last known override.
611                 if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
612                     mLastKeyguardForcedOrientation = req;
613                 } else {
614                     req = mLastKeyguardForcedOrientation;
615                 }
616             }
617             mLastOrientationSource = win;
618             return req;
619         }
620 
621         @Override
asTokens()622         final DisplayArea.Tokens asTokens() {
623             return this;
624         }
625     }
626 
627     /**
628      * DisplayArea that can be dimmed.
629      */
630     static class Dimmable extends DisplayArea<DisplayArea> {
631         private final Dimmer mDimmer = new Dimmer(this);
632         private final Rect mTmpDimBoundsRect = new Rect();
633 
Dimmable(WindowManagerService wms, Type type, String name, int featureId)634         Dimmable(WindowManagerService wms, Type type, String name, int featureId) {
635             super(wms, type, name, featureId);
636         }
637 
638         @Override
getDimmer()639         Dimmer getDimmer() {
640             return mDimmer;
641         }
642 
643         @Override
prepareSurfaces()644         void prepareSurfaces() {
645             mDimmer.resetDimStates();
646             super.prepareSurfaces();
647             getBounds(mTmpDimBoundsRect);
648 
649             // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
650             // on the display level fades out.
651             if (forAllTasks(task -> !task.canAffectSystemUiFlags())) {
652                 mDimmer.resetDimStates();
653             }
654 
655             if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
656                 scheduleAnimation();
657             }
658         }
659     }
660 
661     enum Type {
662         /** Can only contain WindowTokens above the APPLICATION_LAYER. */
663         ABOVE_TASKS,
664         /** Can only contain WindowTokens below the APPLICATION_LAYER. */
665         BELOW_TASKS,
666         /** Can contain anything. */
667         ANY;
668 
checkSiblings(Type bottom, Type top)669         static void checkSiblings(Type bottom, Type top) {
670             checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
671                     bottom + " must be above BELOW_TASKS");
672             checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
673                     top + " must be below ABOVE_TASKS");
674         }
675 
checkChild(Type parent, Type child)676         static void checkChild(Type parent, Type child) {
677             switch (parent) {
678                 case ABOVE_TASKS:
679                     checkState(child == ABOVE_TASKS, "ABOVE_TASKS can only contain ABOVE_TASKS");
680                     break;
681                 case BELOW_TASKS:
682                     checkState(child == BELOW_TASKS, "BELOW_TASKS can only contain BELOW_TASKS");
683                     break;
684             }
685         }
686 
typeOf(WindowContainer c)687         static Type typeOf(WindowContainer c) {
688             if (c.asDisplayArea() != null) {
689                 return ((DisplayArea) c).mType;
690             } else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) {
691                 return typeOf((WindowToken) c);
692             } else if (c instanceof Task) {
693                 return ANY;
694             } else {
695                 throw new IllegalArgumentException("Unknown container: " + c);
696             }
697         }
698 
typeOf(WindowToken c)699         private static Type typeOf(WindowToken c) {
700             return c.getWindowLayerFromType() < APPLICATION_LAYER ? BELOW_TASKS : ABOVE_TASKS;
701         }
702     }
703 }
704