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