TaskStack.java revision d66af6a65533e86468db402907b35ed3e96e6bec
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                // ensure bounds are entirely within the display rect
148                if (!bounds.intersect(mTmpRect)) {
149                    // Can't set bounds outside the containing display.. Sorry!
150                    return false;
151                }
152                mFullscreen = mTmpRect.equals(bounds);
153            }
154        }
155
156        if (bounds == null) {
157            // Can't set to fullscreen if we don't have a display to get bounds from...
158            return false;
159        }
160        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
161            return false;
162        }
163
164        mDimLayer.setBounds(bounds);
165        mAnimationBackgroundSurface.setBounds(bounds);
166        mBounds.set(bounds);
167        mRotation = rotation;
168        updateOverrideConfiguration();
169        return true;
170    }
171
172    void getBounds(Rect out) {
173        out.set(mBounds);
174    }
175
176    private void updateOverrideConfiguration() {
177        final Configuration serviceConfig = mService.mCurConfiguration;
178        if (mFullscreen) {
179            mOverrideConfig = Configuration.EMPTY;
180            return;
181        }
182
183        if (mOverrideConfig == Configuration.EMPTY) {
184            mOverrideConfig  = new Configuration();
185        }
186
187        // TODO(multidisplay): Update Dp to that of display stack is on.
188        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
189        mOverrideConfig.screenWidthDp =
190                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
191        mOverrideConfig.screenHeightDp =
192                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
193        mOverrideConfig.smallestScreenWidthDp =
194                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
195        mOverrideConfig.orientation =
196                (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
197                        ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
198    }
199
200    void updateDisplayInfo() {
201        if (mFullscreen) {
202            setBounds(null);
203        } else if (mDisplayContent != null) {
204            final int newRotation = mDisplayContent.getDisplayInfo().rotation;
205            if (mRotation == newRotation) {
206                return;
207            }
208
209            // Device rotation changed. We don't want the stack to move around on the screen when
210            // this happens, so update the stack bounds so it stays in the same place.
211            final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
212            mDisplayContent.getLogicalDisplayRect(mTmpRect);
213            switch (rotationDelta) {
214                case Surface.ROTATION_0:
215                    mTmpRect2.set(mBounds);
216                    break;
217                case Surface.ROTATION_90:
218                    mTmpRect2.top = mTmpRect.bottom - mBounds.right;
219                    mTmpRect2.left = mBounds.top;
220                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
221                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
222                    break;
223                case Surface.ROTATION_180:
224                    mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
225                    mTmpRect2.left = mTmpRect.right - mBounds.right;
226                    mTmpRect2.right = mTmpRect2.left + mBounds.width();
227                    mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
228                    break;
229                case Surface.ROTATION_270:
230                    mTmpRect2.top = mBounds.left;
231                    mTmpRect2.left = mTmpRect.right - mBounds.bottom;
232                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
233                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
234                    break;
235            }
236            setBounds(mTmpRect2);
237        }
238    }
239
240    boolean isFullscreen() {
241        return mFullscreen;
242    }
243
244    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
245     * Returns true if something happened.
246     */
247    boolean forceFullscreen(boolean forceFullscreen) {
248        if (mForceFullscreen == forceFullscreen) {
249            return false;
250        }
251        mForceFullscreen = forceFullscreen;
252        if (forceFullscreen) {
253            if (mFullscreen) {
254                return false;
255            }
256            mPreForceFullscreenBounds.set(mBounds);
257            return setBounds(null);
258        } else {
259            if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) {
260                return false;
261            }
262            return setBounds(mPreForceFullscreenBounds);
263        }
264    }
265
266    boolean isAnimating() {
267        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
268            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
269            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
270                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
271                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
272                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
273                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
274                        return true;
275                    }
276                }
277            }
278        }
279        return false;
280    }
281
282    void addTask(Task task, boolean toTop) {
283        addTask(task, toTop, task.showForAllUsers());
284    }
285
286    /**
287     * Put a Task in this stack. Used for adding and moving.
288     * @param task The task to add.
289     * @param toTop Whether to add it to the top or bottom.
290     * @param showForAllUsers Whether to show the task regardless of the current user.
291     */
292    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
293        int stackNdx;
294        if (!toTop) {
295            stackNdx = 0;
296        } else {
297            stackNdx = mTasks.size();
298            if (!showForAllUsers && !mService.isCurrentProfileLocked(task.mUserId)) {
299                // Place the task below all current user tasks.
300                while (--stackNdx >= 0) {
301                    final Task tmpTask = mTasks.get(stackNdx);
302                    if (!tmpTask.showForAllUsers()
303                            || !mService.isCurrentProfileLocked(tmpTask.mUserId)) {
304                        break;
305                    }
306                }
307                // Put it above first non-current user task.
308                ++stackNdx;
309            }
310        }
311        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
312                + " pos=" + stackNdx);
313        mTasks.add(stackNdx, task);
314
315        task.mStack = this;
316        if (toTop) {
317            mDisplayContent.moveStack(this, true);
318        }
319        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
320    }
321
322    void moveTaskToTop(Task task) {
323        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
324                + Debug.getCallers(6));
325        mTasks.remove(task);
326        addTask(task, true);
327    }
328
329    void moveTaskToBottom(Task task) {
330        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
331        mTasks.remove(task);
332        addTask(task, false);
333    }
334
335    /**
336     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
337     * back.
338     * @param task The Task to delete.
339     */
340    void removeTask(Task task) {
341        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
342        mTasks.remove(task);
343        if (mDisplayContent != null) {
344            if (mTasks.isEmpty()) {
345                mDisplayContent.moveStack(this, false);
346            }
347            mDisplayContent.layoutNeeded = true;
348        }
349        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
350            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
351            if (wtoken.mTask == task) {
352                wtoken.mIsExiting = false;
353                mExitingAppTokens.remove(appNdx);
354            }
355        }
356    }
357
358    void attachDisplayContent(DisplayContent displayContent) {
359        if (mDisplayContent != null) {
360            throw new IllegalStateException("attachDisplayContent: Already attached");
361        }
362
363        mDisplayContent = displayContent;
364        mDimLayer = new DimLayer(mService, this, displayContent);
365        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
366        updateDisplayInfo();
367    }
368
369    void detachDisplay() {
370        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
371
372        boolean doAnotherLayoutPass = false;
373        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
374            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
375            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
376                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
377                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
378                    // We are in the middle of changing the state of displays/stacks/tasks. We need
379                    // to finish that, before we let layout interfere with it.
380                    mService.removeWindowInnerLocked(appWindows.get(winNdx),
381                            false /* performLayout */);
382                    doAnotherLayoutPass = true;
383                }
384            }
385        }
386        if (doAnotherLayoutPass) {
387            mService.requestTraversalLocked();
388        }
389
390        close();
391    }
392
393    void resetAnimationBackgroundAnimator() {
394        mAnimationBackgroundAnimator = null;
395        mAnimationBackgroundSurface.hide();
396    }
397
398    private long getDimBehindFadeDuration(long duration) {
399        TypedValue tv = new TypedValue();
400        mService.mContext.getResources().getValue(
401                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
402        if (tv.type == TypedValue.TYPE_FRACTION) {
403            duration = (long)tv.getFraction(duration, duration);
404        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
405            duration = tv.data;
406        }
407        return duration;
408    }
409
410    boolean animateDimLayers() {
411        final int dimLayer;
412        final float dimAmount;
413        if (mDimWinAnimator == null) {
414            dimLayer = mDimLayer.getLayer();
415            dimAmount = 0;
416        } else {
417            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
418            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
419        }
420        final float targetAlpha = mDimLayer.getTargetAlpha();
421        if (targetAlpha != dimAmount) {
422            if (mDimWinAnimator == null) {
423                mDimLayer.hide(DEFAULT_DIM_DURATION);
424            } else {
425                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
426                        ? mDimWinAnimator.mAnimation.computeDurationHint()
427                        : DEFAULT_DIM_DURATION;
428                if (targetAlpha > dimAmount) {
429                    duration = getDimBehindFadeDuration(duration);
430                }
431                mDimLayer.show(dimLayer, dimAmount, duration);
432            }
433        } else if (mDimLayer.getLayer() != dimLayer) {
434            mDimLayer.setLayer(dimLayer);
435        }
436        if (mDimLayer.isAnimating()) {
437            if (!mService.okToDisplay()) {
438                // Jump to the end of the animation.
439                mDimLayer.show();
440            } else {
441                return mDimLayer.stepAnimation();
442            }
443        }
444        return false;
445    }
446
447    void resetDimmingTag() {
448        mDimmingTag = false;
449    }
450
451    void setDimmingTag() {
452        mDimmingTag = true;
453    }
454
455    boolean testDimmingTag() {
456        return mDimmingTag;
457    }
458
459    boolean isDimming() {
460        return mDimLayer.isDimming();
461    }
462
463    boolean isDimming(WindowStateAnimator winAnimator) {
464        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
465    }
466
467    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
468        // Only set dim params on the highest dimmed layer.
469        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
470        if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
471                || !mDimWinAnimator.mSurfaceShown
472                || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
473            mDimWinAnimator = newWinAnimator;
474            if (mDimWinAnimator.mWin.mAppToken == null
475                    && !mFullscreen && mDisplayContent != null) {
476                // Dim should cover the entire screen for system windows.
477                mDisplayContent.getLogicalDisplayRect(mTmpRect);
478                mDimLayer.setBounds(mTmpRect);
479            }
480        }
481    }
482
483    void stopDimmingIfNeeded() {
484        if (!mDimmingTag && isDimming()) {
485            mDimWinAnimator = null;
486            mDimLayer.setBounds(mBounds);
487        }
488    }
489
490    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
491        int animLayer = winAnimator.mAnimLayer;
492        if (mAnimationBackgroundAnimator == null
493                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
494            mAnimationBackgroundAnimator = winAnimator;
495            animLayer = mService.adjustAnimationBackground(winAnimator);
496            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
497                    ((color >> 24) & 0xff) / 255f, 0);
498        }
499    }
500
501    void switchUser() {
502        int top = mTasks.size();
503        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
504            Task task = mTasks.get(taskNdx);
505            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
506                mTasks.remove(taskNdx);
507                mTasks.add(task);
508                --top;
509            }
510        }
511    }
512
513    void close() {
514        if (mAnimationBackgroundSurface != null) {
515            mAnimationBackgroundSurface.destroySurface();
516            mAnimationBackgroundSurface = null;
517        }
518        if (mDimLayer != null) {
519            mDimLayer.destroySurface();
520            mDimLayer = null;
521        }
522        mDisplayContent = null;
523    }
524
525    public void dump(String prefix, PrintWriter pw) {
526        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
527        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
528        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
529            pw.print(prefix); pw.println(mTasks.get(taskNdx));
530        }
531        if (mAnimationBackgroundSurface.isDimming()) {
532            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
533            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
534        }
535        if (mDimLayer.isDimming()) {
536            pw.print(prefix); pw.println("mDimLayer:");
537            mDimLayer.printTo(prefix + " ", pw);
538            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
539        }
540        if (!mExitingAppTokens.isEmpty()) {
541            pw.println();
542            pw.println("  Exiting application tokens:");
543            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
544                WindowToken token = mExitingAppTokens.get(i);
545                pw.print("  Exiting App #"); pw.print(i);
546                pw.print(' '); pw.print(token);
547                pw.println(':');
548                token.dump(pw, "    ");
549            }
550        }
551    }
552
553    @Override
554    public String toString() {
555        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
556    }
557}
558