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