TaskStack.java revision ddc1cb2c15549ed23dce9d416680a009fa6ae23c
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.EventLog;
26import android.util.IntArray;
27import android.util.Slog;
28import android.view.DisplayInfo;
29
30import com.android.server.EventLogTags;
31
32import java.io.PrintWriter;
33import java.util.ArrayList;
34import java.util.List;
35
36public class TaskStack implements DimLayer.DimLayerUser {
37    /** Unique identifier */
38    final int mStackId;
39
40    /** The service */
41    private final WindowManagerService mService;
42
43    /** The display this stack sits under. */
44    private DisplayContent mDisplayContent;
45
46    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
47     * mTaskHistory in the ActivityStack with the same mStackId */
48    private final ArrayList<Task> mTasks = new ArrayList<>();
49
50    /** For comparison with DisplayContent bounds. */
51    private Rect mTmpRect = new Rect();
52
53    /** Content limits relative to the DisplayContent this sits in. */
54    private Rect mBounds = new Rect();
55
56    /** Whether mBounds is fullscreen */
57    private boolean mFullscreen = true;
58
59    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
60    DimLayer mAnimationBackgroundSurface;
61
62    /** The particular window with an Animation with non-zero background color. */
63    WindowStateAnimator mAnimationBackgroundAnimator;
64
65    /** Application tokens that are exiting, but still on screen for animations. */
66    final AppTokenList mExitingAppTokens = new AppTokenList();
67
68    /** Detach this stack from its display when animation completes. */
69    boolean mDeferDetach;
70
71    TaskStack(WindowManagerService service, int stackId) {
72        mService = service;
73        mStackId = stackId;
74        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
75    }
76
77    DisplayContent getDisplayContent() {
78        return mDisplayContent;
79    }
80
81    ArrayList<Task> getTasks() {
82        return mTasks;
83    }
84
85    void resizeWindows() {
86        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
87            mTasks.get(taskNdx).resizeWindows();
88        }
89    }
90
91    /**
92     * Set the bounds of the stack and its containing tasks.
93     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
94     * @param changedTaskIds Output list of Ids of tasks that changed in bounds.
95     * @param newTaskConfigs Output list of new Configuation of the tasks that changed.
96     * @return True if the stack bounds was changed.
97     * */
98    boolean setBounds(Rect bounds, IntArray changedTaskIds, List<Configuration> newTaskConfigs) {
99        if (!setBounds(bounds)) {
100            return false;
101        }
102
103        // Update bounds of containing tasks.
104        final Rect newBounds = mFullscreen ? null : mBounds;
105        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
106            final Task task = mTasks.get(taskNdx);
107            if (task.setBounds(newBounds)) {
108                changedTaskIds.add(task.mTaskId);
109                newTaskConfigs.add(task.mOverrideConfig);
110            }
111        }
112        return true;
113    }
114
115    private boolean setBounds(Rect bounds) {
116        boolean oldFullscreen = mFullscreen;
117        if (mDisplayContent != null) {
118            mDisplayContent.getLogicalDisplayRect(mTmpRect);
119            if (bounds == null) {
120                bounds = mTmpRect;
121                mFullscreen = true;
122            } else {
123                // ensure bounds are entirely within the display rect
124                if (!bounds.intersect(mTmpRect)) {
125                    // Can't set bounds outside the containing display.. Sorry!
126                    return false;
127                }
128                mFullscreen = mTmpRect.equals(bounds);
129            }
130        }
131
132        if (bounds == null) {
133            // Can't set to fullscreen if we don't have a display to get bounds from...
134            return false;
135        }
136        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
137            return false;
138        }
139
140        mAnimationBackgroundSurface.setBounds(bounds);
141        mBounds.set(bounds);
142        return true;
143    }
144
145    void getBounds(Rect out) {
146        out.set(mBounds);
147    }
148
149    void updateDisplayInfo() {
150        if (mDisplayContent != null) {
151            mDisplayContent.getLogicalDisplayRect(mTmpRect);
152            mAnimationBackgroundSurface.setBounds(mTmpRect);
153            for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
154                mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
155            }
156        }
157    }
158
159    boolean isAnimating() {
160        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
161            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
162            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
163                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
164                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
165                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
166                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
167                        return true;
168                    }
169                }
170            }
171        }
172        return false;
173    }
174
175    void addTask(Task task, boolean toTop) {
176        addTask(task, toTop, task.showForAllUsers());
177    }
178
179    /**
180     * Put a Task in this stack. Used for adding and moving.
181     * @param task The task to add.
182     * @param toTop Whether to add it to the top or bottom.
183     * @param showForAllUsers Whether to show the task regardless of the current user.
184     */
185    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
186        positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
187    }
188
189    void positionTask(Task task, int position, boolean showForAllUsers) {
190        final boolean canShowTask =
191                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
192        mTasks.remove(task);
193        int stackSize = mTasks.size();
194        int minPosition = 0;
195        int maxPosition = stackSize;
196
197        if (canShowTask) {
198            minPosition = computeMinPosition(minPosition, stackSize);
199        } else {
200            maxPosition = computeMaxPosition(maxPosition);
201        }
202        // Reset position based on minimum/maximum possible positions.
203        position = Math.min(Math.max(position, minPosition), maxPosition);
204
205        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG,
206                "positionTask: task=" + task + " position=" + position);
207        mTasks.add(position, task);
208
209        task.mStack = this;
210        task.updateDisplayInfo(mDisplayContent);
211        boolean toTop = position == mTasks.size() - 1;
212        if (toTop) {
213            mDisplayContent.moveStack(this, true);
214        }
215        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
216    }
217
218    /** Calculate the minimum possible position for a task that can be shown to the user.
219     *  The minimum position will be above all other tasks that can't be shown.
220     *  @param minPosition The minimum position the caller is suggesting.
221     *                  We will start adjusting up from here.
222     *  @param size The size of the current task list.
223     */
224    private int computeMinPosition(int minPosition, int size) {
225        while (minPosition < size) {
226            final Task tmpTask = mTasks.get(minPosition);
227            final boolean canShowTmpTask =
228                    tmpTask.showForAllUsers()
229                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
230            if (canShowTmpTask) {
231                break;
232            }
233            minPosition++;
234        }
235        return minPosition;
236    }
237
238    /** Calculate the maximum possible position for a task that can't be shown to the user.
239     *  The maximum position will be below all other tasks that can be shown.
240     *  @param maxPosition The maximum position the caller is suggesting.
241     *                  We will start adjusting down from here.
242     */
243    private int computeMaxPosition(int maxPosition) {
244        while (maxPosition > 0) {
245            final Task tmpTask = mTasks.get(maxPosition - 1);
246            final boolean canShowTmpTask =
247                    tmpTask.showForAllUsers()
248                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
249            if (!canShowTmpTask) {
250                break;
251            }
252            maxPosition--;
253        }
254        return maxPosition;
255    }
256
257    void moveTaskToTop(Task task) {
258        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
259                + Debug.getCallers(6));
260        mTasks.remove(task);
261        addTask(task, true);
262    }
263
264    void moveTaskToBottom(Task task) {
265        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
266        mTasks.remove(task);
267        addTask(task, false);
268    }
269
270    /**
271     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
272     * back.
273     * @param task The Task to delete.
274     */
275    void removeTask(Task task) {
276        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
277        mTasks.remove(task);
278        if (mDisplayContent != null) {
279            if (mTasks.isEmpty()) {
280                mDisplayContent.moveStack(this, false);
281            }
282            mDisplayContent.layoutNeeded = true;
283        }
284        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
285            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
286            if (wtoken.mTask == task) {
287                wtoken.mIsExiting = false;
288                mExitingAppTokens.remove(appNdx);
289            }
290        }
291    }
292
293    void attachDisplayContent(DisplayContent displayContent) {
294        if (mDisplayContent != null) {
295            throw new IllegalStateException("attachDisplayContent: Already attached");
296        }
297
298        mDisplayContent = displayContent;
299        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
300        updateDisplayInfo();
301    }
302
303    void detachDisplay() {
304        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
305
306        boolean doAnotherLayoutPass = false;
307        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
308            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
309            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
310                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
311                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
312                    mService.removeWindowInnerLocked(appWindows.get(winNdx));
313                    doAnotherLayoutPass = true;
314                }
315            }
316        }
317        if (doAnotherLayoutPass) {
318            mService.requestTraversalLocked();
319        }
320
321        close();
322
323        mDisplayContent = null;
324    }
325
326    void resetAnimationBackgroundAnimator() {
327        mAnimationBackgroundAnimator = null;
328        mAnimationBackgroundSurface.hide();
329    }
330
331    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
332        int animLayer = winAnimator.mAnimLayer;
333        if (mAnimationBackgroundAnimator == null
334                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
335            mAnimationBackgroundAnimator = winAnimator;
336            animLayer = mService.adjustAnimationBackground(winAnimator);
337            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
338                    ((color >> 24) & 0xff) / 255f, 0);
339        }
340    }
341
342    void switchUser() {
343        int top = mTasks.size();
344        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
345            Task task = mTasks.get(taskNdx);
346            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
347                mTasks.remove(taskNdx);
348                mTasks.add(task);
349                --top;
350            }
351        }
352    }
353
354    void close() {
355        if (mAnimationBackgroundSurface != null) {
356            mAnimationBackgroundSurface.destroySurface();
357            mAnimationBackgroundSurface = null;
358        }
359        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
360            mTasks.get(taskNdx).close();
361        }
362    }
363
364    public void dump(String prefix, PrintWriter pw) {
365        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
366        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
367        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
368            pw.print(prefix);
369            mTasks.get(taskNdx).printTo(prefix + " ", pw);
370        }
371        if (mAnimationBackgroundSurface.isDimming()) {
372            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
373            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
374        }
375        if (!mExitingAppTokens.isEmpty()) {
376            pw.println();
377            pw.println("  Exiting application tokens:");
378            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
379                WindowToken token = mExitingAppTokens.get(i);
380                pw.print("  Exiting App #"); pw.print(i);
381                pw.print(' '); pw.print(token);
382                pw.println(':');
383                token.dump(pw, "    ");
384            }
385        }
386    }
387
388    @Override
389    public boolean isFullscreen() {
390        return mFullscreen;
391    }
392
393    @Override
394    public DisplayInfo getDisplayInfo() {
395        return mDisplayContent.getDisplayInfo();
396    }
397
398    @Override
399    public String toString() {
400        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
401    }
402}
403