TaskStack.java revision b605f4af143bd129ec84bdafe89400cdc022ddc2
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    // When true this stack is at the top of the screen and should be layed out to extend under
97    // the status bar.
98    boolean mUnderStatusBar;
99
100    // Device rotation as of the last time {@link #mBounds} was set.
101    int mRotation;
102
103    TaskStack(WindowManagerService service, int stackId) {
104        mService = service;
105        mStackId = stackId;
106        mOverrideConfig = Configuration.EMPTY;
107        mForceFullscreen = false;
108        mPreForceFullscreenBounds = new Rect();
109        mUnderStatusBar = true;
110        // TODO: remove bounds from log, they are always 0.
111        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
112                mBounds.right, mBounds.bottom);
113    }
114
115    DisplayContent getDisplayContent() {
116        return mDisplayContent;
117    }
118
119    ArrayList<Task> getTasks() {
120        return mTasks;
121    }
122
123    void resizeWindows() {
124        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
125        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
126            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
127            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
128                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
129                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
130                    final WindowState win = windows.get(winNdx);
131                    if (!resizingWindows.contains(win)) {
132                        if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
133                                "setBounds: Resizing " + win);
134                        resizingWindows.add(win);
135                    }
136                }
137            }
138        }
139    }
140
141    /** Set the stack bounds. Passing in null sets the bounds to fullscreen. */
142    boolean setBounds(Rect bounds) {
143        boolean oldFullscreen = mFullscreen;
144        int rotation = Surface.ROTATION_0;
145        if (mDisplayContent != null) {
146            mDisplayContent.getLogicalDisplayRect(mTmpRect);
147            rotation = mDisplayContent.getDisplayInfo().rotation;
148            if (bounds == null) {
149                bounds = mTmpRect;
150                mFullscreen = true;
151            } else {
152                bounds.intersect(mTmpRect); // ensure bounds are entirely within the display rect
153                mFullscreen = mTmpRect.equals(bounds);
154            }
155        }
156
157        if (bounds == null) {
158            // Can't set to fullscreen if we don't have a display to get bounds from...
159            return false;
160        }
161        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
162            return false;
163        }
164
165        mDimLayer.setBounds(bounds);
166        mAnimationBackgroundSurface.setBounds(bounds);
167        mBounds.set(bounds);
168        mUnderStatusBar = (mBounds.top == 0);
169        mRotation = rotation;
170        updateOverrideConfiguration();
171        return true;
172    }
173
174    void getBounds(Rect out) {
175        out.set(mBounds);
176    }
177
178    private void updateOverrideConfiguration() {
179        final Configuration serviceConfig = mService.mCurConfiguration;
180        if (mFullscreen) {
181            mOverrideConfig = Configuration.EMPTY;
182            return;
183        }
184
185        if (mOverrideConfig == Configuration.EMPTY) {
186            mOverrideConfig  = new Configuration();
187        }
188
189        // TODO(multidisplay): Update Dp to that of display stack is on.
190        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
191        mOverrideConfig.screenWidthDp =
192                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
193        mOverrideConfig.screenHeightDp =
194                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
195        mOverrideConfig.smallestScreenWidthDp =
196                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
197        mOverrideConfig.orientation =
198                (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
199                        ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
200    }
201
202    void updateDisplayInfo() {
203        if (mFullscreen) {
204            setBounds(null);
205        } else if (mDisplayContent != null) {
206            final int newRotation = mDisplayContent.getDisplayInfo().rotation;
207            if (mRotation == newRotation) {
208                return;
209            }
210
211            // Device rotation changed. We don't want the stack to move around on the screen when
212            // this happens, so update the stack bounds so it stays in the same place.
213            final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
214            mDisplayContent.getLogicalDisplayRect(mTmpRect);
215            switch (rotationDelta) {
216                case Surface.ROTATION_0:
217                    mTmpRect2.set(mBounds);
218                    break;
219                case Surface.ROTATION_90:
220                    mTmpRect2.top = mTmpRect.bottom - mBounds.right;
221                    mTmpRect2.left = mBounds.top;
222                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
223                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
224                    break;
225                case Surface.ROTATION_180:
226                    mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
227                    mTmpRect2.left = mTmpRect.right - mBounds.right;
228                    mTmpRect2.right = mTmpRect2.left + mBounds.width();
229                    mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
230                    break;
231                case Surface.ROTATION_270:
232                    mTmpRect2.top = mBounds.left;
233                    mTmpRect2.left = mTmpRect.right - mBounds.bottom;
234                    mTmpRect2.right = mTmpRect2.left + mBounds.height();
235                    mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
236                    break;
237            }
238            setBounds(mTmpRect2);
239        }
240    }
241
242    boolean isFullscreen() {
243        return mFullscreen;
244    }
245
246    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
247     * Returns true if something happened.
248     */
249    boolean forceFullscreen(boolean forceFullscreen) {
250        if (mForceFullscreen == forceFullscreen) {
251            return false;
252        }
253        mForceFullscreen = forceFullscreen;
254        if (forceFullscreen) {
255            if (mFullscreen) {
256                return false;
257            }
258            mPreForceFullscreenBounds.set(mBounds);
259            return setBounds(null);
260        } else {
261            if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) {
262                return false;
263            }
264            return setBounds(mPreForceFullscreenBounds);
265        }
266    }
267
268    boolean isAnimating() {
269        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
270            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
271            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
272                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
273                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
274                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
275                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
276                        return true;
277                    }
278                }
279            }
280        }
281        return false;
282    }
283
284    /**
285     * Put a Task in this stack. Used for adding and moving.
286     * @param task The task to add.
287     * @param toTop Whether to add it to the top or bottom.
288     */
289    void addTask(Task task, boolean toTop) {
290        int stackNdx;
291        if (!toTop) {
292            stackNdx = 0;
293        } else {
294            stackNdx = mTasks.size();
295            if (!mService.isCurrentProfileLocked(task.mUserId)) {
296                // Place the task below all current user tasks.
297                while (--stackNdx >= 0) {
298                    if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
299                        break;
300                    }
301                }
302                // Put it above first non-current user task.
303                ++stackNdx;
304            }
305        }
306        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
307                + " pos=" + stackNdx);
308        mTasks.add(stackNdx, task);
309
310        task.mStack = this;
311        if (toTop) {
312            mDisplayContent.moveStack(this, true);
313        }
314        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
315    }
316
317    void moveTaskToTop(Task task) {
318        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
319                + Debug.getCallers(6));
320        mTasks.remove(task);
321        addTask(task, true);
322    }
323
324    void moveTaskToBottom(Task task) {
325        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
326        mTasks.remove(task);
327        addTask(task, false);
328    }
329
330    /**
331     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
332     * back.
333     * @param task The Task to delete.
334     */
335    void removeTask(Task task) {
336        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
337        mTasks.remove(task);
338        if (mDisplayContent != null) {
339            if (mTasks.isEmpty()) {
340                mDisplayContent.moveStack(this, false);
341            }
342            mDisplayContent.layoutNeeded = true;
343        }
344        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
345            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
346            if (wtoken.mTask == task) {
347                wtoken.mIsExiting = false;
348                mExitingAppTokens.remove(appNdx);
349            }
350        }
351    }
352
353    void attachDisplayContent(DisplayContent displayContent) {
354        if (mDisplayContent != null) {
355            throw new IllegalStateException("attachDisplayContent: Already attached");
356        }
357
358        mDisplayContent = displayContent;
359        mDimLayer = new DimLayer(mService, this, displayContent);
360        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
361        updateDisplayInfo();
362    }
363
364    void detachDisplay() {
365        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
366
367        boolean doAnotherLayoutPass = false;
368        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
369            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
370            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
371                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
372                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
373                    mService.removeWindowInnerLocked(appWindows.get(winNdx));
374                    doAnotherLayoutPass = true;
375                }
376            }
377        }
378        if (doAnotherLayoutPass) {
379            mService.requestTraversalLocked();
380        }
381
382        mAnimationBackgroundSurface.destroySurface();
383        mAnimationBackgroundSurface = null;
384        mDimLayer.destroySurface();
385        mDimLayer = null;
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)) {
502                mTasks.remove(taskNdx);
503                mTasks.add(task);
504                --top;
505            }
506        }
507    }
508
509    void close() {
510        mDimLayer.mDimSurface.destroy();
511        mAnimationBackgroundSurface.mDimSurface.destroy();
512    }
513
514    public void dump(String prefix, PrintWriter pw) {
515        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
516        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
517        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
518            pw.print(prefix); pw.println(mTasks.get(taskNdx));
519        }
520        if (mAnimationBackgroundSurface.isDimming()) {
521            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
522            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
523        }
524        if (mDimLayer.isDimming()) {
525            pw.print(prefix); pw.println("mDimLayer:");
526            mDimLayer.printTo(prefix + " ", pw);
527            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
528        }
529        if (!mExitingAppTokens.isEmpty()) {
530            pw.println();
531            pw.println("  Exiting application tokens:");
532            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
533                WindowToken token = mExitingAppTokens.get(i);
534                pw.print("  Exiting App #"); pw.print(i);
535                pw.print(' '); pw.print(token);
536                pw.println(':');
537                token.dump(pw, "    ");
538            }
539        }
540    }
541
542    @Override
543    public String toString() {
544        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
545    }
546}
547