1 /*
2  * Copyright (C) 2017 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 com.android.server.wm.WindowManagerDebugConfig.DEBUG;
20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22 import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
23 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
24 
25 import android.os.Debug;
26 import android.util.Slog;
27 
28 import java.io.PrintWriter;
29 
30 /**
31  * Positions windows and their surfaces.
32  *
33  * It sets positions of windows by calculating their frames and then applies this by positioning
34  * surfaces according to these frames. Z layer is still assigned withing WindowManagerService.
35  */
36 class WindowSurfacePlacer {
37     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM;
38     private final WindowManagerService mService;
39 
40     private boolean mInLayout = false;
41 
42     /** Only do a maximum of 6 repeated layouts. After that quit */
43     private int mLayoutRepeatCount;
44 
45     static final int SET_UPDATE_ROTATION                = 1 << 0;
46     static final int SET_ORIENTATION_CHANGE_COMPLETE    = 1 << 2;
47     static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 3;
48 
49     private boolean mTraversalScheduled;
50     private int mDeferDepth = 0;
51     /** The number of layout requests when deferring. */
52     private int mDeferredRequests;
53 
54     private class Traverser implements Runnable {
55         @Override
run()56         public void run() {
57             synchronized (mService.mGlobalLock) {
58                 performSurfacePlacement();
59             }
60         }
61     }
62 
63     private final Traverser mPerformSurfacePlacement = new Traverser();
64 
WindowSurfacePlacer(WindowManagerService service)65     WindowSurfacePlacer(WindowManagerService service) {
66         mService = service;
67     }
68 
69     /**
70      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
71      * performance, only one layout pass should be done. This can be called multiple times, and
72      * layouting will be resumed once the last caller has called {@link #continueLayout}.
73      */
deferLayout()74     void deferLayout() {
75         mDeferDepth++;
76     }
77 
78     /**
79      * Resumes layout passes after deferring them. If there is a deferred direct invocation of
80      * {@link #performSurfacePlacement} ({@link #mDeferredRequests} > 0), when the defer is
81      * done, it will continue to perform layout.
82      *
83      * @param hasChanges Something has changed. That means whether to call
84      *                   {@link #performSurfacePlacement} when {@link #mDeferDepth} becomes zero.
85      * @see #deferLayout
86      */
continueLayout(boolean hasChanges)87     void continueLayout(boolean hasChanges) {
88         mDeferDepth--;
89         if (mDeferDepth > 0) {
90             return;
91         }
92 
93         if (hasChanges || mDeferredRequests > 0) {
94             if (DEBUG) {
95                 Slog.i(TAG, "continueLayout hasChanges=" + hasChanges
96                         + " deferredRequests=" + mDeferredRequests + " " + Debug.getCallers(2, 3));
97             }
98             performSurfacePlacement();
99             mDeferredRequests = 0;
100         } else if (DEBUG) {
101             Slog.i(TAG, "Cancel continueLayout " + Debug.getCallers(2, 3));
102         }
103     }
104 
isLayoutDeferred()105     boolean isLayoutDeferred() {
106         return mDeferDepth > 0;
107     }
108 
performSurfacePlacementIfScheduled()109     void performSurfacePlacementIfScheduled() {
110         if (mTraversalScheduled) {
111             performSurfacePlacement();
112         }
113     }
114 
performSurfacePlacement()115     final void performSurfacePlacement() {
116         performSurfacePlacement(false /* force */);
117     }
118 
performSurfacePlacement(boolean force)119     final void performSurfacePlacement(boolean force) {
120         if (mDeferDepth > 0 && !force) {
121             mDeferredRequests++;
122             return;
123         }
124         int loopCount = 6;
125         do {
126             mTraversalScheduled = false;
127             performSurfacePlacementLoop();
128             mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
129             loopCount--;
130         } while (mTraversalScheduled && loopCount > 0);
131         mService.mRoot.mWallpaperActionPending = false;
132     }
133 
performSurfacePlacementLoop()134     private void performSurfacePlacementLoop() {
135         if (mInLayout) {
136             if (DEBUG) {
137                 throw new RuntimeException("Recursive call!");
138             }
139             Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
140                     + Debug.getCallers(3));
141             return;
142         }
143 
144         // TODO(multi-display):
145         final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
146         if (defaultDisplay.mWaitingForConfig) {
147             // Our configuration has changed (most likely rotation), but we
148             // don't yet have the complete configuration to report to
149             // applications.  Don't do any window layout until we have it.
150             return;
151         }
152 
153         if (!mService.mDisplayReady) {
154             // Not yet initialized, nothing to do.
155             return;
156         }
157 
158         mInLayout = true;
159 
160         if (!mService.mForceRemoves.isEmpty()) {
161             // Wait a little bit for things to settle down, and off we go.
162             while (!mService.mForceRemoves.isEmpty()) {
163                 final WindowState ws = mService.mForceRemoves.remove(0);
164                 Slog.i(TAG, "Force removing: " + ws);
165                 ws.removeImmediately();
166             }
167             Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
168             Object tmp = new Object();
169             synchronized (tmp) {
170                 try {
171                     tmp.wait(250);
172                 } catch (InterruptedException e) {
173                 }
174             }
175         }
176 
177         try {
178             mService.mRoot.performSurfacePlacement();
179 
180             mInLayout = false;
181 
182             if (mService.mRoot.isLayoutNeeded()) {
183                 if (++mLayoutRepeatCount < 6) {
184                     requestTraversal();
185                 } else {
186                     Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
187                     mLayoutRepeatCount = 0;
188                 }
189             } else {
190                 mLayoutRepeatCount = 0;
191             }
192 
193             if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
194                 mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
195                 mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
196             }
197         } catch (RuntimeException e) {
198             mInLayout = false;
199             Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
200         }
201     }
202 
debugLayoutRepeats(final String msg, int pendingLayoutChanges)203     void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
204         if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
205             Slog.v(TAG, "Layouts looping: " + msg +
206                     ", mPendingLayoutChanges = 0x" + Integer.toHexString(pendingLayoutChanges));
207         }
208     }
209 
isInLayout()210     boolean isInLayout() {
211         return mInLayout;
212     }
213 
requestTraversal()214     void requestTraversal() {
215         if (mTraversalScheduled) {
216             return;
217         }
218 
219         // Set as scheduled even the request will be deferred because mDeferredRequests is also
220         // increased, then the end of deferring will perform the request.
221         mTraversalScheduled = true;
222         if (mDeferDepth > 0) {
223             mDeferredRequests++;
224             if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
225             return;
226         }
227         mService.mAnimationHandler.post(mPerformSurfacePlacement);
228     }
229 
dump(PrintWriter pw, String prefix)230     public void dump(PrintWriter pw, String prefix) {
231         pw.println(prefix + "mTraversalScheduled=" + mTraversalScheduled);
232         pw.println(prefix + "mHoldScreenWindow=" + mService.mRoot.mHoldScreenWindow);
233         pw.println(prefix + "mObscuringWindow=" + mService.mRoot.mObscuringWindow);
234     }
235 }
236