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