| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.server.wm; |
| |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; |
| import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE; |
| import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; |
| |
| import android.os.Debug; |
| import android.os.Trace; |
| import android.util.Slog; |
| import android.util.SparseIntArray; |
| |
| import java.io.PrintWriter; |
| |
| /** |
| * Positions windows and their surfaces. |
| * |
| * It sets positions of windows by calculating their frames and then applies this by positioning |
| * surfaces according to these frames. Z layer is still assigned withing WindowManagerService. |
| */ |
| class WindowSurfacePlacer { |
| private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM; |
| private final WindowManagerService mService; |
| |
| private boolean mInLayout = false; |
| |
| /** Only do a maximum of 6 repeated layouts. After that quit */ |
| private int mLayoutRepeatCount; |
| |
| static final int SET_UPDATE_ROTATION = 1 << 0; |
| static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 2; |
| static final int SET_WALLPAPER_ACTION_PENDING = 1 << 3; |
| |
| private boolean mTraversalScheduled; |
| private int mDeferDepth = 0; |
| |
| private final SparseIntArray mTempTransitionReasons = new SparseIntArray(); |
| |
| private final Runnable mPerformSurfacePlacement; |
| |
| public WindowSurfacePlacer(WindowManagerService service) { |
| mService = service; |
| mPerformSurfacePlacement = () -> { |
| synchronized (mService.mGlobalLock) { |
| performSurfacePlacement(); |
| } |
| }; |
| } |
| |
| /** |
| * See {@link WindowManagerService#deferSurfaceLayout()} |
| */ |
| void deferLayout() { |
| mDeferDepth++; |
| } |
| |
| /** |
| * See {@link WindowManagerService#continueSurfaceLayout()} |
| */ |
| void continueLayout() { |
| mDeferDepth--; |
| if (mDeferDepth <= 0) { |
| performSurfacePlacement(); |
| } |
| } |
| |
| boolean isLayoutDeferred() { |
| return mDeferDepth > 0; |
| } |
| |
| void performSurfacePlacementIfScheduled() { |
| if (mTraversalScheduled) { |
| performSurfacePlacement(); |
| } |
| } |
| |
| final void performSurfacePlacement() { |
| performSurfacePlacement(false /* force */); |
| } |
| |
| final void performSurfacePlacement(boolean force) { |
| if (mDeferDepth > 0 && !force) { |
| return; |
| } |
| int loopCount = 6; |
| do { |
| mTraversalScheduled = false; |
| performSurfacePlacementLoop(); |
| mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement); |
| loopCount--; |
| } while (mTraversalScheduled && loopCount > 0); |
| mService.mRoot.mWallpaperActionPending = false; |
| } |
| |
| private void performSurfacePlacementLoop() { |
| if (mInLayout) { |
| if (DEBUG) { |
| throw new RuntimeException("Recursive call!"); |
| } |
| Slog.w(TAG, "performLayoutAndPlaceSurfacesLocked called while in layout. Callers=" |
| + Debug.getCallers(3)); |
| return; |
| } |
| |
| // TODO(multi-display): |
| final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked(); |
| if (defaultDisplay.mWaitingForConfig) { |
| // Our configuration has changed (most likely rotation), but we |
| // don't yet have the complete configuration to report to |
| // applications. Don't do any window layout until we have it. |
| return; |
| } |
| |
| if (!mService.mDisplayReady) { |
| // Not yet initialized, nothing to do. |
| return; |
| } |
| |
| Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout"); |
| mInLayout = true; |
| |
| boolean recoveringMemory = false; |
| if (!mService.mForceRemoves.isEmpty()) { |
| recoveringMemory = true; |
| // Wait a little bit for things to settle down, and off we go. |
| while (!mService.mForceRemoves.isEmpty()) { |
| final WindowState ws = mService.mForceRemoves.remove(0); |
| Slog.i(TAG, "Force removing: " + ws); |
| ws.removeImmediately(); |
| } |
| Slog.w(TAG, "Due to memory failure, waiting a bit for next layout"); |
| Object tmp = new Object(); |
| synchronized (tmp) { |
| try { |
| tmp.wait(250); |
| } catch (InterruptedException e) { |
| } |
| } |
| } |
| |
| try { |
| mService.mRoot.performSurfacePlacement(recoveringMemory); |
| |
| mInLayout = false; |
| |
| if (mService.mRoot.isLayoutNeeded()) { |
| if (++mLayoutRepeatCount < 6) { |
| requestTraversal(); |
| } else { |
| Slog.e(TAG, "Performed 6 layouts in a row. Skipping"); |
| mLayoutRepeatCount = 0; |
| } |
| } else { |
| mLayoutRepeatCount = 0; |
| } |
| |
| if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) { |
| mService.mH.removeMessages(REPORT_WINDOWS_CHANGE); |
| mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE); |
| } |
| } catch (RuntimeException e) { |
| mInLayout = false; |
| Slog.wtf(TAG, "Unhandled exception while laying out windows", e); |
| } |
| |
| Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); |
| } |
| |
| void debugLayoutRepeats(final String msg, int pendingLayoutChanges) { |
| if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) { |
| Slog.v(TAG, "Layouts looping: " + msg + |
| ", mPendingLayoutChanges = 0x" + Integer.toHexString(pendingLayoutChanges)); |
| } |
| } |
| |
| boolean isInLayout() { |
| return mInLayout; |
| } |
| |
| void requestTraversal() { |
| if (!mTraversalScheduled) { |
| mTraversalScheduled = true; |
| mService.mAnimationHandler.post(mPerformSurfacePlacement); |
| } |
| } |
| |
| public void dump(PrintWriter pw, String prefix) { |
| pw.println(prefix + "mTraversalScheduled=" + mTraversalScheduled); |
| pw.println(prefix + "mHoldScreenWindow=" + mService.mRoot.mHoldScreenWindow); |
| pw.println(prefix + "mObscuringWindow=" + mService.mRoot.mObscuringWindow); |
| } |
| } |