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