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