TaskStack.java revision b660b9d8cf6b951b85a35599d636c470795e9a1a
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    TaskStack(WindowManagerService service, int stackId) {
79        mService = service;
80        mStackId = stackId;
81        // TODO: remove bounds from log, they are always 0.
82        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
83                mBounds.right, mBounds.bottom);
84    }
85
86    DisplayContent getDisplayContent() {
87        return mDisplayContent;
88    }
89
90    ArrayList<Task> getTasks() {
91        return mTasks;
92    }
93
94    void resizeWindows() {
95        final boolean underStatusBar = mBounds.top == 0;
96
97        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
98        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
99            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
100            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
101                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
102                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
103                    final WindowState win = windows.get(winNdx);
104                    if (!resizingWindows.contains(win)) {
105                        if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
106                                "setBounds: Resizing " + win);
107                        resizingWindows.add(win);
108                    }
109                    win.mUnderStatusBar = underStatusBar;
110                }
111            }
112        }
113    }
114
115    boolean setBounds(Rect bounds) {
116        boolean oldFullscreen = mFullscreen;
117        if (mDisplayContent != null) {
118            mDisplayContent.getLogicalDisplayRect(mTmpRect);
119            mFullscreen = mTmpRect.equals(bounds);
120        }
121
122        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
123            return false;
124        }
125
126        mDimLayer.setBounds(bounds);
127        mAnimationBackgroundSurface.setBounds(bounds);
128        mBounds.set(bounds);
129
130        return true;
131    }
132
133    void getBounds(Rect out) {
134        out.set(mBounds);
135    }
136
137    void updateDisplayInfo() {
138        if (mFullscreen && mDisplayContent != null) {
139            mDisplayContent.getLogicalDisplayRect(mTmpRect);
140            setBounds(mTmpRect);
141        }
142    }
143
144    boolean isFullscreen() {
145        return mFullscreen;
146    }
147
148    boolean isAnimating() {
149        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
150            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
151            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
152                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
153                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
154                    if (windows.get(winNdx).mWinAnimator.isAnimating()) {
155                        return true;
156                    }
157                }
158            }
159        }
160        return false;
161    }
162
163    /**
164     * Put a Task in this stack. Used for adding and moving.
165     * @param task The task to add.
166     * @param toTop Whether to add it to the top or bottom.
167     */
168    void addTask(Task task, boolean toTop) {
169        int stackNdx;
170        if (!toTop) {
171            stackNdx = 0;
172        } else {
173            stackNdx = mTasks.size();
174            final int currentUserId = mService.mCurrentUserId;
175            if (task.mUserId != currentUserId) {
176                // Place the task below all current user tasks.
177                while (--stackNdx >= 0) {
178                    if (currentUserId != mTasks.get(stackNdx).mUserId) {
179                        break;
180                    }
181                }
182                ++stackNdx;
183            }
184        }
185        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
186                + " pos=" + stackNdx);
187        mTasks.add(stackNdx, task);
188
189        task.mStack = this;
190        mDisplayContent.moveStack(this, true);
191        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx);
192    }
193
194    void moveTaskToTop(Task task) {
195        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
196                + Debug.getCallers(6));
197        mTasks.remove(task);
198        addTask(task, true);
199    }
200
201    void moveTaskToBottom(Task task) {
202        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
203        mTasks.remove(task);
204        addTask(task, false);
205    }
206
207    /**
208     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
209     * back.
210     * @param task The Task to delete.
211     */
212    void removeTask(Task task) {
213        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
214        mTasks.remove(task);
215        if (mDisplayContent != null) {
216            if (mTasks.isEmpty()) {
217                mDisplayContent.moveStack(this, false);
218            }
219            mDisplayContent.layoutNeeded = true;
220        }
221    }
222
223    void attachDisplayContent(DisplayContent displayContent) {
224        if (mDisplayContent != null) {
225            throw new IllegalStateException("attachDisplayContent: Already attached");
226        }
227
228        mDisplayContent = displayContent;
229        mDimLayer = new DimLayer(mService, this, displayContent);
230        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
231        updateDisplayInfo();
232    }
233
234    void detachDisplay() {
235        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
236        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
237            mService.tmpRemoveTaskWindowsLocked(mTasks.get(taskNdx));
238        }
239        mAnimationBackgroundSurface.destroySurface();
240        mAnimationBackgroundSurface = null;
241        mDimLayer.destroySurface();
242        mDimLayer = null;
243        mDisplayContent = null;
244    }
245
246    void resetAnimationBackgroundAnimator() {
247        mAnimationBackgroundAnimator = null;
248        mAnimationBackgroundSurface.hide();
249    }
250
251    private long getDimBehindFadeDuration(long duration) {
252        TypedValue tv = new TypedValue();
253        mService.mContext.getResources().getValue(
254                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
255        if (tv.type == TypedValue.TYPE_FRACTION) {
256            duration = (long)tv.getFraction(duration, duration);
257        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
258            duration = tv.data;
259        }
260        return duration;
261    }
262
263    boolean animateDimLayers() {
264        final int dimLayer;
265        final float dimAmount;
266        if (mDimWinAnimator == null) {
267            dimLayer = mDimLayer.getLayer();
268            dimAmount = 0;
269        } else {
270            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
271            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
272        }
273        final float targetAlpha = mDimLayer.getTargetAlpha();
274        if (targetAlpha != dimAmount) {
275            if (mDimWinAnimator == null) {
276                mDimLayer.hide(DEFAULT_DIM_DURATION);
277            } else {
278                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
279                        ? mDimWinAnimator.mAnimation.computeDurationHint()
280                        : DEFAULT_DIM_DURATION;
281                if (targetAlpha > dimAmount) {
282                    duration = getDimBehindFadeDuration(duration);
283                }
284                mDimLayer.show(dimLayer, dimAmount, duration);
285            }
286        } else if (mDimLayer.getLayer() != dimLayer) {
287            mDimLayer.setLayer(dimLayer);
288        }
289        if (mDimLayer.isAnimating()) {
290            if (!mService.okToDisplay()) {
291                // Jump to the end of the animation.
292                mDimLayer.show();
293            } else {
294                return mDimLayer.stepAnimation();
295            }
296        }
297        return false;
298    }
299
300    void resetDimmingTag() {
301        mDimmingTag = false;
302    }
303
304    void setDimmingTag() {
305        mDimmingTag = true;
306    }
307
308    boolean testDimmingTag() {
309        return mDimmingTag;
310    }
311
312    boolean isDimming() {
313        return mDimLayer.isDimming();
314    }
315
316    boolean isDimming(WindowStateAnimator winAnimator) {
317        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
318    }
319
320    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
321        // Only set dim params on the highest dimmed layer.
322        final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
323        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
324        if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
325                || !existingDimWinAnimator.mSurfaceShown
326                || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
327            mDimWinAnimator = newWinAnimator;
328        }
329    }
330
331    void stopDimmingIfNeeded() {
332        if (!mDimmingTag && isDimming()) {
333            mDimWinAnimator = null;
334        }
335    }
336
337    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
338        int animLayer = winAnimator.mAnimLayer;
339        if (mAnimationBackgroundAnimator == null
340                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
341            mAnimationBackgroundAnimator = winAnimator;
342            animLayer = mService.adjustAnimationBackground(winAnimator);
343            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
344                    ((color >> 24) & 0xff) / 255f, 0);
345        }
346    }
347
348    void switchUser(int userId) {
349        int top = mTasks.size();
350        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
351            Task task = mTasks.get(taskNdx);
352            if (task.mUserId == userId) {
353                mTasks.remove(taskNdx);
354                mTasks.add(task);
355                --top;
356            }
357        }
358    }
359
360    void close() {
361        mDimLayer.mDimSurface.destroy();
362        mAnimationBackgroundSurface.mDimSurface.destroy();
363    }
364
365    void checkForDeferredActions() {
366        if (mDisplayContent != null &&
367                (mDisplayContent.mDeferredActions & DisplayContent.DEFER_DETACH) != 0 &&
368                !isAnimating()) {
369            mDisplayContent.mDeferredActions &= ~DisplayContent.DEFER_DETACH;
370            mService.detachStack(mStackId);
371            if ((mDisplayContent.mDeferredActions & DisplayContent.DEFER_REMOVAL) != 0) {
372                mDisplayContent.mDeferredActions &= ~DisplayContent.DEFER_REMOVAL;
373                mService.onDisplayRemoved(mDisplayContent.getDisplayId());
374            }
375        }
376        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
377            final Task task = mTasks.get(taskNdx);
378            AppTokenList tokens = task.mAppTokens;
379            for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
380                AppWindowToken wtoken = tokens.get(tokenNdx);
381                if (wtoken.mDeferRemoval) {
382                    wtoken.mDeferRemoval = false;
383                    mService.removeAppFromTaskLocked(wtoken);
384                }
385            }
386            if (task.mDeferRemoval) {
387                task.mDeferRemoval = false;
388                mService.removeTaskLocked(task);
389            }
390        }
391    }
392
393    public void dump(String prefix, PrintWriter pw) {
394        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
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