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