TaskStack.java revision 6dfdfd6741c5a3dd8d8a49ddbd6ee5dfe2fd292d
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.content.res.Configuration;
23import android.graphics.Rect;
24import android.os.Debug;
25import android.util.DisplayMetrics;
26import android.util.EventLog;
27import android.util.Slog;
28import android.util.TypedValue;
29import android.view.Surface;
30
31import com.android.server.EventLogTags;
32
33import java.io.PrintWriter;
34import java.util.ArrayList;
35
36public class TaskStack {
37    /** Amount of time in milliseconds to animate the dim surface from one value to another,
38     * when no window animation is driving it. */
39    private static final int DEFAULT_DIM_DURATION = 200;
40
41    /** Unique identifier */
42    final int mStackId;
43
44    /** The service */
45    private final WindowManagerService mService;
46
47    /** The display this stack sits under. */
48    private DisplayContent mDisplayContent;
49
50    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
51     * mTaskHistory in the ActivityStack with the same mStackId */
52    private final ArrayList<Task> mTasks = new ArrayList<Task>();
53
54    /** For comparison with DisplayContent bounds. */
55    private Rect mTmpRect = new Rect();
56    /** For handling display rotations. */
57    private Rect mTmpRect2 = new Rect();
58
59    /** Content limits relative to the DisplayContent this sits in. */
60    private Rect mBounds = new Rect();
61
62    /** Whether mBounds is fullscreen */
63    private boolean mFullscreen = true;
64
65    /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
66    private DimLayer mDimLayer;
67
68    /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
69    WindowStateAnimator mDimWinAnimator;
70
71    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
72    DimLayer mAnimationBackgroundSurface;
73
74    /** The particular window with an Animation with non-zero background color. */
75    WindowStateAnimator mAnimationBackgroundAnimator;
76
77    /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
78     * then stop any dimming. */
79    boolean mDimmingTag;
80
81    /** Application tokens that are exiting, but still on screen for animations. */
82    final AppTokenList mExitingAppTokens = new AppTokenList();
83
84    /** Detach this stack from its display when animation completes. */
85    boolean mDeferDetach;
86
87    // Contains configurations settings that are different from the global configuration due to
88    // stack specific operations. E.g. {@link #setBounds}.
89    Configuration mOverrideConfig;
90    // True if the stack was forced to fullscreen disregarding the override configuration.
91    private boolean mForceFullscreen;
92    // The {@link #mBounds} before the stack was forced to fullscreen. Will be restored as the
93    // stack bounds once the stack is no longer forced to fullscreen.
94    final private Rect mPreForceFullscreenBounds;
95
96    // Device rotation as of the last time {@link #mBounds} was set.
97    int mRotation;
98
99    TaskStack(WindowManagerService service, int stackId) {
100        mService = service;
101        mStackId = stackId;
102        mOverrideConfig = Configuration.EMPTY;
103        mForceFullscreen = false;
104        mPreForceFullscreenBounds = new Rect();
105        // TODO: remove bounds from log, they are always 0.
106        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
107                mBounds.right, mBounds.bottom);
108    }
109
110    DisplayContent getDisplayContent() {
111        return mDisplayContent;
112    }
113
114    ArrayList<Task> getTasks() {
115        return mTasks;
116    }
117
118    void resizeWindows() {
119        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
120        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
121            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
122            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
123                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
124                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
125                    final WindowState win = windows.get(winNdx);
126                    if (!resizingWindows.contains(win)) {
127                        if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
128                                "setBounds: Resizing " + win);
129                        resizingWindows.add(win);
130                    }
131                }
132            }
133        }
134    }
135
136    /** Set the stack bounds. Passing in null sets the bounds to fullscreen. */
137    boolean setBounds(Rect bounds) {
138        boolean oldFullscreen = mFullscreen;
139        int rotation = Surface.ROTATION_0;
140        if (mDisplayContent != null) {
141            mDisplayContent.getLogicalDisplayRect(mTmpRect);
142            rotation = mDisplayContent.getDisplayInfo().rotation;
143            if (bounds == null) {
144                bounds = mTmpRect;
145                mFullscreen = true;
146            } else {
147                bounds.intersect(mTmpRect); // ensure bounds are entirely within the display rect
148                mFullscreen = mTmpRect.equals(bounds);
149            }
150        }
151
152        if (bounds == null) {
153            // Can't set to fullscreen if we don't have a display to get bounds from...
154            return false;
155        }
156        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
157            return false;
158        }
159
160        mDimLayer.setBounds(bounds);
161        mAnimationBackgroundSurface.setBounds(bounds);
162        mBounds.set(bounds);
163        mRotation = rotation;
164        updateOverrideConfiguration();
165        return true;
166    }
167
168    void getBounds(Rect out) {
169        out.set(mBounds);
170    }
171
172    private void updateOverrideConfiguration() {
173        final Configuration serviceConfig = mService.mCurConfiguration;
174        if (mFullscreen) {
175            mOverrideConfig = Configuration.EMPTY;
176            return;
177        }
178
179        if (mOverrideConfig == Configuration.EMPTY) {
180            mOverrideConfig  = new Configuration();
181        }
182
183        // TODO(multidisplay): Update Dp to that of display stack is on.
184        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
185        mOverrideConfig.screenWidthDp =
186                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
187        mOverrideConfig.screenHeightDp =
188                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
189        mOverrideConfig.smallestScreenWidthDp =
190                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
191        mOverrideConfig.orientation =
192                (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
193                        ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
194    }
195
196    void updateDisplayInfo() {
197        if (mFullscreen) {
198            setBounds(null);
199        } else if (mDisplayContent != null) {
200            final int newRotation = mDisplayContent.getDisplayInfo().rotation;
201            if (mRotation == newRotation) {
202                return;
203            }
204
205            // Device rotation changed. We don't want the stack to move around on the screen when
206            // this happens, so update the stack bounds so it stays in the same place.
207            final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
208            mDisplayContent.getLogicalDisplayRect(mTmpRect);
209            switch (rotationDelta) {
210                case Surface.ROTATION_0:
211                    mTmpRect2.set(mBounds);
212                    break;
213                case Surface.ROTATION_90:
214                    mTmpRect2.top = mTmpRect.bottom - mBounds.right;
215                    mTmpRect2.left = mBounds.top;
216                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
217                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
218                    break;
219                case Surface.ROTATION_180:
220                    mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
221                    mTmpRect2.left = mTmpRect.right - mBounds.right;
222                    mTmpRect2.right = mTmpRect2.left + mBounds.width();
223                    mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
224                    break;
225                case Surface.ROTATION_270:
226                    mTmpRect2.top = mBounds.left;
227                    mTmpRect2.left = mTmpRect.right - mBounds.bottom;
228                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
229                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
230                    break;
231            }
232            setBounds(mTmpRect2);
233        }
234    }
235
236    boolean isFullscreen() {
237        return mFullscreen;
238    }
239
240    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
241     * Returns true if something happened.
242     */
243    boolean forceFullscreen(boolean forceFullscreen) {
244        if (mForceFullscreen == forceFullscreen) {
245            return false;
246        }
247        mForceFullscreen = forceFullscreen;
248        if (forceFullscreen) {
249            if (mFullscreen) {
250                return false;
251            }
252            mPreForceFullscreenBounds.set(mBounds);
253            return setBounds(null);
254        } else {
255            if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) {
256                return false;
257            }
258            return setBounds(mPreForceFullscreenBounds);
259        }
260    }
261
262    boolean isAnimating() {
263        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
264            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
265            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
266                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
267                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
268                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
269                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
270                        return true;
271                    }
272                }
273            }
274        }
275        return false;
276    }
277
278    void addTask(Task task, boolean toTop) {
279        addTask(task, toTop, task.showForAllUsers());
280    }
281
282    /**
283     * Put a Task in this stack. Used for adding and moving.
284     * @param task The task to add.
285     * @param toTop Whether to add it to the top or bottom.
286     * @param showForAllUsers Whether to show the task regardless of the current user.
287     */
288    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
289        int stackNdx;
290        if (!toTop) {
291            stackNdx = 0;
292        } else {
293            stackNdx = mTasks.size();
294            if (!showForAllUsers && !mService.isCurrentProfileLocked(task.mUserId)) {
295                // Place the task below all current user tasks.
296                while (--stackNdx >= 0) {
297                    final Task tmpTask = mTasks.get(stackNdx);
298                    if (!tmpTask.showForAllUsers()
299                            || !mService.isCurrentProfileLocked(tmpTask.mUserId)) {
300                        break;
301                    }
302                }
303                // Put it above first non-current user task.
304                ++stackNdx;
305            }
306        }
307        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
308                + " pos=" + stackNdx);
309        mTasks.add(stackNdx, task);
310
311        task.mStack = this;
312        if (toTop) {
313            mDisplayContent.moveStack(this, true);
314        }
315        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
316    }
317
318    void moveTaskToTop(Task task) {
319        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
320                + Debug.getCallers(6));
321        mTasks.remove(task);
322        addTask(task, true);
323    }
324
325    void moveTaskToBottom(Task task) {
326        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
327        mTasks.remove(task);
328        addTask(task, false);
329    }
330
331    /**
332     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
333     * back.
334     * @param task The Task to delete.
335     */
336    void removeTask(Task task) {
337        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
338        mTasks.remove(task);
339        if (mDisplayContent != null) {
340            if (mTasks.isEmpty()) {
341                mDisplayContent.moveStack(this, false);
342            }
343            mDisplayContent.layoutNeeded = true;
344        }
345        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
346            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
347            if (wtoken.mTask == task) {
348                wtoken.mIsExiting = false;
349                mExitingAppTokens.remove(appNdx);
350            }
351        }
352    }
353
354    void attachDisplayContent(DisplayContent displayContent) {
355        if (mDisplayContent != null) {
356            throw new IllegalStateException("attachDisplayContent: Already attached");
357        }
358
359        mDisplayContent = displayContent;
360        mDimLayer = new DimLayer(mService, this, displayContent);
361        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
362        updateDisplayInfo();
363    }
364
365    void detachDisplay() {
366        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
367
368        boolean doAnotherLayoutPass = false;
369        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
370            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
371            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
372                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
373                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
374                    mService.removeWindowInnerLocked(appWindows.get(winNdx));
375                    doAnotherLayoutPass = true;
376                }
377            }
378        }
379        if (doAnotherLayoutPass) {
380            mService.requestTraversalLocked();
381        }
382
383        close();
384
385        mDisplayContent = null;
386    }
387
388    void resetAnimationBackgroundAnimator() {
389        mAnimationBackgroundAnimator = null;
390        mAnimationBackgroundSurface.hide();
391    }
392
393    private long getDimBehindFadeDuration(long duration) {
394        TypedValue tv = new TypedValue();
395        mService.mContext.getResources().getValue(
396                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
397        if (tv.type == TypedValue.TYPE_FRACTION) {
398            duration = (long)tv.getFraction(duration, duration);
399        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
400            duration = tv.data;
401        }
402        return duration;
403    }
404
405    boolean animateDimLayers() {
406        final int dimLayer;
407        final float dimAmount;
408        if (mDimWinAnimator == null) {
409            dimLayer = mDimLayer.getLayer();
410            dimAmount = 0;
411        } else {
412            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
413            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
414        }
415        final float targetAlpha = mDimLayer.getTargetAlpha();
416        if (targetAlpha != dimAmount) {
417            if (mDimWinAnimator == null) {
418                mDimLayer.hide(DEFAULT_DIM_DURATION);
419            } else {
420                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
421                        ? mDimWinAnimator.mAnimation.computeDurationHint()
422                        : DEFAULT_DIM_DURATION;
423                if (targetAlpha > dimAmount) {
424                    duration = getDimBehindFadeDuration(duration);
425                }
426                mDimLayer.show(dimLayer, dimAmount, duration);
427            }
428        } else if (mDimLayer.getLayer() != dimLayer) {
429            mDimLayer.setLayer(dimLayer);
430        }
431        if (mDimLayer.isAnimating()) {
432            if (!mService.okToDisplay()) {
433                // Jump to the end of the animation.
434                mDimLayer.show();
435            } else {
436                return mDimLayer.stepAnimation();
437            }
438        }
439        return false;
440    }
441
442    void resetDimmingTag() {
443        mDimmingTag = false;
444    }
445
446    void setDimmingTag() {
447        mDimmingTag = true;
448    }
449
450    boolean testDimmingTag() {
451        return mDimmingTag;
452    }
453
454    boolean isDimming() {
455        return mDimLayer.isDimming();
456    }
457
458    boolean isDimming(WindowStateAnimator winAnimator) {
459        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
460    }
461
462    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
463        // Only set dim params on the highest dimmed layer.
464        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
465        if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
466                || !mDimWinAnimator.mSurfaceShown
467                || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
468            mDimWinAnimator = newWinAnimator;
469            if (mDimWinAnimator.mWin.mAppToken == null
470                    && !mFullscreen && mDisplayContent != null) {
471                // Dim should cover the entire screen for system windows.
472                mDisplayContent.getLogicalDisplayRect(mTmpRect);
473                mDimLayer.setBounds(mTmpRect);
474            }
475        }
476    }
477
478    void stopDimmingIfNeeded() {
479        if (!mDimmingTag && isDimming()) {
480            mDimWinAnimator = null;
481            mDimLayer.setBounds(mBounds);
482        }
483    }
484
485    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
486        int animLayer = winAnimator.mAnimLayer;
487        if (mAnimationBackgroundAnimator == null
488                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
489            mAnimationBackgroundAnimator = winAnimator;
490            animLayer = mService.adjustAnimationBackground(winAnimator);
491            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
492                    ((color >> 24) & 0xff) / 255f, 0);
493        }
494    }
495
496    void switchUser() {
497        int top = mTasks.size();
498        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
499            Task task = mTasks.get(taskNdx);
500            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
501                mTasks.remove(taskNdx);
502                mTasks.add(task);
503                --top;
504            }
505        }
506    }
507
508    void close() {
509        if (mAnimationBackgroundSurface != null) {
510            mAnimationBackgroundSurface.destroySurface();
511            mAnimationBackgroundSurface = null;
512        }
513        if (mDimLayer != null) {
514            mDimLayer.destroySurface();
515            mDimLayer = null;
516        }
517    }
518
519    public void dump(String prefix, PrintWriter pw) {
520        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
521        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
522        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
523            pw.print(prefix); pw.println(mTasks.get(taskNdx));
524        }
525        if (mAnimationBackgroundSurface.isDimming()) {
526            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
527            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
528        }
529        if (mDimLayer.isDimming()) {
530            pw.print(prefix); pw.println("mDimLayer:");
531            mDimLayer.printTo(prefix + " ", pw);
532            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
533        }
534        if (!mExitingAppTokens.isEmpty()) {
535            pw.println();
536            pw.println("  Exiting application tokens:");
537            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
538                WindowToken token = mExitingAppTokens.get(i);
539                pw.print("  Exiting App #"); pw.print(i);
540                pw.print(' '); pw.print(token);
541                pw.println(':');
542                token.dump(pw, "    ");
543            }
544        }
545    }
546
547    @Override
548    public String toString() {
549        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
550    }
551}
552