blob: 072e10fee84d9d23c86d31e7e110af9ddf47ec4b [file] [log] [blame]
/*
* Copyright (C) 2015 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 android.util.Slog;
import android.view.Display;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
/**
* Controller for assigning layers to windows on the display.
*
* This class encapsulates general algorithm for assigning layers and special rules that we need to
* apply on top. The general algorithm goes through windows from bottom to the top and the higher
* the window is, the higher layer is assigned. The final layer is equal to base layer +
* adjustment from the order. This means that the window list is assumed to be ordered roughly by
* the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
* handled with care, because they break the algorithm).
*
* On top of the general algorithm we add special rules, that govern such amazing things as:
* <li>IME (which has higher base layer, but will be positioned above application windows)</li>
* <li>docked/pinned windows (that need to be lifted above other application windows, including
* animations)
* <li>dock divider (which needs to live above applications, but below IME)</li>
* <li>replaced windows, which need to live above their normal level, because they anticipate
* an animation</li>.
*/
public class WindowLayersController {
private final WindowManagerService mService;
private int mInputMethodAnimLayerAdjustment;
public WindowLayersController(WindowManagerService service) {
mService = service;
}
private int mHighestApplicationLayer = 0;
private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
private WindowState mDockDivider = null;
private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
final void assignLayersLocked(WindowList windows) {
if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
new RuntimeException("here").fillInStackTrace());
clear();
int curBaseLayer = 0;
int curLayer = 0;
boolean anyLayerChanged = false;
for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
final WindowState w = windows.get(i);
boolean layerChanged = false;
int oldLayer = w.mLayer;
if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
} else {
curBaseLayer = curLayer = w.mBaseLayer;
}
assignAnimLayer(w, curLayer);
// TODO: Preserved old behavior of code here but not sure comparing
// oldLayer to mAnimLayer and mLayer makes sense...though the
// worst case would be unintentionalp layer reassignment.
if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
layerChanged = true;
anyLayerChanged = true;
}
if (w.mAppToken != null) {
mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
w.mWinAnimator.mAnimLayer);
}
collectSpecialWindows(w);
if (layerChanged) {
w.scheduleAnimationIfDimming();
}
}
adjustSpecialWindows();
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null && anyLayerChanged
&& windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
mService.mAccessibilityController.onWindowLayersChangedLocked();
}
if (DEBUG_LAYERS) logDebugLayers(windows);
}
void setInputMethodAnimLayerAdjustment(int adj) {
if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
mInputMethodAnimLayerAdjustment = adj;
final WindowState imw = mService.mInputMethodWindow;
if (imw != null) {
imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
+ " anim layer: " + imw.mWinAnimator.mAnimLayer);
for (int i = imw.mChildWindows.size() - 1; i >= 0; i--) {
final WindowState childWindow = imw.mChildWindows.get(i);
childWindow.mWinAnimator.mAnimLayer = childWindow.mLayer + adj;
if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + childWindow
+ " anim layer: " + childWindow.mWinAnimator.mAnimLayer);
}
}
for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
final WindowState dialog = mService.mInputMethodDialogs.get(i);
dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj;
if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
+ " anim layer: " + dialog.mWinAnimator.mAnimLayer);
}
}
int getSpecialWindowAnimLayerAdjustment(WindowState win) {
if (win.mIsImWindow) {
return mInputMethodAnimLayerAdjustment;
} else if (win.mIsWallpaper) {
return mService.mWallpaperControllerLocked.getAnimLayerAdjustment();
}
return 0;
}
/**
* @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
* above all application surfaces.
*/
int getResizeDimLayer() {
return (mDockDivider != null) ? mDockDivider.mLayer - 1 : LAYER_OFFSET_DIM;
}
private void logDebugLayers(WindowList windows) {
for (int i = 0, n = windows.size(); i < n; i++) {
final WindowState w = windows.get(i);
final WindowStateAnimator winAnimator = w.mWinAnimator;
Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
+ " mLayer=" + w.mLayer + (w.mAppToken == null
? "" : " mAppLayer=" + w.mAppToken.mAppAnimator.animLayerAdjustment)
+ " =mAnimLayer=" + winAnimator.mAnimLayer);
}
}
private void clear() {
mHighestApplicationLayer = 0;
mPinnedWindows.clear();
mInputMethodWindows.clear();
mDockedWindows.clear();
mReplacingWindows.clear();
mDockDivider = null;
}
private void collectSpecialWindows(WindowState w) {
if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
mDockDivider = w;
return;
}
if (w.mWillReplaceWindow) {
mReplacingWindows.add(w);
}
if (w.mIsImWindow) {
mInputMethodWindows.add(w);
return;
}
final TaskStack stack = w.getStack();
if (stack == null) {
return;
}
if (stack.mStackId == PINNED_STACK_ID) {
mPinnedWindows.add(w);
} else if (stack.mStackId == DOCKED_STACK_ID) {
mDockedWindows.add(w);
}
}
private void adjustSpecialWindows() {
int layer = mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER;
// For pinned and docked stack window, we want to make them above other windows also when
// these windows are animating.
while (!mDockedWindows.isEmpty()) {
layer = assignAndIncreaseLayerIfNeeded(mDockedWindows.remove(), layer);
}
layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
if (mDockDivider != null && mDockDivider.isVisibleLw()) {
while (!mInputMethodWindows.isEmpty()) {
final WindowState w = mInputMethodWindows.remove();
// Only ever move IME windows up, else we brake IME for windows above the divider.
if (layer > w.mLayer) {
layer = assignAndIncreaseLayerIfNeeded(w, layer);
}
}
}
// We know that we will be animating a relaunching window in the near future, which will
// receive a z-order increase. We want the replaced window to immediately receive the same
// treatment, e.g. to be above the dock divider.
while (!mReplacingWindows.isEmpty()) {
layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
}
while (!mPinnedWindows.isEmpty()) {
layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
}
}
private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
if (win != null) {
assignAnimLayer(win, layer);
// Make sure we leave space inbetween normal windows for dims and such.
layer += WINDOW_LAYER_MULTIPLIER;
}
return layer;
}
private void assignAnimLayer(WindowState w, int layer) {
w.mLayer = layer;
w.mWinAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
getSpecialWindowAnimLayerAdjustment(w);
if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
&& w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
}
}
void dump(PrintWriter pw, String s) {
if (mInputMethodAnimLayerAdjustment != 0 ||
mService.mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
pw.print(" mInputMethodAnimLayerAdjustment=");
pw.print(mInputMethodAnimLayerAdjustment);
pw.print(" mWallpaperAnimLayerAdjustment=");
pw.println(mService.mWallpaperControllerLocked.getAnimLayerAdjustment());
}
}
}