1 /*
2  * Copyright (C) 2015 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 android.util.Slog;
20 import android.view.Display;
21 
22 import java.io.PrintWriter;
23 import java.util.ArrayDeque;
24 
25 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
26 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
27 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
28 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
29 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
30 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
31 import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
32 
33 /**
34  * Controller for assigning layers to windows on the display.
35  *
36  * This class encapsulates general algorithm for assigning layers and special rules that we need to
37  * apply on top. The general algorithm goes through windows from bottom to the top and the higher
38  * the window is, the higher layer is assigned. The final layer is equal to base layer +
39  * adjustment from the order. This means that the window list is assumed to be ordered roughly by
40  * the base layer (there are exceptions, e.g. due to keyguard and wallpaper and they need to be
41  * handled with care, because they break the algorithm).
42  *
43  * On top of the general algorithm we add special rules, that govern such amazing things as:
44  * <li>IME (which has higher base layer, but will be positioned above application windows)</li>
45  * <li>docked/pinned windows (that need to be lifted above other application windows, including
46  * animations)
47  * <li>dock divider (which needs to live above applications, but below IME)</li>
48  * <li>replaced windows, which need to live above their normal level, because they anticipate
49  * an animation</li>.
50  */
51 public class WindowLayersController {
52     private final WindowManagerService mService;
53 
54     private int mInputMethodAnimLayerAdjustment;
55 
WindowLayersController(WindowManagerService service)56     public WindowLayersController(WindowManagerService service) {
57         mService = service;
58     }
59 
60     private int mHighestApplicationLayer = 0;
61     private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
62     private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
63     private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
64     private WindowState mDockDivider = null;
65     private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
66 
assignLayersLocked(WindowList windows)67     final void assignLayersLocked(WindowList windows) {
68         if (DEBUG_LAYERS) Slog.v(TAG_WM, "Assigning layers based on windows=" + windows,
69                 new RuntimeException("here").fillInStackTrace());
70 
71         clear();
72         int curBaseLayer = 0;
73         int curLayer = 0;
74         boolean anyLayerChanged = false;
75         for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
76             final WindowState w = windows.get(i);
77             boolean layerChanged = false;
78 
79             int oldLayer = w.mLayer;
80             if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
81                 curLayer += WINDOW_LAYER_MULTIPLIER;
82             } else {
83                 curBaseLayer = curLayer = w.mBaseLayer;
84             }
85             assignAnimLayer(w, curLayer);
86 
87             // TODO: Preserved old behavior of code here but not sure comparing
88             // oldLayer to mAnimLayer and mLayer makes sense...though the
89             // worst case would be unintentionalp layer reassignment.
90             if (w.mLayer != oldLayer || w.mWinAnimator.mAnimLayer != oldLayer) {
91                 layerChanged = true;
92                 anyLayerChanged = true;
93             }
94 
95             if (w.mAppToken != null) {
96                 mHighestApplicationLayer = Math.max(mHighestApplicationLayer,
97                         w.mWinAnimator.mAnimLayer);
98             }
99             collectSpecialWindows(w);
100 
101             if (layerChanged) {
102                 w.scheduleAnimationIfDimming();
103             }
104         }
105 
106         adjustSpecialWindows();
107 
108         //TODO (multidisplay): Magnification is supported only for the default display.
109         if (mService.mAccessibilityController != null && anyLayerChanged
110                 && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
111             mService.mAccessibilityController.onWindowLayersChangedLocked();
112         }
113 
114         if (DEBUG_LAYERS) logDebugLayers(windows);
115     }
116 
setInputMethodAnimLayerAdjustment(int adj)117     void setInputMethodAnimLayerAdjustment(int adj) {
118         if (DEBUG_LAYERS) Slog.v(TAG_WM, "Setting im layer adj to " + adj);
119         mInputMethodAnimLayerAdjustment = adj;
120         final WindowState imw = mService.mInputMethodWindow;
121         if (imw != null) {
122             imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
123             if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
124                     + " anim layer: " + imw.mWinAnimator.mAnimLayer);
125             for (int i = imw.mChildWindows.size() - 1; i >= 0; i--) {
126                 final WindowState childWindow = imw.mChildWindows.get(i);
127                 childWindow.mWinAnimator.mAnimLayer = childWindow.mLayer + adj;
128                 if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + childWindow
129                         + " anim layer: " + childWindow.mWinAnimator.mAnimLayer);
130             }
131         }
132         for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
133             final WindowState dialog = mService.mInputMethodDialogs.get(i);
134             dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj;
135             if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
136                     + " anim layer: " + dialog.mWinAnimator.mAnimLayer);
137         }
138     }
139 
getSpecialWindowAnimLayerAdjustment(WindowState win)140     int getSpecialWindowAnimLayerAdjustment(WindowState win) {
141         if (win.mIsImWindow) {
142             return mInputMethodAnimLayerAdjustment;
143         } else if (win.mIsWallpaper) {
144             return mService.mWallpaperControllerLocked.getAnimLayerAdjustment();
145         }
146         return 0;
147     }
148 
149     /**
150      * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
151      *         above all application surfaces.
152      */
getResizeDimLayer()153     int getResizeDimLayer() {
154         return (mDockDivider != null) ? mDockDivider.mLayer - 1 : LAYER_OFFSET_DIM;
155     }
156 
logDebugLayers(WindowList windows)157     private void logDebugLayers(WindowList windows) {
158         for (int i = 0, n = windows.size(); i < n; i++) {
159             final WindowState w = windows.get(i);
160             final WindowStateAnimator winAnimator = w.mWinAnimator;
161             Slog.v(TAG_WM, "Assign layer " + w + ": " + "mBase=" + w.mBaseLayer
162                     + " mLayer=" + w.mLayer + (w.mAppToken == null
163                     ? "" : " mAppLayer=" + w.mAppToken.mAppAnimator.animLayerAdjustment)
164                     + " =mAnimLayer=" + winAnimator.mAnimLayer);
165         }
166     }
167 
clear()168     private void clear() {
169         mHighestApplicationLayer = 0;
170         mPinnedWindows.clear();
171         mInputMethodWindows.clear();
172         mDockedWindows.clear();
173         mReplacingWindows.clear();
174         mDockDivider = null;
175     }
176 
collectSpecialWindows(WindowState w)177     private void collectSpecialWindows(WindowState w) {
178         if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
179             mDockDivider = w;
180             return;
181         }
182         if (w.mWillReplaceWindow) {
183             mReplacingWindows.add(w);
184         }
185         if (w.mIsImWindow) {
186             mInputMethodWindows.add(w);
187             return;
188         }
189         final TaskStack stack = w.getStack();
190         if (stack == null) {
191             return;
192         }
193         if (stack.mStackId == PINNED_STACK_ID) {
194             mPinnedWindows.add(w);
195         } else if (stack.mStackId == DOCKED_STACK_ID) {
196             mDockedWindows.add(w);
197         }
198     }
199 
adjustSpecialWindows()200     private void adjustSpecialWindows() {
201         int layer = mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER;
202         // For pinned and docked stack window, we want to make them above other windows also when
203         // these windows are animating.
204         while (!mDockedWindows.isEmpty()) {
205             layer = assignAndIncreaseLayerIfNeeded(mDockedWindows.remove(), layer);
206         }
207 
208         layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
209 
210         if (mDockDivider != null && mDockDivider.isVisibleLw()) {
211             while (!mInputMethodWindows.isEmpty()) {
212                 final WindowState w = mInputMethodWindows.remove();
213                 // Only ever move IME windows up, else we brake IME for windows above the divider.
214                 if (layer > w.mLayer) {
215                     layer = assignAndIncreaseLayerIfNeeded(w, layer);
216                 }
217             }
218         }
219 
220         // We know that we will be animating a relaunching window in the near future, which will
221         // receive a z-order increase. We want the replaced window to immediately receive the same
222         // treatment, e.g. to be above the dock divider.
223         while (!mReplacingWindows.isEmpty()) {
224             layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
225         }
226 
227         while (!mPinnedWindows.isEmpty()) {
228             layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
229         }
230     }
231 
assignAndIncreaseLayerIfNeeded(WindowState win, int layer)232     private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
233         if (win != null) {
234             assignAnimLayer(win, layer);
235             // Make sure we leave space inbetween normal windows for dims and such.
236             layer += WINDOW_LAYER_MULTIPLIER;
237         }
238         return layer;
239     }
240 
assignAnimLayer(WindowState w, int layer)241     private void assignAnimLayer(WindowState w, int layer) {
242         w.mLayer = layer;
243         w.mWinAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
244                     getSpecialWindowAnimLayerAdjustment(w);
245         if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
246                 && w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
247             w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
248         }
249     }
250 
dump(PrintWriter pw, String s)251     void dump(PrintWriter pw, String s) {
252         if (mInputMethodAnimLayerAdjustment != 0 ||
253                 mService.mWallpaperControllerLocked.getAnimLayerAdjustment() != 0) {
254             pw.print("  mInputMethodAnimLayerAdjustment=");
255             pw.print(mInputMethodAnimLayerAdjustment);
256             pw.print("  mWallpaperAnimLayerAdjustment=");
257             pw.println(mService.mWallpaperControllerLocked.getAnimLayerAdjustment());
258         }
259     }
260 }
261