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