TaskStack.java revision 9d3de4cfb42519fefe9d8b03c38ba440bd6bc886
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;
29
30import com.android.server.EventLogTags;
31
32import java.io.PrintWriter;
33import java.util.ArrayList;
34
35public class TaskStack {
36    /** Amount of time in milliseconds to animate the dim surface from one value to another,
37     * when no window animation is driving it. */
38    private static final int DEFAULT_DIM_DURATION = 200;
39
40    /** Unique identifier */
41    final int mStackId;
42
43    /** The service */
44    private final WindowManagerService mService;
45
46    /** The display this stack sits under. */
47    private DisplayContent mDisplayContent;
48
49    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
50     * mTaskHistory in the ActivityStack with the same mStackId */
51    private final ArrayList<Task> mTasks = new ArrayList<Task>();
52
53    /** For comparison with DisplayContent bounds. */
54    private Rect mTmpRect = new Rect();
55
56    /** Content limits relative to the DisplayContent this sits in. */
57    private Rect mBounds = new Rect();
58
59    /** Whether mBounds is fullscreen */
60    private boolean mFullscreen = true;
61
62    /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
63    private DimLayer mDimLayer;
64
65    /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
66    WindowStateAnimator mDimWinAnimator;
67
68    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
69    DimLayer mAnimationBackgroundSurface;
70
71    /** The particular window with an Animation with non-zero background color. */
72    WindowStateAnimator mAnimationBackgroundAnimator;
73
74    /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
75     * then stop any dimming. */
76    boolean mDimmingTag;
77
78    /** Application tokens that are exiting, but still on screen for animations. */
79    final AppTokenList mExitingAppTokens = new AppTokenList();
80
81    /** Detach this stack from its display when animation completes. */
82    boolean mDeferDetach;
83
84    // Contains configurations settings that are different from the global configuration due to
85    // stack specific operations. E.g. {@link #setBounds}.
86    Configuration mOverrideConfig;
87    // True if the stack was forced to fullscreen disregarding the override configuration.
88    private boolean mForceFullscreen;
89    // The {@link #mBounds} before the stack was forced to fullscreen. Will be restored as the
90    // stack bounds once the stack is no longer forced to fullscreen.
91    final private Rect mPreForceFullscreenBounds;
92
93    TaskStack(WindowManagerService service, int stackId) {
94        mService = service;
95        mStackId = stackId;
96        mOverrideConfig = Configuration.EMPTY;
97        mForceFullscreen = false;
98        mPreForceFullscreenBounds = new Rect();
99        // TODO: remove bounds from log, they are always 0.
100        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
101                mBounds.right, mBounds.bottom);
102    }
103
104    DisplayContent getDisplayContent() {
105        return mDisplayContent;
106    }
107
108    ArrayList<Task> getTasks() {
109        return mTasks;
110    }
111
112    void resizeWindows() {
113        final boolean underStatusBar = mBounds.top == 0;
114
115        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
116        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
117            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
118            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
119                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
120                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
121                    final WindowState win = windows.get(winNdx);
122                    if (!resizingWindows.contains(win)) {
123                        if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
124                                "setBounds: Resizing " + win);
125                        resizingWindows.add(win);
126                    }
127                    win.mUnderStatusBar = underStatusBar;
128                }
129            }
130        }
131    }
132
133    /** Set the stack bounds. Passing in null sets the bounds to fullscreen. */
134    boolean setBounds(Rect bounds) {
135        boolean oldFullscreen = mFullscreen;
136        if (mDisplayContent != null) {
137            mDisplayContent.getLogicalDisplayRect(mTmpRect);
138            if (bounds == null) {
139                bounds = mTmpRect;
140                mFullscreen = true;
141            } else {
142                bounds.intersect(mTmpRect); // ensure bounds are entirely within the display rect
143                mFullscreen = mTmpRect.equals(bounds);
144            }
145        }
146
147        if (bounds == null) {
148            // Can set to fullscreen if we don't have a display to get bounds from...
149            return false;
150        }
151        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
152            return false;
153        }
154
155        mDimLayer.setBounds(bounds);
156        mAnimationBackgroundSurface.setBounds(bounds);
157        mBounds.set(bounds);
158        updateOverrideConfiguration();
159        return true;
160    }
161
162    void getBounds(Rect out) {
163        out.set(mBounds);
164    }
165
166    private void updateOverrideConfiguration() {
167        final Configuration serviceConfig = mService.mCurConfiguration;
168        if (mFullscreen) {
169            mOverrideConfig = Configuration.EMPTY;
170            return;
171        }
172
173        if (mOverrideConfig == Configuration.EMPTY) {
174            mOverrideConfig  = new Configuration();
175        }
176
177        // TODO(multidisplay): Update Dp to that of display stack is on.
178        final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
179        mOverrideConfig.screenWidthDp =
180                Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
181        mOverrideConfig.screenHeightDp =
182                Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
183        mOverrideConfig.smallestScreenWidthDp =
184                Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
185    }
186
187    void updateDisplayInfo() {
188        if (mFullscreen && mDisplayContent != null) {
189            mDisplayContent.getLogicalDisplayRect(mTmpRect);
190            setBounds(mTmpRect);
191        }
192    }
193
194    boolean isFullscreen() {
195        return mFullscreen;
196    }
197
198    /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
199     * Returns true if something happened.
200     */
201    boolean forceFullscreen(boolean forceFullscreen) {
202        if (mForceFullscreen == forceFullscreen) {
203            return false;
204        }
205        mForceFullscreen = forceFullscreen;
206        if (forceFullscreen) {
207            if (mFullscreen) {
208                return false;
209            }
210            mPreForceFullscreenBounds.set(mBounds);
211            return setBounds(null);
212        } else {
213            if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) {
214                return false;
215            }
216            return setBounds(mPreForceFullscreenBounds);
217        }
218    }
219
220    boolean isAnimating() {
221        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
222            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
223            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
224                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
225                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
226                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
227                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
228                        return true;
229                    }
230                }
231            }
232        }
233        return false;
234    }
235
236    /**
237     * Put a Task in this stack. Used for adding and moving.
238     * @param task The task to add.
239     * @param toTop Whether to add it to the top or bottom.
240     */
241    void addTask(Task task, boolean toTop) {
242        int stackNdx;
243        if (!toTop) {
244            stackNdx = 0;
245        } else {
246            stackNdx = mTasks.size();
247            if (!mService.isCurrentProfileLocked(task.mUserId)) {
248                // Place the task below all current user tasks.
249                while (--stackNdx >= 0) {
250                    if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
251                        break;
252                    }
253                }
254                // Put it above first non-current user task.
255                ++stackNdx;
256            }
257        }
258        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
259                + " pos=" + stackNdx);
260        mTasks.add(stackNdx, task);
261
262        task.mStack = this;
263        if (toTop) {
264            mDisplayContent.moveStack(this, true);
265        }
266        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
267    }
268
269    void moveTaskToTop(Task task) {
270        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
271                + Debug.getCallers(6));
272        mTasks.remove(task);
273        addTask(task, true);
274    }
275
276    void moveTaskToBottom(Task task) {
277        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
278        mTasks.remove(task);
279        addTask(task, false);
280    }
281
282    /**
283     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
284     * back.
285     * @param task The Task to delete.
286     */
287    void removeTask(Task task) {
288        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
289        mTasks.remove(task);
290        if (mDisplayContent != null) {
291            if (mTasks.isEmpty()) {
292                mDisplayContent.moveStack(this, false);
293            }
294            mDisplayContent.layoutNeeded = true;
295        }
296        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
297            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
298            if (wtoken.mTask == task) {
299                wtoken.mIsExiting = false;
300                mExitingAppTokens.remove(appNdx);
301            }
302        }
303    }
304
305    void attachDisplayContent(DisplayContent displayContent) {
306        if (mDisplayContent != null) {
307            throw new IllegalStateException("attachDisplayContent: Already attached");
308        }
309
310        mDisplayContent = displayContent;
311        mDimLayer = new DimLayer(mService, this, displayContent);
312        mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
313        updateDisplayInfo();
314    }
315
316    void detachDisplay() {
317        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
318
319        boolean doAnotherLayoutPass = false;
320        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
321            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
322            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
323                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
324                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
325                    mService.removeWindowInnerLocked(null, appWindows.get(winNdx));
326                    doAnotherLayoutPass = true;
327                }
328            }
329        }
330        if (doAnotherLayoutPass) {
331            mService.requestTraversalLocked();
332        }
333
334        mAnimationBackgroundSurface.destroySurface();
335        mAnimationBackgroundSurface = null;
336        mDimLayer.destroySurface();
337        mDimLayer = null;
338        mDisplayContent = null;
339    }
340
341    void resetAnimationBackgroundAnimator() {
342        mAnimationBackgroundAnimator = null;
343        mAnimationBackgroundSurface.hide();
344    }
345
346    private long getDimBehindFadeDuration(long duration) {
347        TypedValue tv = new TypedValue();
348        mService.mContext.getResources().getValue(
349                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
350        if (tv.type == TypedValue.TYPE_FRACTION) {
351            duration = (long)tv.getFraction(duration, duration);
352        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
353            duration = tv.data;
354        }
355        return duration;
356    }
357
358    boolean animateDimLayers() {
359        final int dimLayer;
360        final float dimAmount;
361        if (mDimWinAnimator == null) {
362            dimLayer = mDimLayer.getLayer();
363            dimAmount = 0;
364        } else {
365            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
366            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
367        }
368        final float targetAlpha = mDimLayer.getTargetAlpha();
369        if (targetAlpha != dimAmount) {
370            if (mDimWinAnimator == null) {
371                mDimLayer.hide(DEFAULT_DIM_DURATION);
372            } else {
373                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
374                        ? mDimWinAnimator.mAnimation.computeDurationHint()
375                        : DEFAULT_DIM_DURATION;
376                if (targetAlpha > dimAmount) {
377                    duration = getDimBehindFadeDuration(duration);
378                }
379                mDimLayer.show(dimLayer, dimAmount, duration);
380            }
381        } else if (mDimLayer.getLayer() != dimLayer) {
382            mDimLayer.setLayer(dimLayer);
383        }
384        if (mDimLayer.isAnimating()) {
385            if (!mService.okToDisplay()) {
386                // Jump to the end of the animation.
387                mDimLayer.show();
388            } else {
389                return mDimLayer.stepAnimation();
390            }
391        }
392        return false;
393    }
394
395    void resetDimmingTag() {
396        mDimmingTag = false;
397    }
398
399    void setDimmingTag() {
400        mDimmingTag = true;
401    }
402
403    boolean testDimmingTag() {
404        return mDimmingTag;
405    }
406
407    boolean isDimming() {
408        return mDimLayer.isDimming();
409    }
410
411    boolean isDimming(WindowStateAnimator winAnimator) {
412        return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
413    }
414
415    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
416        // Only set dim params on the highest dimmed layer.
417        final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
418        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
419        if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
420                || !existingDimWinAnimator.mSurfaceShown
421                || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
422            mDimWinAnimator = newWinAnimator;
423        }
424    }
425
426    void stopDimmingIfNeeded() {
427        if (!mDimmingTag && isDimming()) {
428            mDimWinAnimator = null;
429        }
430    }
431
432    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
433        int animLayer = winAnimator.mAnimLayer;
434        if (mAnimationBackgroundAnimator == null
435                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
436            mAnimationBackgroundAnimator = winAnimator;
437            animLayer = mService.adjustAnimationBackground(winAnimator);
438            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
439                    ((color >> 24) & 0xff) / 255f, 0);
440        }
441    }
442
443    void switchUser(int userId) {
444        int top = mTasks.size();
445        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
446            Task task = mTasks.get(taskNdx);
447            if (mService.isCurrentProfileLocked(task.mUserId)) {
448                mTasks.remove(taskNdx);
449                mTasks.add(task);
450                --top;
451            }
452        }
453    }
454
455    void close() {
456        mDimLayer.mDimSurface.destroy();
457        mAnimationBackgroundSurface.mDimSurface.destroy();
458    }
459
460    public void dump(String prefix, PrintWriter pw) {
461        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
462        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
463        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
464            pw.print(prefix); pw.println(mTasks.get(taskNdx));
465        }
466        if (mAnimationBackgroundSurface.isDimming()) {
467            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
468            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
469        }
470        if (mDimLayer.isDimming()) {
471            pw.print(prefix); pw.println("mDimLayer:");
472            mDimLayer.printTo(prefix, pw);
473            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
474        }
475        if (!mExitingAppTokens.isEmpty()) {
476            pw.println();
477            pw.println("  Exiting application tokens:");
478            for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
479                WindowToken token = mExitingAppTokens.get(i);
480                pw.print("  Exiting App #"); pw.print(i);
481                pw.print(' '); pw.print(token);
482                pw.println(':');
483                token.dump(pw, "    ");
484            }
485        }
486    }
487
488    @Override
489    public String toString() {
490        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
491    }
492}
493