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