TaskStack.java revision 99db1863a84364339fc5dc9142f15910cdd96ed8
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.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20import static android.app.ActivityManager.DOCKED_STACK_ID;
21import static android.app.ActivityManager.FIRST_STATIC_STACK_ID;
22import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
23import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
24import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
25import static android.app.ActivityManager.PINNED_STACK_ID;
26import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
27import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
28import static com.android.server.wm.WindowManagerService.TAG;
29
30import android.annotation.IntDef;
31import android.content.res.Configuration;
32import android.graphics.Rect;
33import android.os.Debug;
34import android.util.EventLog;
35import android.util.Slog;
36import android.util.SparseArray;
37import android.view.DisplayInfo;
38import android.view.Surface;
39
40import com.android.server.EventLogTags;
41
42import java.io.PrintWriter;
43import java.lang.annotation.Retention;
44import java.lang.annotation.RetentionPolicy;
45import java.util.ArrayList;
46
47public class TaskStack implements DimLayer.DimLayerUser {
48
49    // If the stack should be resized to fullscreen.
50    private static final boolean FULLSCREEN = true;
51
52    /** Unique identifier */
53    final int mStackId;
54
55    /** The service */
56    private final WindowManagerService mService;
57
58    /** The display this stack sits under. */
59    private DisplayContent mDisplayContent;
60
61    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
62     * mTaskHistory in the ActivityStack with the same mStackId */
63    private final ArrayList<Task> mTasks = new ArrayList<>();
64
65    /** For comparison with DisplayContent bounds. */
66    private Rect mTmpRect = new Rect();
67    private Rect mTmpRect2 = new Rect();
68
69    /** Content limits relative to the DisplayContent this sits in. */
70    private Rect mBounds = new Rect();
71
72    /** Whether mBounds is fullscreen */
73    private boolean mFullscreen = true;
74
75    // Device rotation as of the last time {@link #mBounds} was set.
76    int mRotation;
77
78    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
79    DimLayer mAnimationBackgroundSurface;
80
81    /** The particular window with an Animation with non-zero background color. */
82    WindowStateAnimator mAnimationBackgroundAnimator;
83
84    /** Application tokens that are exiting, but still on screen for animations. */
85    final AppTokenList mExitingAppTokens = new AppTokenList();
86
87    /** Detach this stack from its display when animation completes. */
88    boolean mDeferDetach;
89
90    static final int DOCKED_INVALID = -1;
91    static final int DOCKED_LEFT = 1;
92    static final int DOCKED_TOP = 2;
93    static final int DOCKED_RIGHT = 3;
94    static final int DOCKED_BOTTOM = 4;
95
96    @IntDef({
97            DOCKED_INVALID,
98            DOCKED_LEFT,
99            DOCKED_TOP,
100            DOCKED_RIGHT,
101            DOCKED_BOTTOM})
102    @Retention(RetentionPolicy.SOURCE)
103    @interface DockSide {}
104
105    TaskStack(WindowManagerService service, int stackId) {
106        mService = service;
107        mStackId = stackId;
108        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
109    }
110
111    DisplayContent getDisplayContent() {
112        return mDisplayContent;
113    }
114
115    ArrayList<Task> getTasks() {
116        return mTasks;
117    }
118
119    void resizeWindows() {
120        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
121            mTasks.get(taskNdx).resizeWindows();
122        }
123    }
124
125    boolean allowTaskResize() {
126        return mStackId == FREEFORM_WORKSPACE_STACK_ID
127                || mStackId == DOCKED_STACK_ID
128                || mStackId == PINNED_STACK_ID;
129    }
130
131    /**
132     * Set the bounds of the stack and its containing tasks.
133     * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
134     * @param configs Configuration for individual tasks, keyed by task id.
135     * @param taskBounds Bounds for individual tasks, keyed by task id.
136     * @return True if the stack bounds was changed.
137     * */
138    boolean setBounds(
139            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
140        if (!setBounds(stackBounds)) {
141            return false;
142        }
143
144        // Update bounds of containing tasks.
145        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
146            final Task task = mTasks.get(taskNdx);
147            Configuration config = configs.get(task.mTaskId);
148            if (config != null) {
149                Rect bounds = taskBounds.get(task.mTaskId);
150                task.setBounds(bounds, config);
151            } else {
152                Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?");
153            }
154        }
155        return true;
156    }
157
158    private boolean setBounds(Rect bounds) {
159        boolean oldFullscreen = mFullscreen;
160        int rotation = Surface.ROTATION_0;
161        if (mDisplayContent != null) {
162            mDisplayContent.getLogicalDisplayRect(mTmpRect);
163            rotation = mDisplayContent.getDisplayInfo().rotation;
164            if (bounds == null) {
165                bounds = mTmpRect;
166                mFullscreen = true;
167            } else {
168                // ensure bounds are entirely within the display rect
169                if (!bounds.intersect(mTmpRect)) {
170                    // Can't set bounds outside the containing display.. Sorry!
171                    return false;
172                }
173                mFullscreen = mTmpRect.equals(bounds);
174            }
175        }
176
177        if (bounds == null) {
178            // Can't set to fullscreen if we don't have a display to get bounds from...
179            return false;
180        }
181        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
182            return false;
183        }
184
185        if (mDisplayContent != null) {
186            mDisplayContent.mDimBehindController.updateDimLayer(this);
187            mAnimationBackgroundSurface.setBounds(bounds);
188        }
189
190        mBounds.set(bounds);
191        mRotation = rotation;
192        return true;
193    }
194
195    /** Bounds of the stack without adjusting for other factors in the system like visibility
196     * of docked stack.
197     * Most callers should be using {@link #getBounds} as it take into consideration other system
198     * factors. */
199    void getRawBounds(Rect out) {
200        out.set(mBounds);
201    }
202
203    /** Return true if the current bound can get outputted to the rest of the system as-is. */
204    private boolean useCurrentBounds() {
205        if (mFullscreen
206                || mStackId == DOCKED_STACK_ID
207                || mStackId == PINNED_STACK_ID
208                || mDisplayContent == null
209                || mDisplayContent.getDockedStackLocked() != null) {
210            return true;
211        }
212        return false;
213    }
214
215    /** Bounds of the stack with other system factors taken into consideration. */
216    @Override
217    public void getBounds(Rect out) {
218        if (useCurrentBounds()) {
219            // No need to adjust the output bounds if fullscreen or the docked stack is visible
220            // since it is already what we want to represent to the rest of the system.
221            out.set(mBounds);
222            return;
223        }
224
225        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
226        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
227        // system.
228        mDisplayContent.getLogicalDisplayRect(out);
229    }
230
231    void updateDisplayInfo(Rect bounds) {
232        if (mDisplayContent != null) {
233            for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
234                mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
235            }
236            if (bounds != null) {
237                setBounds(bounds);
238            } else if (mFullscreen) {
239                setBounds(null);
240            } else {
241                mTmpRect2.set(mBounds);
242                mDisplayContent.rotateBounds(
243                        mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
244                if (setBounds(mTmpRect2)) {
245                    // Post message to inform activity manager of the bounds change simulating
246                    // a one-way call. We do this to prevent a deadlock between window manager
247                    // lock and activity manager lock been held.
248                    mService.mH.sendMessage(mService.mH.obtainMessage(
249                            RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mBounds));
250                }
251            }
252        }
253    }
254
255    boolean isAnimating() {
256        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
257            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
258            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
259                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
260                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
261                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
262                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
263                        return true;
264                    }
265                }
266            }
267        }
268        return false;
269    }
270
271    void addTask(Task task, boolean toTop) {
272        addTask(task, toTop, task.showForAllUsers());
273    }
274
275    /**
276     * Put a Task in this stack. Used for adding and moving.
277     * @param task The task to add.
278     * @param toTop Whether to add it to the top or bottom.
279     * @param showForAllUsers Whether to show the task regardless of the current user.
280     */
281    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
282        positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
283    }
284
285    void positionTask(Task task, int position, boolean showForAllUsers) {
286        final boolean canShowTask =
287                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
288        mTasks.remove(task);
289        int stackSize = mTasks.size();
290        int minPosition = 0;
291        int maxPosition = stackSize;
292
293        if (canShowTask) {
294            minPosition = computeMinPosition(minPosition, stackSize);
295        } else {
296            maxPosition = computeMaxPosition(maxPosition);
297        }
298        // Reset position based on minimum/maximum possible positions.
299        position = Math.min(Math.max(position, minPosition), maxPosition);
300
301        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG,
302                "positionTask: task=" + task + " position=" + position);
303        mTasks.add(position, task);
304
305        task.mStack = this;
306        task.updateDisplayInfo(mDisplayContent);
307        boolean toTop = position == mTasks.size() - 1;
308        if (toTop) {
309            mDisplayContent.moveStack(this, true);
310        }
311        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
312    }
313
314    /** Calculate the minimum possible position for a task that can be shown to the user.
315     *  The minimum position will be above all other tasks that can't be shown.
316     *  @param minPosition The minimum position the caller is suggesting.
317     *                  We will start adjusting up from here.
318     *  @param size The size of the current task list.
319     */
320    private int computeMinPosition(int minPosition, int size) {
321        while (minPosition < size) {
322            final Task tmpTask = mTasks.get(minPosition);
323            final boolean canShowTmpTask =
324                    tmpTask.showForAllUsers()
325                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
326            if (canShowTmpTask) {
327                break;
328            }
329            minPosition++;
330        }
331        return minPosition;
332    }
333
334    /** Calculate the maximum possible position for a task that can't be shown to the user.
335     *  The maximum position will be below all other tasks that can be shown.
336     *  @param maxPosition The maximum position the caller is suggesting.
337     *                  We will start adjusting down from here.
338     */
339    private int computeMaxPosition(int maxPosition) {
340        while (maxPosition > 0) {
341            final Task tmpTask = mTasks.get(maxPosition - 1);
342            final boolean canShowTmpTask =
343                    tmpTask.showForAllUsers()
344                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
345            if (!canShowTmpTask) {
346                break;
347            }
348            maxPosition--;
349        }
350        return maxPosition;
351    }
352
353    void moveTaskToTop(Task task) {
354        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
355                + Debug.getCallers(6));
356        mTasks.remove(task);
357        addTask(task, true);
358    }
359
360    void moveTaskToBottom(Task task) {
361        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
362        mTasks.remove(task);
363        addTask(task, false);
364    }
365
366    /**
367     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
368     * back.
369     * @param task The Task to delete.
370     */
371    void removeTask(Task task) {
372        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
373        mTasks.remove(task);
374        if (mDisplayContent != null) {
375            if (mTasks.isEmpty()) {
376                mDisplayContent.moveStack(this, false);
377            }
378            mDisplayContent.layoutNeeded = true;
379        }
380        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
381            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
382            if (wtoken.mTask == task) {
383                wtoken.mIsExiting = false;
384                mExitingAppTokens.remove(appNdx);
385            }
386        }
387    }
388
389    void attachDisplayContent(DisplayContent displayContent) {
390        if (mDisplayContent != null) {
391            throw new IllegalStateException("attachDisplayContent: Already attached");
392        }
393
394        mDisplayContent = displayContent;
395        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
396
397        Rect bounds = null;
398        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
399        if (mStackId == DOCKED_STACK_ID || (dockedStack != null && mStackId != PINNED_STACK_ID
400                && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
401            // The existence of a docked stack affects the size of any static stack created since
402            // the docked stack occupies a dedicated region on screen.
403            bounds = new Rect();
404            displayContent.getLogicalDisplayRect(mTmpRect);
405            mTmpRect2.setEmpty();
406            if (dockedStack != null) {
407                dockedStack.getRawBounds(mTmpRect2);
408            }
409            final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
410                    == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
411            getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
412                    mDisplayContent.mDividerControllerLocked.getWidthAdjustment(),
413                    dockedOnTopOrLeft);
414        }
415
416        updateDisplayInfo(bounds);
417
418        if (mStackId == DOCKED_STACK_ID) {
419            // Attaching a docked stack to the display affects the size of all other static
420            // stacks since the docked stack occupies a dedicated region on screen.
421            // Resize existing static stacks so they are pushed to the side of the docked stack.
422            resizeNonDockedStacks(!FULLSCREEN, mBounds);
423        }
424    }
425
426    void getStackDockedModeBoundsLocked(Rect outBounds) {
427        if (mStackId == DOCKED_STACK_ID
428                || mStackId == PINNED_STACK_ID
429                || mStackId > LAST_STATIC_STACK_ID
430                || mDisplayContent == null) {
431            outBounds.set(mBounds);
432            return;
433        }
434
435        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
436        if (dockedStack == null) {
437            // Not sure why you are calling this method when there is no docked stack...
438            throw new IllegalStateException(
439                    "Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
440        }
441        if (!dockedStack.isVisibleLocked()) {
442            // The docked stack is being dismissed, but we caught before it finished being
443            // dismissed. In that case we want to treat it as if it is not occupying any space and
444            // let others occupy the whole display.
445            mDisplayContent.getLogicalDisplayRect(mTmpRect);
446            return;
447        }
448
449        @DockSide
450        final int dockedSide = dockedStack.getDockSide();
451        if (dockedSide == DOCKED_INVALID) {
452            // Not sure how you got here...Only thing we can do is return current bounds.
453            Slog.e(TAG, "Failed to get valid docked side for docked stack=" + dockedStack);
454            outBounds.set(mBounds);
455            return;
456        }
457
458        mDisplayContent.getLogicalDisplayRect(mTmpRect);
459        dockedStack.getRawBounds(mTmpRect2);
460        final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
461        getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
462                mDisplayContent.mDividerControllerLocked.getWidthAdjustment(), dockedOnTopOrLeft);
463
464    }
465
466    /**
467     * Outputs the bounds a stack should be given the presence of a docked stack on the display.
468     * @param displayRect The bounds of the display the docked stack is on.
469     * @param outBounds Output bounds that should be used for the stack.
470     * @param stackId Id of stack we are calculating the bounds for.
471     * @param dockedBounds Bounds of the docked stack.
472     * @param adjustment Additional adjustment to make to the output bounds close to the side of the
473     *                   dock.
474     * @param dockOntopOrLeft If the docked stack is on the top or left side of the screen.
475     */
476    private static void getStackDockedModeBounds(
477            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int adjustment,
478            boolean dockOntopOrLeft) {
479        final boolean dockedStack = stackId == DOCKED_STACK_ID;
480        final boolean splitHorizontally = displayRect.width() > displayRect.height();
481
482        outBounds.set(displayRect);
483        if (dockedStack) {
484            // The initial bounds of the docked stack when it is created half the screen space and
485            // its bounds can be adjusted after that. The bounds of all other stacks are adjusted
486            // to occupy whatever screen space the docked stack isn't occupying.
487            if (dockOntopOrLeft) {
488                if (splitHorizontally) {
489                    outBounds.right = displayRect.centerX() - adjustment;
490                } else {
491                    outBounds.bottom = displayRect.centerY() - adjustment;
492                }
493            } else {
494                if (splitHorizontally) {
495                    outBounds.left = displayRect.centerX() + adjustment;
496                } else {
497                    outBounds.top = displayRect.centerY() + adjustment;
498                }
499            }
500            return;
501        }
502
503        // Other stacks occupy whatever space is left by the docked stack.
504        if (!dockOntopOrLeft) {
505            if (splitHorizontally) {
506                outBounds.right = dockedBounds.left - adjustment;
507            } else {
508                outBounds.bottom = dockedBounds.top - adjustment;
509            }
510        } else {
511            if (splitHorizontally) {
512                outBounds.left = dockedBounds.right + adjustment;
513            } else {
514                outBounds.top = dockedBounds.bottom + adjustment;
515            }
516        }
517    }
518
519    /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size
520     * based on the presence of a docked stack.
521     * @param fullscreen If true the stacks will be resized to fullscreen, else they will be
522     *                   resized to the appropriate size based on the presence of a docked stack.
523     * @param dockedBounds Bounds of the docked stack.
524     */
525    private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) {
526        // Not using mTmpRect because we are posting the object in a message.
527        final Rect bounds = new Rect();
528        mDisplayContent.getLogicalDisplayRect(bounds);
529        if (!fullscreen) {
530            final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
531                    == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
532            getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
533                    mDisplayContent.mDividerControllerLocked.getWidth(), dockedOnTopOrLeft);
534        }
535
536        final int count = mService.mStackIdToStack.size();
537        for (int i = 0; i < count; i++) {
538            final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
539            final int otherStackId = otherStack.mStackId;
540            if (otherStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID
541                    && otherStackId >= FIRST_STATIC_STACK_ID
542                    && otherStackId <= LAST_STATIC_STACK_ID) {
543                mService.mH.sendMessage(
544                        mService.mH.obtainMessage(RESIZE_STACK, otherStackId,
545                                1 /*allowResizeInDockedMode*/, bounds));
546            }
547        }
548    }
549
550    void detachDisplay() {
551        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
552
553        boolean doAnotherLayoutPass = false;
554        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
555            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
556            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
557                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
558                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
559                    // We are in the middle of changing the state of displays/stacks/tasks. We need
560                    // to finish that, before we let layout interfere with it.
561                    mService.removeWindowLocked(appWindows.get(winNdx));
562                    doAnotherLayoutPass = true;
563                }
564            }
565        }
566        if (doAnotherLayoutPass) {
567            mService.mWindowPlacerLocked.requestTraversal();
568        }
569
570        if (mStackId == DOCKED_STACK_ID) {
571            // Docked stack was detached from the display, so we no longer need to restrict the
572            // region of the screen other static stacks occupy. Go ahead and make them fullscreen.
573            resizeNonDockedStacks(FULLSCREEN, null);
574        }
575
576        close();
577    }
578
579    void resetAnimationBackgroundAnimator() {
580        mAnimationBackgroundAnimator = null;
581        mAnimationBackgroundSurface.hide();
582    }
583
584    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
585        int animLayer = winAnimator.mAnimLayer;
586        if (mAnimationBackgroundAnimator == null
587                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
588            mAnimationBackgroundAnimator = winAnimator;
589            animLayer = mService.adjustAnimationBackground(winAnimator);
590            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
591                    ((color >> 24) & 0xff) / 255f, 0);
592        }
593    }
594
595    void switchUser() {
596        int top = mTasks.size();
597        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
598            Task task = mTasks.get(taskNdx);
599            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
600                mTasks.remove(taskNdx);
601                mTasks.add(task);
602                --top;
603            }
604        }
605    }
606
607    void close() {
608        if (mAnimationBackgroundSurface != null) {
609            mAnimationBackgroundSurface.destroySurface();
610            mAnimationBackgroundSurface = null;
611        }
612        mDisplayContent = null;
613    }
614
615    public void dump(String prefix, PrintWriter pw) {
616        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
617        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
618        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
619            pw.print(prefix);
620            mTasks.get(taskNdx).printTo(prefix + " ", pw);
621        }
622        if (mAnimationBackgroundSurface.isDimming()) {
623            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
624            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
625        }
626        if (!mExitingAppTokens.isEmpty()) {
627            pw.println();
628            pw.println("  Exiting application tokens:");
629            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
630                WindowToken token = mExitingAppTokens.get(i);
631                pw.print("  Exiting App #"); pw.print(i);
632                pw.print(' '); pw.print(token);
633                pw.println(':');
634                token.dump(pw, "    ");
635            }
636        }
637    }
638
639    /** Fullscreen status of the stack without adjusting for other factors in the system like
640     * visibility of docked stack.
641     * Most callers should be using {@link #isFullscreen} as it take into consideration other
642     * system factors. */
643    boolean getRawFullscreen() {
644        return mFullscreen;
645    }
646
647    @Override
648    public boolean isFullscreen() {
649        if (useCurrentBounds()) {
650            return mFullscreen;
651        }
652        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
653        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
654        // system.
655        return true;
656    }
657
658    @Override
659    public DisplayInfo getDisplayInfo() {
660        return mDisplayContent.getDisplayInfo();
661    }
662
663    @Override
664    public String toString() {
665        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
666    }
667
668    @Override
669    public String toShortString() {
670        return "Stack=" + mStackId;
671    }
672
673    /**
674     * For docked workspace provides information which side of the screen was the dock anchored.
675     */
676    @DockSide
677    int getDockSide() {
678        if (mStackId != DOCKED_STACK_ID) {
679            return DOCKED_INVALID;
680        }
681        if (mDisplayContent == null) {
682            return DOCKED_INVALID;
683        }
684        mDisplayContent.getLogicalDisplayRect(mTmpRect);
685        final int orientation = mService.mCurConfiguration.orientation;
686        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
687            // Portrait mode, docked either at the top or the bottom.
688            if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
689                return DOCKED_TOP;
690            } else {
691                return DOCKED_BOTTOM;
692            }
693        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
694            // Landscape mode, docked either on the left or on the right.
695            if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
696                return DOCKED_LEFT;
697            } else {
698                return DOCKED_RIGHT;
699            }
700        } else {
701            return DOCKED_INVALID;
702        }
703    }
704
705    boolean isVisibleLocked() {
706        for (int i = mTasks.size() - 1; i >= 0; i--) {
707            Task task = mTasks.get(i);
708            for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
709                if (!task.mAppTokens.get(j).hidden) {
710                    return true;
711                }
712            }
713        }
714        return false;
715    }
716}
717