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