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