/*
|
* 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);
|
}
|
}
|