TaskStack.java revision e0d50cc15be2c8aa9e4a4aa6cf34cc7cf23c9109
1/*
2 * Copyright (C) 2013 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 static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
20import static com.android.server.wm.WindowManagerService.TAG;
21
22import android.graphics.Rect;
23import android.os.Debug;
24import android.util.EventLog;
25import android.util.Slog;
26import android.util.TypedValue;
27import com.android.server.EventLogTags;
28
29import java.io.PrintWriter;
30import java.util.ArrayList;
31
32public class TaskStack {
33    /** Amount of time in milliseconds to animate the dim surface from one value to another,
34     * when no window animation is driving it. */
35    private static final int DEFAULT_DIM_DURATION = 200;
36
37    /** Unique identifier */
38    final int mStackId;
39
40    /** The service */
41    private final WindowManagerService mService;
42
43    /** The display this stack sits under. */
44    private DisplayContent mDisplayContent;
45
46    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
47     * mTaskHistory in the ActivityStack with the same mStackId */
48    private final ArrayList<Task> mTasks = new ArrayList<Task>();
49
50    /** For comparison with DisplayContent bounds. */
51    private Rect mTmpRect = new Rect();
52
53    /** Content limits relative to the DisplayContent this sits in. */
54    private Rect mBounds = new Rect();
55
56    /** Whether mBounds is fullscreen */
57    private boolean mFullscreen = true;
58
59    /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
60    private DimLayer mDimLayer;
61
62    /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
63    WindowStateAnimator mDimWinAnimator;
64
65    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
66    DimLayer mAnimationBackgroundSurface;
67
68    /** The particular window with an Animation with non-zero background color. */
69    WindowStateAnimator mAnimationBackgroundAnimator;
70
71    /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
72     * then stop any dimming. */
73    boolean mDimmingTag;
74
75    /** Application tokens that are exiting, but still on screen for animations. */
76    final AppTokenList mExitingAppTokens = new AppTokenList();
77
78    /** Detach this stack from its display when animation completes. */
79    boolean mDeferDetach;
80
81    TaskStack(WindowManagerService service, int stackId) {
82        mService = service;
83        mStackId = stackId;
84        // TODO: remove bounds from log, they are always 0.
85        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
86                mBounds.right, mBounds.bottom);
87    }
88
89    DisplayContent getDisplayContent() {
90        return mDisplayContent;
91    }
92
93    ArrayList<Task> getTasks() {
94        return mTasks;
95    }
96
97    void resizeWindows() {
98        final boolean underStatusBar = mBounds.top == 0;
99
100        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
101        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
102            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
103            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
104                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
105                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
106                    final WindowState win = windows.get(winNdx);
107                    if (!resizingWindows.contains(win)) {
108                        if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
109                                "setBounds: Resizing " + win);
110                        resizingWindows.add(win);
111                    }
112                    win.mUnderStatusBar = underStatusBar;
113                }
114            }
115        }
116    }
117
118    boolean setBounds(Rect bounds) {
119        boolean oldFullscreen = mFullscreen;
120        if (mDisplayContent != null) {
121            mDisplayContent.getLogicalDisplayRect(mTmpRect);
122            mFullscreen = mTmpRect.equals(bounds);
123        }
124
125        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
126            return false;
127        }
128
129        mDimLayer.setBounds(bounds);
130        mAnimationBackgroundSurface.setBounds(bounds);
131        mBounds.set(bounds);
132
133        return true;
134    }
135
136    void getBounds(Rect out) {
137        out.set(mBounds);
138    }
139
140    void updateDisplayInfo() {
141        if (mFullscreen && mDisplayContent != null) {
142            mDisplayContent.getLogicalDisplayRect(mTmpRect);
143            setBounds(mTmpRect);
144        }
145    }
146
147    boolean isFullscreen() {
148        return mFullscreen;
149    }
150
151    boolean isAnimating() {
152        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
153            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
154            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
155                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
156                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
157                    if (windows.get(winNdx).mWinAnimator.isAnimating()) {
158                        return true;
159                    }
160                }
161            }
162        }
163        return false;
164    }
165
166    /**
167     * Put a Task in this stack. Used for adding and moving.
168     * @param task The task to add.
169     * @param toTop Whether to add it to the top or bottom.
170     */
171    void addTask(Task task, boolean toTop) {
172        int stackNdx;
173        if (!toTop) {
174            stackNdx = 0;
175        } else {
176            stackNdx = mTasks.size();
177            if (!mService.isCurrentProfileLocked(task.mUserId)) {
178                // Place the task below all current user tasks.
179                while (--stackNdx >= 0) {
180                    if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
181                        break;
182                    }
183                }
184                // Put it above first non-current user task.
185                ++stackNdx;
186            }
187        }
188        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
189                + " pos=" + stackNdx);
190        mTasks.add(stackNdx, task);
191
192        task.mStack = this;
193        mDisplayContent.moveStack(this, true);
194        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx);
195    }
196
197    void moveTaskToTop(Task task) {
198        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
199                + Debug.getCallers(6));
200        mTasks.remove(task);
201        addTask(task, true);
202    }
203
204    void moveTaskToBottom(Task task) {
205        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
206        mTasks.remove(task);
207        addTask(task, false);
208    }
209
210    /**
211     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
212     * back.
213     * @param task The Task to delete.
214     */
215    void removeTask(Task task) {
216        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
217        mTasks.remove(task);
218        if (mDisplayContent != null) {
219            if (mTasks.isEmpty()) {
220                mDisplayContent.moveStack(this, false);
221            }
222            mDisplayContent.layoutNeeded = true;
223        }
224    }
225
226    void attachDisplayContent(DisplayContent displayContent) {
227        if (mDisplayContent != null) {
228            throw new IllegalStateException("attachDisplayContent: Already attached");
229        }
230
231        mDisplayContent = displayContent;
232        mDimLayer = new DimLayer(mService, this, displayContent);
233        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
234        updateDisplayInfo();
235    }
236
237    void detachDisplay() {
238        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
239        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
240            mService.tmpRemoveTaskWindowsLocked(mTasks.get(taskNdx));
241        }
242        mAnimationBackgroundSurface.destroySurface();
243        mAnimationBackgroundSurface = null;
244        mDimLayer.destroySurface();
245        mDimLayer = null;
246        mDisplayContent = null;
247    }
248
249    void resetAnimationBackgroundAnimator() {
250        mAnimationBackgroundAnimator = null;
251        mAnimationBackgroundSurface.hide();
252    }
253
254    private long getDimBehindFadeDuration(long duration) {
255        TypedValue tv = new TypedValue();
256        mService.mContext.getResources().getValue(
257                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
258        if (tv.type == TypedValue.TYPE_FRACTION) {
259            duration = (long)tv.getFraction(duration, duration);
260        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
261            duration = tv.data;
262        }
263        return duration;
264    }
265
266    boolean animateDimLayers() {
267        final int dimLayer;
268        final float dimAmount;
269        if (mDimWinAnimator == null) {
270            dimLayer = mDimLayer.getLayer();
271            dimAmount = 0;
272        } else {
273            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
274            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
275        }
276        final float targetAlpha = mDimLayer.getTargetAlpha();
277        if (targetAlpha != dimAmount) {
278            if (mDimWinAnimator == null) {
279                mDimLayer.hide(DEFAULT_DIM_DURATION);
280            } else {
281                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
282                        ? mDimWinAnimator.mAnimation.computeDurationHint()
283                        : DEFAULT_DIM_DURATION;
284                if (targetAlpha > dimAmount) {
285                    duration = getDimBehindFadeDuration(duration);
286                }
287                mDimLayer.show(dimLayer, dimAmount, duration);
288            }
289        } else if (mDimLayer.getLayer() != dimLayer) {
290            mDimLayer.setLayer(dimLayer);
291        }
292        if (mDimLayer.isAnimating()) {
293            if (!mService.okToDisplay()) {
294                // Jump to the end of the animation.
295                mDimLayer.show();
296            } else {
297                return mDimLayer.stepAnimation();
298            }
299        }
300        return false;
301    }
302
303    void resetDimmingTag() {
304        mDimmingTag = false;
305    }
306
307    void setDimmingTag() {
308        mDimmingTag = true;
309    }
310
311    boolean testDimmingTag() {
312        return mDimmingTag;
313    }
314
315    boolean isDimming() {
316        return mDimLayer.isDimming();
317    }
318
319    boolean isDimming(WindowStateAnimator winAnimator) {
320        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
321    }
322
323    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
324        // Only set dim params on the highest dimmed layer.
325        final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
326        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
327        if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
328                || !existingDimWinAnimator.mSurfaceShown
329                || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
330            mDimWinAnimator = newWinAnimator;
331        }
332    }
333
334    void stopDimmingIfNeeded() {
335        if (!mDimmingTag && isDimming()) {
336            mDimWinAnimator = null;
337        }
338    }
339
340    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
341        int animLayer = winAnimator.mAnimLayer;
342        if (mAnimationBackgroundAnimator == null
343                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
344            mAnimationBackgroundAnimator = winAnimator;
345            animLayer = mService.adjustAnimationBackground(winAnimator);
346            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
347                    ((color >> 24) & 0xff) / 255f, 0);
348        }
349    }
350
351    void switchUser(int userId) {
352        int top = mTasks.size();
353        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
354            Task task = mTasks.get(taskNdx);
355            if (mService.isCurrentProfileLocked(task.mUserId)) {
356                mTasks.remove(taskNdx);
357                mTasks.add(task);
358                --top;
359            }
360        }
361    }
362
363    void close() {
364        mDimLayer.mDimSurface.destroy();
365        mAnimationBackgroundSurface.mDimSurface.destroy();
366    }
367
368    public void dump(String prefix, PrintWriter pw) {
369        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
370        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
371        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
372            pw.print(prefix); pw.println(mTasks.get(taskNdx));
373        }
374        if (mAnimationBackgroundSurface.isDimming()) {
375            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
376            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
377        }
378        if (mDimLayer.isDimming()) {
379            pw.print(prefix); pw.println("mDimLayer:");
380            mDimLayer.printTo(prefix, pw);
381            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
382        }
383        if (!mExitingAppTokens.isEmpty()) {
384            pw.println();
385            pw.println("  Exiting application tokens:");
386            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
387                WindowToken token = mExitingAppTokens.get(i);
388                pw.print("  Exiting App #"); pw.print(i);
389                pw.print(' '); pw.print(token);
390                pw.println(':');
391                token.dump(pw, "    ");
392            }
393        }
394    }
395
396    @Override
397    public String toString() {
398        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
399    }
400}
401