TaskStack.java revision 49b80afaf9e71d6b5d4cab26f1459d84d1070f19
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 android.app.ActivityManager.*;
20import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
21import static com.android.server.wm.WindowManagerService.TAG;
22
23import android.annotation.IntDef;
24import android.content.res.Configuration;
25import android.graphics.Rect;
26import android.os.Debug;
27import android.os.RemoteException;
28import android.util.EventLog;
29import android.util.Slog;
30import android.util.SparseArray;
31import android.view.DisplayInfo;
32
33import android.view.Surface;
34import com.android.server.EventLogTags;
35
36import java.io.PrintWriter;
37import java.lang.annotation.Retention;
38import java.lang.annotation.RetentionPolicy;
39import java.util.ArrayList;
40
41public class TaskStack implements DimLayer.DimLayerUser {
42
43    // If the stack should be resized to fullscreen.
44    private static final boolean FULLSCREEN = true;
45
46    /** Unique identifier */
47    final int mStackId;
48
49    /** The service */
50    private final WindowManagerService mService;
51
52    /** The display this stack sits under. */
53    private DisplayContent mDisplayContent;
54
55    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
56     * mTaskHistory in the ActivityStack with the same mStackId */
57    private final ArrayList<Task> mTasks = new ArrayList<>();
58
59    /** For comparison with DisplayContent bounds. */
60    private Rect mTmpRect = new Rect();
61    private Rect TmpRect2 = new Rect();
62
63    /** Content limits relative to the DisplayContent this sits in. */
64    private Rect mBounds = new Rect();
65
66    /** Whether mBounds is fullscreen */
67    private boolean mFullscreen = true;
68
69    // Device rotation as of the last time {@link #mBounds} was set.
70    int mRotation;
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    /** 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    static final int DOCKED_INVALID = -1;
85    static final int DOCKED_LEFT = 1;
86    static final int DOCKED_TOP = 2;
87    static final int DOCKED_RIGHT = 3;
88    static final int DOCKED_BOTTOM = 4;
89    @IntDef({
90            DOCKED_INVALID,
91            DOCKED_LEFT,
92            DOCKED_TOP,
93            DOCKED_RIGHT,
94            DOCKED_BOTTOM})
95    @Retention(RetentionPolicy.SOURCE)
96    @interface DockSide {}
97
98    TaskStack(WindowManagerService service, int stackId) {
99        mService = service;
100        mStackId = stackId;
101        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
102    }
103
104    DisplayContent getDisplayContent() {
105        return mDisplayContent;
106    }
107
108    ArrayList<Task> getTasks() {
109        return mTasks;
110    }
111
112    void resizeWindows() {
113        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
114            mTasks.get(taskNdx).resizeWindows();
115        }
116    }
117
118    boolean allowTaskResize() {
119        return mStackId == FREEFORM_WORKSPACE_STACK_ID
120                || mStackId == DOCKED_STACK_ID;
121    }
122
123    /**
124     * Set the bounds of the stack and its containing tasks.
125     * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
126     * @param configs Configuration for individual tasks, keyed by task id.
127     * @param taskBounds Bounds for individual tasks, keyed by task id.
128     * @return True if the stack bounds was changed.
129     * */
130    boolean setBounds(
131            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
132        if (!setBounds(stackBounds)) {
133            return false;
134        }
135
136        // Update bounds of containing tasks.
137        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
138            final Task task = mTasks.get(taskNdx);
139            Configuration config = configs.get(task.mTaskId);
140            if (config != null) {
141                Rect bounds = taskBounds.get(task.mTaskId);
142                task.setBounds(bounds, config);
143            } else {
144                Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?");
145            }
146        }
147        return true;
148    }
149
150    private boolean setBounds(Rect bounds) {
151        boolean oldFullscreen = mFullscreen;
152        int rotation = Surface.ROTATION_0;
153        if (mDisplayContent != null) {
154            mDisplayContent.getLogicalDisplayRect(mTmpRect);
155            rotation = mDisplayContent.getDisplayInfo().rotation;
156            if (bounds == null) {
157                bounds = mTmpRect;
158                mFullscreen = true;
159            } else {
160                // ensure bounds are entirely within the display rect
161                if (!bounds.intersect(mTmpRect)) {
162                    // Can't set bounds outside the containing display.. Sorry!
163                    return false;
164                }
165                mFullscreen = mTmpRect.equals(bounds);
166            }
167        }
168
169        if (bounds == null) {
170            // Can't set to fullscreen if we don't have a display to get bounds from...
171            return false;
172        }
173        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
174            return false;
175        }
176
177        mAnimationBackgroundSurface.setBounds(bounds);
178        mBounds.set(bounds);
179        mRotation = rotation;
180        return true;
181    }
182
183    void getBounds(Rect out) {
184        out.set(mBounds);
185    }
186
187    void updateDisplayInfo(Rect bounds) {
188        if (mDisplayContent != null) {
189            if (bounds != null) {
190                setBounds(bounds);
191            } else if (mFullscreen) {
192                setBounds(null);
193            } else {
194                TmpRect2.set(mBounds);
195                mDisplayContent.rotateBounds(
196                        mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2);
197                setBounds(TmpRect2);
198            }
199            for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
200                mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
201            }
202        }
203    }
204
205    boolean isAnimating() {
206        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
207            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
208            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
209                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
210                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
211                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
212                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
213                        return true;
214                    }
215                }
216            }
217        }
218        return false;
219    }
220
221    void addTask(Task task, boolean toTop) {
222        addTask(task, toTop, task.showForAllUsers());
223    }
224
225    /**
226     * Put a Task in this stack. Used for adding and moving.
227     * @param task The task to add.
228     * @param toTop Whether to add it to the top or bottom.
229     * @param showForAllUsers Whether to show the task regardless of the current user.
230     */
231    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
232        positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
233    }
234
235    void positionTask(Task task, int position, boolean showForAllUsers) {
236        final boolean canShowTask =
237                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
238        mTasks.remove(task);
239        int stackSize = mTasks.size();
240        int minPosition = 0;
241        int maxPosition = stackSize;
242
243        if (canShowTask) {
244            minPosition = computeMinPosition(minPosition, stackSize);
245        } else {
246            maxPosition = computeMaxPosition(maxPosition);
247        }
248        // Reset position based on minimum/maximum possible positions.
249        position = Math.min(Math.max(position, minPosition), maxPosition);
250
251        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG,
252                "positionTask: task=" + task + " position=" + position);
253        mTasks.add(position, task);
254
255        task.mStack = this;
256        task.updateDisplayInfo(mDisplayContent);
257        boolean toTop = position == mTasks.size() - 1;
258        if (toTop) {
259            mDisplayContent.moveStack(this, true);
260        }
261        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
262    }
263
264    /** Calculate the minimum possible position for a task that can be shown to the user.
265     *  The minimum position will be above all other tasks that can't be shown.
266     *  @param minPosition The minimum position the caller is suggesting.
267     *                  We will start adjusting up from here.
268     *  @param size The size of the current task list.
269     */
270    private int computeMinPosition(int minPosition, int size) {
271        while (minPosition < size) {
272            final Task tmpTask = mTasks.get(minPosition);
273            final boolean canShowTmpTask =
274                    tmpTask.showForAllUsers()
275                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
276            if (canShowTmpTask) {
277                break;
278            }
279            minPosition++;
280        }
281        return minPosition;
282    }
283
284    /** Calculate the maximum possible position for a task that can't be shown to the user.
285     *  The maximum position will be below all other tasks that can be shown.
286     *  @param maxPosition The maximum position the caller is suggesting.
287     *                  We will start adjusting down from here.
288     */
289    private int computeMaxPosition(int maxPosition) {
290        while (maxPosition > 0) {
291            final Task tmpTask = mTasks.get(maxPosition - 1);
292            final boolean canShowTmpTask =
293                    tmpTask.showForAllUsers()
294                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
295            if (!canShowTmpTask) {
296                break;
297            }
298            maxPosition--;
299        }
300        return maxPosition;
301    }
302
303    void moveTaskToTop(Task task) {
304        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
305                + Debug.getCallers(6));
306        mTasks.remove(task);
307        addTask(task, true);
308    }
309
310    void moveTaskToBottom(Task task) {
311        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
312        mTasks.remove(task);
313        addTask(task, false);
314    }
315
316    /**
317     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
318     * back.
319     * @param task The Task to delete.
320     */
321    void removeTask(Task task) {
322        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
323        mTasks.remove(task);
324        if (mDisplayContent != null) {
325            if (mTasks.isEmpty()) {
326                mDisplayContent.moveStack(this, false);
327            }
328            mDisplayContent.layoutNeeded = true;
329        }
330        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
331            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
332            if (wtoken.mTask == task) {
333                wtoken.mIsExiting = false;
334                mExitingAppTokens.remove(appNdx);
335            }
336        }
337    }
338
339    void attachDisplayContent(DisplayContent displayContent) {
340        if (mDisplayContent != null) {
341            throw new IllegalStateException("attachDisplayContent: Already attached");
342        }
343
344        mDisplayContent = displayContent;
345        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
346
347        Rect bounds = null;
348        final boolean dockedStackExists = mService.mStackIdToStack.get(DOCKED_STACK_ID) != null;
349        if (mStackId == DOCKED_STACK_ID || (dockedStackExists
350                && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
351            // The existence of a docked stack affects the size of any static stack created since
352            // the docked stack occupies a dedicated region on screen.
353            bounds = new Rect();
354            displayContent.getLogicalDisplayRect(mTmpRect);
355            getInitialDockedStackBounds(mTmpRect, bounds, mStackId,
356                    mDisplayContent.mDividerControllerLocked.getWidth() / 2);
357        }
358
359        updateDisplayInfo(bounds);
360
361        if (mStackId == DOCKED_STACK_ID) {
362            // Attaching a docked stack to the display affects the size of all other static
363            // stacks since the docked stack occupies a dedicated region on screen.
364            // Resize existing static stacks so they are pushed to the side of the docked stack.
365            resizeNonDockedStacks(!FULLSCREEN);
366        }
367    }
368
369    /**
370     * Outputs the initial bounds a stack should be given the presence of a docked stack on the
371     * display.
372     * @param displayRect The bounds of the display the docked stack is on.
373     * @param outBounds Output bounds that should be used for the stack.
374     * @param stackId Id of stack we are calculating the bounds for.
375     * @param adjustment
376     */
377    private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId,
378            int adjustment) {
379        // Docked stack start off occupying half the screen space.
380        final boolean dockedStack = stackId == DOCKED_STACK_ID;
381        final boolean splitHorizontally = displayRect.width() > displayRect.height();
382        final boolean topOrLeftCreateMode =
383                WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
384        final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode)
385                || (!dockedStack && !topOrLeftCreateMode);
386        outBounds.set(displayRect);
387        if (placeTopOrLeft) {
388            if (splitHorizontally) {
389                outBounds.right = displayRect.centerX() - adjustment;
390            } else {
391                outBounds.bottom = displayRect.centerY() - adjustment;
392            }
393        } else {
394            if (splitHorizontally) {
395                outBounds.left = displayRect.centerX() + adjustment;
396            } else {
397                outBounds.top = displayRect.centerY() + adjustment;
398            }
399        }
400    }
401
402    /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size
403     * based on the presence of a docked stack.
404     * @param fullscreen If true the stacks will be resized to fullscreen, else they will be
405     *                   resized to the appropriate size based on the presence of a docked stack.
406     */
407    private void resizeNonDockedStacks(boolean fullscreen) {
408        mDisplayContent.getLogicalDisplayRect(mTmpRect);
409        if (!fullscreen) {
410            getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID,
411                    mDisplayContent.mDividerControllerLocked.getWidth());
412        }
413
414        final int count = mService.mStackIdToStack.size();
415        for (int i = 0; i < count; i++) {
416            final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
417            final int otherStackId = otherStack.mStackId;
418            if (otherStackId != DOCKED_STACK_ID
419                    && otherStackId >= FIRST_STATIC_STACK_ID
420                    && otherStackId <= LAST_STATIC_STACK_ID) {
421                try {
422                    mService.mActivityManager.resizeStack(otherStackId, mTmpRect);
423                } catch (RemoteException e) {
424                    // This will not happen since we are in the same process.
425                }
426            }
427        }
428    }
429
430    void detachDisplay() {
431        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
432
433        boolean doAnotherLayoutPass = false;
434        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
435            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
436            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
437                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
438                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
439                    // We are in the middle of changing the state of displays/stacks/tasks. We need
440                    // to finish that, before we let layout interfere with it.
441                    mService.removeWindowLocked(appWindows.get(winNdx));
442                    doAnotherLayoutPass = true;
443                }
444            }
445        }
446        if (doAnotherLayoutPass) {
447            mService.mWindowPlacerLocked.requestTraversal();
448        }
449
450        if (mStackId == DOCKED_STACK_ID) {
451            // Docked stack was detached from the display, so we no longer need to restrict the
452            // region of the screen other static stacks occupy. Go ahead and make them fullscreen.
453            resizeNonDockedStacks(FULLSCREEN);
454        }
455
456        close();
457    }
458
459    void resetAnimationBackgroundAnimator() {
460        mAnimationBackgroundAnimator = null;
461        mAnimationBackgroundSurface.hide();
462    }
463
464    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
465        int animLayer = winAnimator.mAnimLayer;
466        if (mAnimationBackgroundAnimator == null
467                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
468            mAnimationBackgroundAnimator = winAnimator;
469            animLayer = mService.adjustAnimationBackground(winAnimator);
470            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
471                    ((color >> 24) & 0xff) / 255f, 0);
472        }
473    }
474
475    void switchUser() {
476        int top = mTasks.size();
477        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
478            Task task = mTasks.get(taskNdx);
479            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
480                mTasks.remove(taskNdx);
481                mTasks.add(task);
482                --top;
483            }
484        }
485    }
486
487    void close() {
488        if (mAnimationBackgroundSurface != null) {
489            mAnimationBackgroundSurface.destroySurface();
490            mAnimationBackgroundSurface = null;
491        }
492        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
493            mTasks.get(taskNdx).close();
494        }
495        mDisplayContent = null;
496    }
497
498    public void dump(String prefix, PrintWriter pw) {
499        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
500        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
501        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
502            pw.print(prefix);
503            mTasks.get(taskNdx).printTo(prefix + " ", pw);
504        }
505        if (mAnimationBackgroundSurface.isDimming()) {
506            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
507            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
508        }
509        if (!mExitingAppTokens.isEmpty()) {
510            pw.println();
511            pw.println("  Exiting application tokens:");
512            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
513                WindowToken token = mExitingAppTokens.get(i);
514                pw.print("  Exiting App #"); pw.print(i);
515                pw.print(' '); pw.print(token);
516                pw.println(':');
517                token.dump(pw, "    ");
518            }
519        }
520    }
521
522    @Override
523    public boolean isFullscreen() {
524        return mFullscreen;
525    }
526
527    @Override
528    public DisplayInfo getDisplayInfo() {
529        return mDisplayContent.getDisplayInfo();
530    }
531
532    @Override
533    public String toString() {
534        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
535    }
536
537    /**
538     * For docked workspace provides information which side of the screen was the dock anchored.
539     */
540    @DockSide
541    int getDockSide() {
542        if (mStackId != DOCKED_STACK_ID) {
543            return DOCKED_INVALID;
544        }
545        if (mDisplayContent == null) {
546            return DOCKED_INVALID;
547        }
548        mDisplayContent.getLogicalDisplayRect(mTmpRect);
549        final int orientation = mService.mCurConfiguration.orientation;
550        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
551            // Portrait mode, docked either at the top or the bottom.
552            if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
553                return DOCKED_TOP;
554            } else {
555                return DOCKED_BOTTOM;
556            }
557        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
558            // Landscape mode, docked either on the left or on the right.
559            if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
560                return DOCKED_LEFT;
561            } else {
562                return DOCKED_RIGHT;
563            }
564        } else {
565            return DOCKED_INVALID;
566        }
567    }
568}
569