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