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.os.Trace;
27 import android.util.Slog;
28 import android.util.SparseIntArray;
29 
30 import java.io.PrintWriter;
31 
32 /**
33  * Positions windows and their surfaces.
34  *
35  * It sets positions of windows by calculating their frames and then applies this by positioning
36  * surfaces according to these frames. Z layer is still assigned withing WindowManagerService.
37  */
38 class WindowSurfacePlacer {
39     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM;
40     private final WindowManagerService mService;
41 
42     private boolean mInLayout = false;
43 
44     /** Only do a maximum of 6 repeated layouts. After that quit */
45     private int mLayoutRepeatCount;
46 
47     static final int SET_UPDATE_ROTATION                = 1 << 0;
48     static final int SET_ORIENTATION_CHANGE_COMPLETE    = 1 << 2;
49     static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 3;
50 
51     private boolean mTraversalScheduled;
52     private int mDeferDepth = 0;
53 
54     private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
55 
56     private final Runnable mPerformSurfacePlacement;
57 
WindowSurfacePlacer(WindowManagerService service)58     public WindowSurfacePlacer(WindowManagerService service) {
59         mService = service;
60         mPerformSurfacePlacement = () -> {
61             synchronized (mService.mGlobalLock) {
62                 performSurfacePlacement();
63             }
64         };
65     }
66 
67     /**
68      * See {@link WindowManagerService#deferSurfaceLayout()}
69      */
deferLayout()70     void deferLayout() {
71         mDeferDepth++;
72     }
73 
74     /**
75      * See {@link WindowManagerService#continueSurfaceLayout()}
76      */
continueLayout()77     void continueLayout() {
78         mDeferDepth--;
79         if (mDeferDepth <= 0) {
80             performSurfacePlacement();
81         }
82     }
83 
isLayoutDeferred()84     boolean isLayoutDeferred() {
85         return mDeferDepth > 0;
86     }
87 
performSurfacePlacementIfScheduled()88     void performSurfacePlacementIfScheduled() {
89         if (mTraversalScheduled) {
90             performSurfacePlacement();
91         }
92     }
93 
performSurfacePlacement()94     final void performSurfacePlacement() {
95         performSurfacePlacement(false /* force */);
96     }
97 
performSurfacePlacement(boolean force)98     final void performSurfacePlacement(boolean force) {
99         if (mDeferDepth > 0 && !force) {
100             return;
101         }
102         int loopCount = 6;
103         do {
104             mTraversalScheduled = false;
105             performSurfacePlacementLoop();
106             mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
107             loopCount--;
108         } while (mTraversalScheduled && loopCount > 0);
109         mService.mRoot.mWallpaperActionPending = false;
110     }
111 
performSurfacePlacementLoop()112     private void performSurfacePlacementLoop() {
113         if (mInLayout) {
114             if (DEBUG) {
115                 throw new RuntimeException("Recursive call!");
116             }
117             Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers="
118                     + Debug.getCallers(3));
119             return;
120         }
121 
122         // TODO(multi-display):
123         final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
124         if (defaultDisplay.mWaitingForConfig) {
125             // Our configuration has changed (most likely rotation), but we
126             // don't yet have the complete configuration to report to
127             // applications.  Don't do any window layout until we have it.
128             return;
129         }
130 
131         if (!mService.mDisplayReady) {
132             // Not yet initialized, nothing to do.
133             return;
134         }
135 
136         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
137         mInLayout = true;
138 
139         boolean recoveringMemory = false;
140         if (!mService.mForceRemoves.isEmpty()) {
141             recoveringMemory = true;
142             // Wait a little bit for things to settle down, and off we go.
143             while (!mService.mForceRemoves.isEmpty()) {
144                 final WindowState ws = mService.mForceRemoves.remove(0);
145                 Slog.i(TAG, "Force removing: " + ws);
146                 ws.removeImmediately();
147             }
148             Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
149             Object tmp = new Object();
150             synchronized (tmp) {
151                 try {
152                     tmp.wait(250);
153                 } catch (InterruptedException e) {
154                 }
155             }
156         }
157 
158         try {
159             mService.mRoot.performSurfacePlacement(recoveringMemory);
160 
161             mInLayout = false;
162 
163             if (mService.mRoot.isLayoutNeeded()) {
164                 if (++mLayoutRepeatCount < 6) {
165                     requestTraversal();
166                 } else {
167                     Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
168                     mLayoutRepeatCount = 0;
169                 }
170             } else {
171                 mLayoutRepeatCount = 0;
172             }
173 
174             if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
175                 mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
176                 mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
177             }
178         } catch (RuntimeException e) {
179             mInLayout = false;
180             Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
181         }
182 
183         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
184     }
185 
debugLayoutRepeats(final String msg, int pendingLayoutChanges)186     void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
187         if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
188             Slog.v(TAG, "Layouts looping: " + msg +
189                     ", mPendingLayoutChanges = 0x" + Integer.toHexString(pendingLayoutChanges));
190         }
191     }
192 
isInLayout()193     boolean isInLayout() {
194         return mInLayout;
195     }
196 
requestTraversal()197     void requestTraversal() {
198         if (!mTraversalScheduled) {
199             mTraversalScheduled = true;
200             mService.mAnimationHandler.post(mPerformSurfacePlacement);
201         }
202     }
203 
dump(PrintWriter pw, String prefix)204     public void dump(PrintWriter pw, String prefix) {
205         pw.println(prefix + "mTraversalScheduled=" + mTraversalScheduled);
206         pw.println(prefix + "mHoldScreenWindow=" + mService.mRoot.mHoldScreenWindow);
207         pw.println(prefix + "mObscuringWindow=" + mService.mRoot.mObscuringWindow);
208     }
209 }
210