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