TaskStack.java revision e3119b7d353e71d1f94ddff932b722b4d285931e
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                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
158                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
159                        return true;
160                    }
161                }
162            }
163        }
164        return false;
165    }
166
167    /**
168     * Put a Task in this stack. Used for adding and moving.
169     * @param task The task to add.
170     * @param toTop Whether to add it to the top or bottom.
171     */
172    void addTask(Task task, boolean toTop) {
173        int stackNdx;
174        if (!toTop) {
175            stackNdx = 0;
176        } else {
177            stackNdx = mTasks.size();
178            if (!mService.isCurrentProfileLocked(task.mUserId)) {
179                // Place the task below all current user tasks.
180                while (--stackNdx >= 0) {
181                    if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
182                        break;
183                    }
184                }
185                // Put it above first non-current user task.
186                ++stackNdx;
187            }
188        }
189        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
190                + " pos=" + stackNdx);
191        mTasks.add(stackNdx, task);
192
193        task.mStack = this;
194        if (toTop) {
195            mDisplayContent.moveStack(this, true);
196        }
197        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx);
198    }
199
200    void moveTaskToTop(Task task) {
201        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
202                + Debug.getCallers(6));
203        mTasks.remove(task);
204        addTask(task, true);
205    }
206
207    void moveTaskToBottom(Task task) {
208        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
209        mTasks.remove(task);
210        addTask(task, false);
211    }
212
213    /**
214     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
215     * back.
216     * @param task The Task to delete.
217     */
218    void removeTask(Task task) {
219        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
220        mTasks.remove(task);
221        if (mDisplayContent != null) {
222            if (mTasks.isEmpty()) {
223                mDisplayContent.moveStack(this, false);
224            }
225            mDisplayContent.layoutNeeded = true;
226        }
227        final int taskId = task.taskId;
228        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
229            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
230            if (wtoken.groupId == taskId) {
231                wtoken.mIsExiting = false;
232                mExitingAppTokens.remove(appNdx);
233            }
234        }
235    }
236
237    void attachDisplayContent(DisplayContent displayContent) {
238        if (mDisplayContent != null) {
239            throw new IllegalStateException("attachDisplayContent: Already attached");
240        }
241
242        mDisplayContent = displayContent;
243        mDimLayer = new DimLayer(mService, this, displayContent);
244        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
245        updateDisplayInfo();
246    }
247
248    void detachDisplay() {
249        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
250
251        boolean doAnotherLayoutPass = false;
252        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
253            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
254            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
255                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
256                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
257                    mService.removeWindowInnerLocked(null, appWindows.get(winNdx));
258                    doAnotherLayoutPass = true;
259                }
260            }
261        }
262        if (doAnotherLayoutPass) {
263            mService.requestTraversalLocked();
264        }
265
266        mAnimationBackgroundSurface.destroySurface();
267        mAnimationBackgroundSurface = null;
268        mDimLayer.destroySurface();
269        mDimLayer = null;
270        mDisplayContent = null;
271    }
272
273    void resetAnimationBackgroundAnimator() {
274        mAnimationBackgroundAnimator = null;
275        mAnimationBackgroundSurface.hide();
276    }
277
278    private long getDimBehindFadeDuration(long duration) {
279        TypedValue tv = new TypedValue();
280        mService.mContext.getResources().getValue(
281                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
282        if (tv.type == TypedValue.TYPE_FRACTION) {
283            duration = (long)tv.getFraction(duration, duration);
284        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
285            duration = tv.data;
286        }
287        return duration;
288    }
289
290    boolean animateDimLayers() {
291        final int dimLayer;
292        final float dimAmount;
293        if (mDimWinAnimator == null) {
294            dimLayer = mDimLayer.getLayer();
295            dimAmount = 0;
296        } else {
297            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
298            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
299        }
300        final float targetAlpha = mDimLayer.getTargetAlpha();
301        if (targetAlpha != dimAmount) {
302            if (mDimWinAnimator == null) {
303                mDimLayer.hide(DEFAULT_DIM_DURATION);
304            } else {
305                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
306                        ? mDimWinAnimator.mAnimation.computeDurationHint()
307                        : DEFAULT_DIM_DURATION;
308                if (targetAlpha > dimAmount) {
309                    duration = getDimBehindFadeDuration(duration);
310                }
311                mDimLayer.show(dimLayer, dimAmount, duration);
312            }
313        } else if (mDimLayer.getLayer() != dimLayer) {
314            mDimLayer.setLayer(dimLayer);
315        }
316        if (mDimLayer.isAnimating()) {
317            if (!mService.okToDisplay()) {
318                // Jump to the end of the animation.
319                mDimLayer.show();
320            } else {
321                return mDimLayer.stepAnimation();
322            }
323        }
324        return false;
325    }
326
327    void resetDimmingTag() {
328        mDimmingTag = false;
329    }
330
331    void setDimmingTag() {
332        mDimmingTag = true;
333    }
334
335    boolean testDimmingTag() {
336        return mDimmingTag;
337    }
338
339    boolean isDimming() {
340        return mDimLayer.isDimming();
341    }
342
343    boolean isDimming(WindowStateAnimator winAnimator) {
344        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
345    }
346
347    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
348        // Only set dim params on the highest dimmed layer.
349        final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
350        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
351        if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
352                || !existingDimWinAnimator.mSurfaceShown
353                || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
354            mDimWinAnimator = newWinAnimator;
355        }
356    }
357
358    void stopDimmingIfNeeded() {
359        if (!mDimmingTag && isDimming()) {
360            mDimWinAnimator = null;
361        }
362    }
363
364    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
365        int animLayer = winAnimator.mAnimLayer;
366        if (mAnimationBackgroundAnimator == null
367                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
368            mAnimationBackgroundAnimator = winAnimator;
369            animLayer = mService.adjustAnimationBackground(winAnimator);
370            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
371                    ((color >> 24) & 0xff) / 255f, 0);
372        }
373    }
374
375    void switchUser(int userId) {
376        int top = mTasks.size();
377        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
378            Task task = mTasks.get(taskNdx);
379            if (mService.isCurrentProfileLocked(task.mUserId)) {
380                mTasks.remove(taskNdx);
381                mTasks.add(task);
382                --top;
383            }
384        }
385    }
386
387    void close() {
388        mDimLayer.mDimSurface.destroy();
389        mAnimationBackgroundSurface.mDimSurface.destroy();
390    }
391
392    public void dump(String prefix, PrintWriter pw) {
393        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
394        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
395        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
396            pw.print(prefix); pw.println(mTasks.get(taskNdx));
397        }
398        if (mAnimationBackgroundSurface.isDimming()) {
399            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
400            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
401        }
402        if (mDimLayer.isDimming()) {
403            pw.print(prefix); pw.println("mDimLayer:");
404            mDimLayer.printTo(prefix, pw);
405            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
406        }
407        if (!mExitingAppTokens.isEmpty()) {
408            pw.println();
409            pw.println("  Exiting application tokens:");
410            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
411                WindowToken token = mExitingAppTokens.get(i);
412                pw.print("  Exiting App #"); pw.print(i);
413                pw.print(' '); pw.print(token);
414                pw.println(':');
415                token.dump(pw, "    ");
416            }
417        }
418    }
419
420    @Override
421    public String toString() {
422        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
423    }
424}
425