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