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