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.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
23 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
24 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
25 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
26 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
27 
28 import static com.android.internal.util.Preconditions.checkState;
29 import static com.android.server.wm.DisplayAreaProto.NAME;
30 import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
31 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
32 import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
33 
34 import android.content.res.Configuration;
35 import android.graphics.Rect;
36 import android.util.proto.ProtoOutputStream;
37 import android.window.DisplayAreaInfo;
38 import android.window.IDisplayAreaOrganizer;
39 
40 import com.android.server.policy.WindowManagerPolicy;
41 import com.android.server.protolog.common.ProtoLog;
42 
43 import java.util.Comparator;
44 import java.util.function.Consumer;
45 import java.util.function.Predicate;
46 
47 /**
48  * Container for grouping WindowContainer below DisplayContent.
49  *
50  * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and
51  * can be leashed.
52  *
53  * DisplayAreas can contain nested DisplayAreas.
54  *
55  * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order:
56  * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
57  * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
58  * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
59  *
60  * @param <T> type of the children of the DisplayArea.
61  */
62 public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
63 
64     protected final Type mType;
65     private final String mName;
66     final int mFeatureId;
67     private final DisplayAreaOrganizerController mOrganizerController;
68     IDisplayAreaOrganizer mOrganizer;
69     private final Configuration mTmpConfiguration = new Configuration();
70 
DisplayArea(WindowManagerService wms, Type type, String name)71     DisplayArea(WindowManagerService wms, Type type, String name) {
72         this(wms, type, name, FEATURE_UNDEFINED);
73     }
74 
DisplayArea(WindowManagerService wms, Type type, String name, int featureId)75     DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
76         super(wms);
77         // TODO(display-area): move this up to ConfigurationContainer
78         mOrientation = SCREEN_ORIENTATION_UNSET;
79         mType = type;
80         mName = name;
81         mFeatureId = featureId;
82         mRemoteToken = new RemoteToken(this);
83         mOrganizerController =
84                 wms.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
85     }
86 
87     @Override
onChildPositionChanged(WindowContainer child)88     void onChildPositionChanged(WindowContainer child) {
89         super.onChildPositionChanged(child);
90 
91         // Verify that we have proper ordering
92         Type.checkChild(mType, Type.typeOf(child));
93 
94         if (child instanceof ActivityStack) {
95             // TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings.
96             //                     They might need a separate type.
97             return;
98         }
99 
100         for (int i = 1; i < getChildCount(); i++) {
101             final WindowContainer top = getChildAt(i - 1);
102             final WindowContainer bottom = getChildAt(i);
103             if (child == top || child == bottom) {
104                 Type.checkSiblings(Type.typeOf(top), Type.typeOf(bottom));
105             }
106         }
107     }
108 
109     @Override
needsZBoost()110     boolean needsZBoost() {
111         // Z Boost should only happen at or below the ActivityStack level.
112         return false;
113     }
114 
115     @Override
fillsParent()116     boolean fillsParent() {
117         return true;
118     }
119 
120     @Override
getName()121     String getName() {
122         return mName;
123     }
124 
125     @Override
toString()126     public String toString() {
127         return mName + "@" + System.identityHashCode(this);
128     }
129 
130     @Override
dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel)131     public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
132         final long token = proto.start(fieldId);
133         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
134         proto.write(NAME, mName);
135         proto.end(token);
136     }
137 
138     @Override
getProtoFieldId()139     long getProtoFieldId() {
140         return DISPLAY_AREA;
141     }
142 
forAllDisplayAreas(Consumer<DisplayArea> callback)143     void forAllDisplayAreas(Consumer<DisplayArea> callback) {
144         super.forAllDisplayAreas(callback);
145         callback.accept(this);
146     }
147 
setOrganizer(IDisplayAreaOrganizer organizer)148     void setOrganizer(IDisplayAreaOrganizer organizer) {
149         if (mOrganizer == organizer) return;
150         IDisplayAreaOrganizer lastOrganizer = mOrganizer;
151         // Update the new display area organizer before calling sendDisplayAreaVanished since it
152         // could result in a new SurfaceControl getting created that would notify the old organizer
153         // about it.
154         mOrganizer = organizer;
155         sendDisplayAreaVanished(lastOrganizer);
156         sendDisplayAreaAppeared();
157     }
158 
sendDisplayAreaAppeared()159     void sendDisplayAreaAppeared() {
160         if (mOrganizer == null) return;
161         mOrganizerController.onDisplayAreaAppeared(mOrganizer, this);
162     }
163 
sendDisplayAreaVanished(IDisplayAreaOrganizer organizer)164     void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) {
165         if (organizer == null) return;
166         migrateToNewSurfaceControl();
167         mOrganizerController.onDisplayAreaVanished(organizer, this);
168     }
169 
170     @Override
onConfigurationChanged(Configuration newParentConfig)171     public void onConfigurationChanged(Configuration newParentConfig) {
172         mTmpConfiguration.setTo(getConfiguration());
173         super.onConfigurationChanged(newParentConfig);
174 
175         if (mOrganizer != null && getConfiguration().diff(mTmpConfiguration) != 0) {
176             mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this);
177         }
178     }
179 
180     @Override
isOrganized()181     boolean isOrganized() {
182         return mOrganizer != null;
183     }
184 
185 
getDisplayAreaInfo()186     DisplayAreaInfo getDisplayAreaInfo() {
187         DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
188                 getDisplayContent().getDisplayId(), mFeatureId);
189         info.configuration.setTo(getConfiguration());
190         return info;
191     }
192 
193     /**
194      * DisplayArea that contains WindowTokens, and orders them according to their type.
195      */
196     public static class Tokens extends DisplayArea<WindowToken> {
197         int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
198 
199         private final Comparator<WindowToken> mWindowComparator =
200                 Comparator.comparingInt(WindowToken::getWindowLayerFromType);
201 
202         private final Predicate<WindowState> mGetOrientingWindow = w -> {
203             final WindowManagerPolicy policy = mWmService.mPolicy;
204             if (policy.isKeyguardHostWindow(w.mAttrs)) {
205                 if (mWmService.mKeyguardGoingAway) {
206                     return false;
207                 }
208                 // Consider unoccluding only when all unknown visibilities have been
209                 // resolved, as otherwise we just may be starting another occluding activity.
210                 final boolean isUnoccluding =
211                         mDisplayContent.mAppTransition.getAppTransition()
212                                 == TRANSIT_KEYGUARD_UNOCCLUDE
213                                 && mDisplayContent.mUnknownAppVisibilityController.allResolved();
214                 // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
215                 // even if SystemUI hasn't updated the attrs yet.
216                 if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
217                     return true;
218                 }
219             }
220             final int req = w.mAttrs.screenOrientation;
221             if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
222                     || req == SCREEN_ORIENTATION_UNSET) {
223                 return false;
224             }
225             return true;
226         };
227 
Tokens(WindowManagerService wms, Type type, String name)228         Tokens(WindowManagerService wms, Type type, String name) {
229             super(wms, type, name, FEATURE_WINDOW_TOKENS);
230         }
231 
addChild(WindowToken token)232         void addChild(WindowToken token) {
233             addChild(token, mWindowComparator);
234         }
235 
236         @Override
getOrientation(int candidate)237         int getOrientation(int candidate) {
238             // Find a window requesting orientation.
239             final WindowState win = getWindow(mGetOrientingWindow);
240 
241             if (win == null) {
242                 return candidate;
243             }
244             int req = win.mAttrs.screenOrientation;
245             ProtoLog.v(WM_DEBUG_ORIENTATION, "%s forcing orientation to %d for display id=%d",
246                     win, req, mDisplayContent.getDisplayId());
247             if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
248                 // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
249                 // stale. We record / use the last known override.
250                 if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
251                     mLastKeyguardForcedOrientation = req;
252                 } else {
253                     req = mLastKeyguardForcedOrientation;
254                 }
255             }
256             mLastOrientationSource = win;
257             return req;
258         }
259     }
260 
261     @Override
getDisplayArea()262     DisplayArea getDisplayArea() {
263         return this;
264     }
265 
266     /**
267      * Top-most DisplayArea under DisplayContent.
268      */
269     public static class Root extends DisplayArea<DisplayArea> {
270         private final Dimmer mDimmer = new Dimmer(this);
271         private final Rect mTmpDimBoundsRect = new Rect();
272 
Root(WindowManagerService wms)273         Root(WindowManagerService wms) {
274             super(wms, Type.ANY, "DisplayArea.Root", FEATURE_ROOT);
275         }
276 
277         @Override
getDimmer()278         Dimmer getDimmer() {
279             return mDimmer;
280         }
281 
282         @Override
prepareSurfaces()283         void prepareSurfaces() {
284             mDimmer.resetDimStates();
285             super.prepareSurfaces();
286             getBounds(mTmpDimBoundsRect);
287 
288             // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
289             // on the display level fades out.
290             if (forAllTasks(task -> !task.canAffectSystemUiFlags())) {
291                 mDimmer.resetDimStates();
292             }
293 
294             if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
295                 scheduleAnimation();
296             }
297         }
298     }
299 
300     enum Type {
301         /** Can only contain WindowTokens above the APPLICATION_LAYER. */
302         ABOVE_TASKS,
303         /** Can only contain WindowTokens below the APPLICATION_LAYER. */
304         BELOW_TASKS,
305         /** Can contain anything. */
306         ANY;
307 
checkSiblings(Type bottom, Type top)308         static void checkSiblings(Type bottom, Type top) {
309             checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
310                     bottom + " must be above BELOW_TASKS");
311             checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
312                     top + " must be below ABOVE_TASKS");
313         }
314 
checkChild(Type parent, Type child)315         static void checkChild(Type parent, Type child) {
316             switch (parent) {
317                 case ABOVE_TASKS:
318                     checkState(child == ABOVE_TASKS, "ABOVE_TASKS can only contain ABOVE_TASKS");
319                     break;
320                 case BELOW_TASKS:
321                     checkState(child == BELOW_TASKS, "BELOW_TASKS can only contain BELOW_TASKS");
322                     break;
323             }
324         }
325 
typeOf(WindowContainer c)326         static Type typeOf(WindowContainer c) {
327             if (c instanceof DisplayArea) {
328                 return ((DisplayArea) c).mType;
329             } else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) {
330                 return typeOf((WindowToken) c);
331             } else if (c instanceof ActivityStack) {
332                 return ANY;
333             } else {
334                 throw new IllegalArgumentException("Unknown container: " + c);
335             }
336         }
337 
typeOf(WindowToken c)338         private static Type typeOf(WindowToken c) {
339             return c.getWindowLayerFromType() < APPLICATION_LAYER ? BELOW_TASKS : ABOVE_TASKS;
340         }
341     }
342 }
343