TaskStack.java revision 00a6675461dda409869a1af56107506e24cf6f9e
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    /**
279     * Put a Task in this stack. Used for adding and moving.
280     * @param task The task to add.
281     * @param toTop Whether to add it to the top or bottom.
282     */
283    void addTask(Task task, boolean toTop) {
284        int stackNdx;
285        if (!toTop) {
286            stackNdx = 0;
287        } else {
288            stackNdx = mTasks.size();
289            if (!mService.isCurrentProfileLocked(task.mUserId)) {
290                // Place the task below all current user tasks.
291                while (--stackNdx >= 0) {
292                    if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
293                        break;
294                    }
295                }
296                // Put it above first non-current user task.
297                ++stackNdx;
298            }
299        }
300        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
301                + " pos=" + stackNdx);
302        mTasks.add(stackNdx, task);
303
304        task.mStack = this;
305        if (toTop) {
306            mDisplayContent.moveStack(this, true);
307        }
308        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
309    }
310
311    void moveTaskToTop(Task task) {
312        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
313                + Debug.getCallers(6));
314        mTasks.remove(task);
315        addTask(task, true);
316    }
317
318    void moveTaskToBottom(Task task) {
319        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
320        mTasks.remove(task);
321        addTask(task, false);
322    }
323
324    /**
325     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
326     * back.
327     * @param task The Task to delete.
328     */
329    void removeTask(Task task) {
330        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
331        mTasks.remove(task);
332        if (mDisplayContent != null) {
333            if (mTasks.isEmpty()) {
334                mDisplayContent.moveStack(this, false);
335            }
336            mDisplayContent.layoutNeeded = true;
337        }
338        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
339            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
340            if (wtoken.mTask == task) {
341                wtoken.mIsExiting = false;
342                mExitingAppTokens.remove(appNdx);
343            }
344        }
345    }
346
347    void attachDisplayContent(DisplayContent displayContent) {
348        if (mDisplayContent != null) {
349            throw new IllegalStateException("attachDisplayContent: Already attached");
350        }
351
352        mDisplayContent = displayContent;
353        mDimLayer = new DimLayer(mService, this, displayContent);
354        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
355        updateDisplayInfo();
356    }
357
358    void detachDisplay() {
359        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
360
361        boolean doAnotherLayoutPass = false;
362        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
363            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
364            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
365                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
366                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
367                    mService.removeWindowInnerLocked(appWindows.get(winNdx));
368                    doAnotherLayoutPass = true;
369                }
370            }
371        }
372        if (doAnotherLayoutPass) {
373            mService.requestTraversalLocked();
374        }
375
376        close();
377
378        mDisplayContent = null;
379    }
380
381    void resetAnimationBackgroundAnimator() {
382        mAnimationBackgroundAnimator = null;
383        mAnimationBackgroundSurface.hide();
384    }
385
386    private long getDimBehindFadeDuration(long duration) {
387        TypedValue tv = new TypedValue();
388        mService.mContext.getResources().getValue(
389                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
390        if (tv.type == TypedValue.TYPE_FRACTION) {
391            duration = (long)tv.getFraction(duration, duration);
392        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
393            duration = tv.data;
394        }
395        return duration;
396    }
397
398    boolean animateDimLayers() {
399        final int dimLayer;
400        final float dimAmount;
401        if (mDimWinAnimator == null) {
402            dimLayer = mDimLayer.getLayer();
403            dimAmount = 0;
404        } else {
405            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
406            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
407        }
408        final float targetAlpha = mDimLayer.getTargetAlpha();
409        if (targetAlpha != dimAmount) {
410            if (mDimWinAnimator == null) {
411                mDimLayer.hide(DEFAULT_DIM_DURATION);
412            } else {
413                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
414                        ? mDimWinAnimator.mAnimation.computeDurationHint()
415                        : DEFAULT_DIM_DURATION;
416                if (targetAlpha > dimAmount) {
417                    duration = getDimBehindFadeDuration(duration);
418                }
419                mDimLayer.show(dimLayer, dimAmount, duration);
420            }
421        } else if (mDimLayer.getLayer() != dimLayer) {
422            mDimLayer.setLayer(dimLayer);
423        }
424        if (mDimLayer.isAnimating()) {
425            if (!mService.okToDisplay()) {
426                // Jump to the end of the animation.
427                mDimLayer.show();
428            } else {
429                return mDimLayer.stepAnimation();
430            }
431        }
432        return false;
433    }
434
435    void resetDimmingTag() {
436        mDimmingTag = false;
437    }
438
439    void setDimmingTag() {
440        mDimmingTag = true;
441    }
442
443    boolean testDimmingTag() {
444        return mDimmingTag;
445    }
446
447    boolean isDimming() {
448        return mDimLayer.isDimming();
449    }
450
451    boolean isDimming(WindowStateAnimator winAnimator) {
452        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
453    }
454
455    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
456        // Only set dim params on the highest dimmed layer.
457        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
458        if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
459                || !mDimWinAnimator.mSurfaceShown
460                || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
461            mDimWinAnimator = newWinAnimator;
462            if (mDimWinAnimator.mWin.mAppToken == null
463                    && !mFullscreen && mDisplayContent != null) {
464                // Dim should cover the entire screen for system windows.
465                mDisplayContent.getLogicalDisplayRect(mTmpRect);
466                mDimLayer.setBounds(mTmpRect);
467            }
468        }
469    }
470
471    void stopDimmingIfNeeded() {
472        if (!mDimmingTag && isDimming()) {
473            mDimWinAnimator = null;
474            mDimLayer.setBounds(mBounds);
475        }
476    }
477
478    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
479        int animLayer = winAnimator.mAnimLayer;
480        if (mAnimationBackgroundAnimator == null
481                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
482            mAnimationBackgroundAnimator = winAnimator;
483            animLayer = mService.adjustAnimationBackground(winAnimator);
484            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
485                    ((color >> 24) & 0xff) / 255f, 0);
486        }
487    }
488
489    void switchUser() {
490        int top = mTasks.size();
491        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
492            Task task = mTasks.get(taskNdx);
493            if (mService.isCurrentProfileLocked(task.mUserId)) {
494                mTasks.remove(taskNdx);
495                mTasks.add(task);
496                --top;
497            }
498        }
499    }
500
501    void close() {
502        if (mAnimationBackgroundSurface != null) {
503            mAnimationBackgroundSurface.destroySurface();
504            mAnimationBackgroundSurface = null;
505        }
506        if (mDimLayer != null) {
507            mDimLayer.destroySurface();
508            mDimLayer = null;
509        }
510    }
511
512    public void dump(String prefix, PrintWriter pw) {
513        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
514        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
515        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
516            pw.print(prefix); pw.println(mTasks.get(taskNdx));
517        }
518        if (mAnimationBackgroundSurface.isDimming()) {
519            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
520            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
521        }
522        if (mDimLayer.isDimming()) {
523            pw.print(prefix); pw.println("mDimLayer:");
524            mDimLayer.printTo(prefix + " ", pw);
525            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
526        }
527        if (!mExitingAppTokens.isEmpty()) {
528            pw.println();
529            pw.println("  Exiting application tokens:");
530            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
531                WindowToken token = mExitingAppTokens.get(i);
532                pw.print("  Exiting App #"); pw.print(i);
533                pw.print(' '); pw.print(token);
534                pw.println(':');
535                token.dump(pw, "    ");
536            }
537        }
538    }
539
540    @Override
541    public String toString() {
542        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
543    }
544}
545