TaskStack.java revision b816b862b0c73033f1ebd8070e77fcfe33605171
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 android.app.ActivityManager.StackId;
20import android.content.res.Configuration;
21import android.graphics.Rect;
22import android.os.Debug;
23import android.util.EventLog;
24import android.util.Slog;
25import android.util.SparseArray;
26import android.view.DisplayInfo;
27import android.view.Surface;
28
29import com.android.internal.policy.DividerSnapAlgorithm;
30import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
31import com.android.internal.policy.DockedDividerUtils;
32import com.android.server.EventLogTags;
33
34import java.io.PrintWriter;
35import java.util.ArrayList;
36
37import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
38import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
39import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
40import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
41import static android.view.WindowManager.DOCKED_BOTTOM;
42import static android.view.WindowManager.DOCKED_INVALID;
43import static android.view.WindowManager.DOCKED_LEFT;
44import static android.view.WindowManager.DOCKED_RIGHT;
45import static android.view.WindowManager.DOCKED_TOP;
46import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
47import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
48import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
49
50public class TaskStack implements DimLayer.DimLayerUser {
51
52    // If the stack should be resized to fullscreen.
53    private static final boolean FULLSCREEN = true;
54
55    /** Unique identifier */
56    final int mStackId;
57
58    /** The service */
59    private final WindowManagerService mService;
60
61    /** The display this stack sits under. */
62    private DisplayContent mDisplayContent;
63
64    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
65     * mTaskHistory in the ActivityStack with the same mStackId */
66    private final ArrayList<Task> mTasks = new ArrayList<>();
67
68    /** For comparison with DisplayContent bounds. */
69    private Rect mTmpRect = new Rect();
70    private Rect mTmpRect2 = new Rect();
71
72    /** Content limits relative to the DisplayContent this sits in. */
73    private Rect mBounds = new Rect();
74
75    /** Screen content area excluding IM windows, etc. */
76    private final Rect mContentBounds = new Rect();
77
78    /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
79    private final Rect mAdjustedBounds = new Rect();
80
81    /** Whether mBounds is fullscreen */
82    private boolean mFullscreen = true;
83
84    // Device rotation as of the last time {@link #mBounds} was set.
85    int mRotation;
86
87    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
88    DimLayer mAnimationBackgroundSurface;
89
90    /** The particular window with an Animation with non-zero background color. */
91    WindowStateAnimator mAnimationBackgroundAnimator;
92
93    /** Application tokens that are exiting, but still on screen for animations. */
94    final AppTokenList mExitingAppTokens = new AppTokenList();
95
96    /** Detach this stack from its display when animation completes. */
97    boolean mDeferDetach;
98    private boolean mUpdateBoundsAfterRotation = false;
99
100    TaskStack(WindowManagerService service, int stackId) {
101        mService = service;
102        mStackId = stackId;
103        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
104    }
105
106    DisplayContent getDisplayContent() {
107        return mDisplayContent;
108    }
109
110    ArrayList<Task> getTasks() {
111        return mTasks;
112    }
113
114    /**
115     * Set the bounds of the stack and its containing tasks.
116     * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
117     * @param configs Configuration for individual tasks, keyed by task id.
118     * @param taskBounds Bounds for individual tasks, keyed by task id.
119     * @return True if the stack bounds was changed.
120     * */
121    boolean setBounds(
122            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
123            SparseArray<Rect> taskTempInsetBounds) {
124        if (!setBounds(stackBounds)) {
125            return false;
126        }
127
128        // Update bounds of containing tasks.
129        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
130            final Task task = mTasks.get(taskNdx);
131            Configuration config = configs.get(task.mTaskId);
132            if (config != null) {
133                Rect bounds = taskBounds.get(task.mTaskId);
134                if (task.isTwoFingerScrollMode()) {
135                    // This is a non-resizeable task that's docked (or side-by-side to the docked
136                    // stack). It might have been scrolled previously, and after the stack resizing,
137                    // it might no longer fully cover the stack area.
138                    // Save the old bounds and re-apply the scroll. This adjusts the bounds to
139                    // fit the new stack bounds.
140                    task.resizeLocked(bounds, config, false /* forced */);
141                    task.getBounds(mTmpRect);
142                    task.scrollLocked(mTmpRect);
143                } else {
144                    task.resizeLocked(bounds, config, false /* forced */);
145                    task.setTempInsetBounds(
146                            taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId)
147                                    : null);
148                }
149            } else {
150                Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?");
151            }
152        }
153        return true;
154    }
155
156    void prepareFreezingTaskBounds() {
157        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
158            final Task task = mTasks.get(taskNdx);
159            task.prepareFreezingBounds();
160        }
161    }
162
163    boolean isFullscreenBounds(Rect bounds) {
164        if (mDisplayContent == null || bounds == null) {
165            return true;
166        }
167        mDisplayContent.getLogicalDisplayRect(mTmpRect);
168        return mTmpRect.equals(bounds);
169    }
170
171    void alignTasksToAdjustedBounds(final Rect adjustedBounds) {
172        if (mFullscreen) {
173            return;
174        }
175        // Update bounds of containing tasks.
176        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
177            final Task task = mTasks.get(taskNdx);
178            if (task.isTwoFingerScrollMode()) {
179                // If we're scrolling we don't care about your bounds or configs,
180                // they should be null as if we were in fullscreen.
181                task.resizeLocked(null, null, false /* forced */);
182                task.getBounds(mTmpRect2);
183                task.scrollLocked(mTmpRect2);
184            } else if (task.isResizeable()) {
185                task.getBounds(mTmpRect2);
186                mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
187                task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */);
188            }
189        }
190    }
191
192    void adjustForIME(final WindowState imeWin) {
193        final int dockedSide = getDockSide();
194        final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
195        final Rect adjustedBounds = mAdjustedBounds;
196        if (imeWin == null || !dockedTopOrBottom) {
197            // If mContentBounds is already empty, it means we're not applying
198            // any adjustments, so nothing to do; otherwise clear any adjustments.
199            if (!mContentBounds.isEmpty()) {
200                mContentBounds.setEmpty();
201                adjustedBounds.set(mBounds);
202                alignTasksToAdjustedBounds(adjustedBounds);
203            }
204            return;
205        }
206
207        final Rect displayContentRect = mTmpRect;
208        final Rect contentBounds = mTmpRect2;
209
210        // Calculate the content bounds excluding the area occupied by IME
211        mDisplayContent.getContentRect(displayContentRect);
212        contentBounds.set(displayContentRect);
213        int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
214        imeTop += imeWin.getGivenContentInsetsLw().top;
215        if (contentBounds.bottom > imeTop) {
216            contentBounds.bottom = imeTop;
217        }
218
219        // If content bounds not changing, nothing to do.
220        if (mContentBounds.equals(contentBounds)) {
221            return;
222        }
223
224        // Content bounds changed, need to apply adjustments depending on dock sides.
225        mContentBounds.set(contentBounds);
226        adjustedBounds.set(mBounds);
227        final int yOffset = displayContentRect.bottom - contentBounds.bottom;
228
229        if (dockedSide == DOCKED_TOP) {
230            // If this stack is docked on top, we make it smaller so the bottom stack is not
231            // occluded by IME. We shift its bottom up by the height of the IME (capped by
232            // the display content rect). Note that we don't change the task bounds.
233            adjustedBounds.bottom = Math.max(
234                    adjustedBounds.bottom - yOffset, displayContentRect.top);
235        } else {
236            // If this stack is docked on bottom, we shift it up so that it's not occluded by
237            // IME. We try to move it up by the height of the IME window (although the best
238            // we could do is to make the top stack fully collapsed).
239            final int dividerWidth = mDisplayContent.mDividerControllerLocked.getContentWidth();
240            adjustedBounds.top = Math.max(
241                    adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
242            adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
243
244            // We also move the member tasks together, taking care not to resize them.
245            // Resizing might cause relaunch, and IME window may not come back after that.
246            alignTasksToAdjustedBounds(adjustedBounds);
247        }
248    }
249
250    private boolean setBounds(Rect bounds) {
251        boolean oldFullscreen = mFullscreen;
252        int rotation = Surface.ROTATION_0;
253        if (mDisplayContent != null) {
254            mDisplayContent.getLogicalDisplayRect(mTmpRect);
255            rotation = mDisplayContent.getDisplayInfo().rotation;
256            if (bounds == null) {
257                bounds = mTmpRect;
258                mFullscreen = true;
259            } else {
260                mFullscreen = mTmpRect.equals(bounds);
261            }
262        }
263
264        if (bounds == null) {
265            // Can't set to fullscreen if we don't have a display to get bounds from...
266            return false;
267        }
268        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
269            return false;
270        }
271
272        if (mDisplayContent != null) {
273            mDisplayContent.mDimLayerController.updateDimLayer(this);
274            mAnimationBackgroundSurface.setBounds(bounds);
275        }
276
277        mBounds.set(bounds);
278        mRotation = rotation;
279
280        // Clear the adjusted content bounds as they're no longer valid.
281        // If IME is still visible, these will be re-applied.
282        // Note that we don't clear mContentBounds here, so that we know the last IME
283        // adjust we applied.
284        // If user starts dragging the dock divider while IME is visible, the new bounds
285        // we received are based on the actual screen location of the divider. It already
286        // accounted for the IME window, so we don't want to adjust again.
287        mAdjustedBounds.set(mBounds);
288
289        return true;
290    }
291
292    /** Bounds of the stack without adjusting for other factors in the system like visibility
293     * of docked stack.
294     * Most callers should be using {@link #getBounds} as it take into consideration other system
295     * factors. */
296    void getRawBounds(Rect out) {
297        out.set(mBounds);
298    }
299
300    /** Return true if the current bound can get outputted to the rest of the system as-is. */
301    private boolean useCurrentBounds() {
302        if (mFullscreen
303                || !StackId.isResizeableByDockedStack(mStackId)
304                || mDisplayContent == null
305                || mDisplayContent.getDockedStackLocked() != null) {
306            return true;
307        }
308        return false;
309    }
310
311    public void getBounds(Rect out) {
312        if (useCurrentBounds()) {
313            // If we're currently adjusting for IME, we use the adjusted bounds; otherwise,
314            // no need to adjust the output bounds if fullscreen or the docked stack is visible
315            // since it is already what we want to represent to the rest of the system.
316            if (!mContentBounds.isEmpty()) {
317                out.set(mAdjustedBounds);
318            } else {
319                out.set(mBounds);
320            }
321            return;
322        }
323
324        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
325        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
326        // system.
327        mDisplayContent.getLogicalDisplayRect(out);
328    }
329
330    /** Bounds of the stack with other system factors taken into consideration. */
331    @Override
332    public void getDimBounds(Rect out) {
333        getBounds(out);
334    }
335
336    void updateDisplayInfo(Rect bounds) {
337        mUpdateBoundsAfterRotation = false;
338        if (mDisplayContent != null) {
339            for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
340                mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
341            }
342            if (bounds != null) {
343                setBounds(bounds);
344            } else if (mFullscreen) {
345                setBounds(null);
346            } else {
347                mUpdateBoundsAfterRotation = true;
348                mTmpRect2.set(mBounds);
349                final int newRotation = mDisplayContent.getDisplayInfo().rotation;
350                if (mRotation == newRotation) {
351                    setBounds(mTmpRect2);
352                }
353
354                // If the rotation changes, we'll handle it in updateBoundsAfterRotation
355            }
356        }
357    }
358
359    /**
360     * Updates the bounds after rotating the screen. We can't handle it in
361     * {@link #updateDisplayInfo} because at that point the configuration might not be fully updated
362     * yet.
363     */
364    void updateBoundsAfterRotation() {
365        if (!mUpdateBoundsAfterRotation) {
366            return;
367        }
368        mUpdateBoundsAfterRotation = false;
369        final int newRotation = getDisplayInfo().rotation;
370        mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
371        if (mStackId == DOCKED_STACK_ID) {
372            snapDockedStackAfterRotation(mTmpRect2);
373        }
374
375        // Post message to inform activity manager of the bounds change simulating
376        // a one-way call. We do this to prevent a deadlock between window manager
377        // lock and activity manager lock been held.
378        mService.mH.sendMessage(mService.mH.obtainMessage(
379                RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2));
380    }
381
382    /**
383     * Snaps the bounds after rotation to the closest snap target for the docked stack.
384     */
385    private void snapDockedStackAfterRotation(Rect outBounds) {
386
387        // Calculate the current position.
388        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
389        final int dividerSize = mService.getDefaultDisplayContentLocked()
390                .getDockedDividerController().getContentWidth();
391        final int dockSide = getDockSide(outBounds);
392        final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
393                dockSide, dividerSize);
394        final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
395        final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
396
397        // Snap the position to a target.
398        final int rotation = displayInfo.rotation;
399        final int orientation = mService.mCurConfiguration.orientation;
400        mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
401        final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
402                mService.mContext.getResources(), displayWidth, displayHeight,
403                dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
404        final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
405
406        // Recalculate the bounds based on the position of the target.
407        DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
408                outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
409                dividerSize);
410    }
411
412    boolean isAnimating() {
413        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
414            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
415            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
416                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
417                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
418                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
419                    if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
420                        return true;
421                    }
422                }
423            }
424        }
425        return false;
426    }
427
428    void addTask(Task task, boolean toTop) {
429        addTask(task, toTop, task.showForAllUsers());
430    }
431
432    /**
433     * Put a Task in this stack. Used for adding and moving.
434     * @param task The task to add.
435     * @param toTop Whether to add it to the top or bottom.
436     * @param showForAllUsers Whether to show the task regardless of the current user.
437     */
438    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
439        positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
440    }
441
442    void positionTask(Task task, int position, boolean showForAllUsers) {
443        final boolean canShowTask =
444                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
445        mTasks.remove(task);
446        int stackSize = mTasks.size();
447        int minPosition = 0;
448        int maxPosition = stackSize;
449
450        if (canShowTask) {
451            minPosition = computeMinPosition(minPosition, stackSize);
452        } else {
453            maxPosition = computeMaxPosition(maxPosition);
454        }
455        // Reset position based on minimum/maximum possible positions.
456        position = Math.min(Math.max(position, minPosition), maxPosition);
457
458        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM,
459                "positionTask: task=" + task + " position=" + position);
460        mTasks.add(position, task);
461
462        // If we are moving the task across stacks, the scroll is no longer valid.
463        if (task.mStack != this) {
464            task.resetScrollLocked();
465        }
466        task.mStack = this;
467        task.updateDisplayInfo(mDisplayContent);
468        boolean toTop = position == mTasks.size() - 1;
469        if (toTop) {
470            mDisplayContent.moveStack(this, true);
471        }
472        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
473    }
474
475    /** Calculate the minimum possible position for a task that can be shown to the user.
476     *  The minimum position will be above all other tasks that can't be shown.
477     *  @param minPosition The minimum position the caller is suggesting.
478     *                  We will start adjusting up from here.
479     *  @param size The size of the current task list.
480     */
481    private int computeMinPosition(int minPosition, int size) {
482        while (minPosition < size) {
483            final Task tmpTask = mTasks.get(minPosition);
484            final boolean canShowTmpTask =
485                    tmpTask.showForAllUsers()
486                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
487            if (canShowTmpTask) {
488                break;
489            }
490            minPosition++;
491        }
492        return minPosition;
493    }
494
495    /** Calculate the maximum possible position for a task that can't be shown to the user.
496     *  The maximum position will be below all other tasks that can be shown.
497     *  @param maxPosition The maximum position the caller is suggesting.
498     *                  We will start adjusting down from here.
499     */
500    private int computeMaxPosition(int maxPosition) {
501        while (maxPosition > 0) {
502            final Task tmpTask = mTasks.get(maxPosition - 1);
503            final boolean canShowTmpTask =
504                    tmpTask.showForAllUsers()
505                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
506            if (!canShowTmpTask) {
507                break;
508            }
509            maxPosition--;
510        }
511        return maxPosition;
512    }
513
514    void moveTaskToTop(Task task) {
515        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers="
516                + Debug.getCallers(6));
517        mTasks.remove(task);
518        addTask(task, true);
519    }
520
521    void moveTaskToBottom(Task task) {
522        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + task);
523        mTasks.remove(task);
524        addTask(task, false);
525    }
526
527    /**
528     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
529     * back.
530     * @param task The Task to delete.
531     */
532    void removeTask(Task task) {
533        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeTask: task=" + task);
534        mTasks.remove(task);
535        if (mDisplayContent != null) {
536            if (mTasks.isEmpty()) {
537                mDisplayContent.moveStack(this, false);
538            }
539            mDisplayContent.layoutNeeded = true;
540        }
541        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
542            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
543            if (wtoken.mTask == task) {
544                wtoken.mIsExiting = false;
545                mExitingAppTokens.remove(appNdx);
546            }
547        }
548    }
549
550    void attachDisplayContent(DisplayContent displayContent) {
551        if (mDisplayContent != null) {
552            throw new IllegalStateException("attachDisplayContent: Already attached");
553        }
554
555        mDisplayContent = displayContent;
556        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
557
558        Rect bounds = null;
559        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
560        if (mStackId == DOCKED_STACK_ID
561                || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId))) {
562            // The existence of a docked stack affects the size of other static stack created since
563            // the docked stack occupies a dedicated region on screen.
564            bounds = new Rect();
565            displayContent.getLogicalDisplayRect(mTmpRect);
566            mTmpRect2.setEmpty();
567            if (dockedStack != null) {
568                dockedStack.getRawBounds(mTmpRect2);
569            }
570            final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
571                    == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
572            getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
573                    mDisplayContent.mDividerControllerLocked.getContentWidth(),
574                    dockedOnTopOrLeft);
575        }
576
577        updateDisplayInfo(bounds);
578
579        if (mStackId == DOCKED_STACK_ID) {
580            // Attaching a docked stack to the display affects the size of all other static
581            // stacks since the docked stack occupies a dedicated region on screen.
582            // Resize existing static stacks so they are pushed to the side of the docked stack.
583            resizeNonDockedStacks(!FULLSCREEN, mBounds);
584        }
585    }
586
587    void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibilityOnKeyguardShowing) {
588        if (!StackId.isResizeableByDockedStack(mStackId) || mDisplayContent == null) {
589            outBounds.set(mBounds);
590            return;
591        }
592
593        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
594        if (dockedStack == null) {
595            // Not sure why you are calling this method when there is no docked stack...
596            throw new IllegalStateException(
597                    "Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
598        }
599        if (!dockedStack.isVisibleLocked(ignoreVisibilityOnKeyguardShowing)) {
600            // The docked stack is being dismissed, but we caught before it finished being
601            // dismissed. In that case we want to treat it as if it is not occupying any space and
602            // let others occupy the whole display.
603            mDisplayContent.getLogicalDisplayRect(outBounds);
604            return;
605        }
606
607        final int dockedSide = dockedStack.getDockSide();
608        if (dockedSide == DOCKED_INVALID) {
609            // Not sure how you got here...Only thing we can do is return current bounds.
610            Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack);
611            outBounds.set(mBounds);
612            return;
613        }
614
615        mDisplayContent.getLogicalDisplayRect(mTmpRect);
616        dockedStack.getRawBounds(mTmpRect2);
617        final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP
618                || dockedSide == DOCKED_LEFT;
619        getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
620                mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
621
622    }
623
624    /**
625     * Outputs the bounds a stack should be given the presence of a docked stack on the display.
626     * @param displayRect The bounds of the display the docked stack is on.
627     * @param outBounds Output bounds that should be used for the stack.
628     * @param stackId Id of stack we are calculating the bounds for.
629     * @param dockedBounds Bounds of the docked stack.
630     * @param dockDividerWidth We need to know the width of the divider make to the output bounds
631     *                         close to the side of the dock.
632     * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
633     */
634    private void getStackDockedModeBounds(
635            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
636            boolean dockOnTopOrLeft) {
637        final boolean dockedStack = stackId == DOCKED_STACK_ID;
638        final boolean splitHorizontally = displayRect.width() > displayRect.height();
639
640        outBounds.set(displayRect);
641        if (dockedStack) {
642            if (mService.mDockedStackCreateBounds != null) {
643                outBounds.set(mService.mDockedStackCreateBounds);
644                return;
645            }
646
647            // The initial bounds of the docked stack when it is created about half the screen space
648            // and its bounds can be adjusted after that. The bounds of all other stacks are
649            // adjusted to occupy whatever screen space the docked stack isn't occupying.
650            final DisplayInfo di = mDisplayContent.getDisplayInfo();
651            mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
652                    mTmpRect2);
653            final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
654                    di.logicalWidth,
655                    di.logicalHeight,
656                    dockDividerWidth,
657                    mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT,
658                    mTmpRect2).getMiddleTarget().position;
659
660            if (dockOnTopOrLeft) {
661                if (splitHorizontally) {
662                    outBounds.right = position;
663                } else {
664                    outBounds.bottom = position;
665                }
666            } else {
667                if (splitHorizontally) {
668                    outBounds.left = position - dockDividerWidth;
669                } else {
670                    outBounds.top = position - dockDividerWidth;
671                }
672            }
673            return;
674        }
675
676        // Other stacks occupy whatever space is left by the docked stack.
677        if (!dockOnTopOrLeft) {
678            if (splitHorizontally) {
679                outBounds.right = dockedBounds.left - dockDividerWidth;
680            } else {
681                outBounds.bottom = dockedBounds.top - dockDividerWidth;
682            }
683        } else {
684            if (splitHorizontally) {
685                outBounds.left = dockedBounds.right + dockDividerWidth;
686            } else {
687                outBounds.top = dockedBounds.bottom + dockDividerWidth;
688            }
689        }
690    }
691
692    /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size
693     * based on the presence of a docked stack.
694     * @param fullscreen If true the stacks will be resized to fullscreen, else they will be
695     *                   resized to the appropriate size based on the presence of a docked stack.
696     * @param dockedBounds Bounds of the docked stack.
697     */
698    private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) {
699        // Not using mTmpRect because we are posting the object in a message.
700        final Rect bounds = new Rect();
701        mDisplayContent.getLogicalDisplayRect(bounds);
702        if (!fullscreen) {
703            final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
704                    == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
705            getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
706                    mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
707        }
708
709        final int count = mService.mStackIdToStack.size();
710        for (int i = 0; i < count; i++) {
711            final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
712            final int otherStackId = otherStack.mStackId;
713            if (StackId.isResizeableByDockedStack(otherStackId)
714                    && !otherStack.mBounds.equals(bounds)) {
715                mService.mH.sendMessage(
716                        mService.mH.obtainMessage(RESIZE_STACK, otherStackId,
717                                1 /*allowResizeInDockedMode*/, fullscreen ? null : bounds));
718            }
719        }
720    }
721
722    void detachDisplay() {
723        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
724
725        boolean doAnotherLayoutPass = false;
726        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
727            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
728            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
729                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
730                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
731                    // We are in the middle of changing the state of displays/stacks/tasks. We need
732                    // to finish that, before we let layout interfere with it.
733                    mService.removeWindowLocked(appWindows.get(winNdx));
734                    doAnotherLayoutPass = true;
735                }
736            }
737        }
738        if (doAnotherLayoutPass) {
739            mService.mWindowPlacerLocked.requestTraversal();
740        }
741
742        if (mStackId == DOCKED_STACK_ID) {
743            // Docked stack was detached from the display, so we no longer need to restrict the
744            // region of the screen other static stacks occupy. Go ahead and make them fullscreen.
745            resizeNonDockedStacks(FULLSCREEN, null);
746        }
747
748        close();
749    }
750
751    void resetAnimationBackgroundAnimator() {
752        mAnimationBackgroundAnimator = null;
753        mAnimationBackgroundSurface.hide();
754    }
755
756    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
757        int animLayer = winAnimator.mAnimLayer;
758        if (mAnimationBackgroundAnimator == null
759                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
760            mAnimationBackgroundAnimator = winAnimator;
761            animLayer = mService.adjustAnimationBackground(winAnimator);
762            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
763                    ((color >> 24) & 0xff) / 255f, 0);
764        }
765    }
766
767    void switchUser() {
768        int top = mTasks.size();
769        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
770            Task task = mTasks.get(taskNdx);
771            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
772                mTasks.remove(taskNdx);
773                mTasks.add(task);
774                --top;
775            }
776        }
777    }
778
779    void close() {
780        if (mAnimationBackgroundSurface != null) {
781            mAnimationBackgroundSurface.destroySurface();
782            mAnimationBackgroundSurface = null;
783        }
784        mDisplayContent = null;
785    }
786
787    public void dump(String prefix, PrintWriter pw) {
788        pw.println(prefix + "mStackId=" + mStackId);
789        pw.println(prefix + "mDeferDetach=" + mDeferDetach);
790        pw.println(prefix + "mFullscreen=" + mFullscreen);
791        pw.println(prefix + "mBounds=" + mBounds.toShortString());
792        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
793            mTasks.get(taskNdx).dump(prefix + "  ", pw);
794        }
795        if (mAnimationBackgroundSurface.isDimming()) {
796            pw.println(prefix + "mWindowAnimationBackgroundSurface:");
797            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
798        }
799        if (!mExitingAppTokens.isEmpty()) {
800            pw.println();
801            pw.println("  Exiting application tokens:");
802            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
803                WindowToken token = mExitingAppTokens.get(i);
804                pw.print("  Exiting App #"); pw.print(i);
805                pw.print(' '); pw.print(token);
806                pw.println(':');
807                token.dump(pw, "    ");
808            }
809        }
810    }
811
812    /** Fullscreen status of the stack without adjusting for other factors in the system like
813     * visibility of docked stack.
814     * Most callers should be using {@link #isFullscreen} as it take into consideration other
815     * system factors. */
816    boolean getRawFullscreen() {
817        return mFullscreen;
818    }
819
820    @Override
821    public boolean isFullscreen() {
822        if (useCurrentBounds()) {
823            return mFullscreen;
824        }
825        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
826        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
827        // system.
828        return true;
829    }
830
831    @Override
832    public DisplayInfo getDisplayInfo() {
833        return mDisplayContent.getDisplayInfo();
834    }
835
836    @Override
837    public String toString() {
838        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
839    }
840
841    @Override
842    public String toShortString() {
843        return "Stack=" + mStackId;
844    }
845
846    /**
847     * For docked workspace (or workspace that's side-by-side to the docked), provides
848     * information which side of the screen was the dock anchored.
849     */
850    int getDockSide() {
851        return getDockSide(mBounds);
852    }
853
854    int getDockSide(Rect bounds) {
855        if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
856            return DOCKED_INVALID;
857        }
858        if (mDisplayContent == null) {
859            return DOCKED_INVALID;
860        }
861        mDisplayContent.getLogicalDisplayRect(mTmpRect);
862        final int orientation = mService.mCurConfiguration.orientation;
863        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
864            // Portrait mode, docked either at the top or the bottom.
865            if (bounds.top - mTmpRect.top < mTmpRect.bottom - bounds.bottom) {
866                return DOCKED_TOP;
867            } else {
868                return DOCKED_BOTTOM;
869            }
870        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
871            // Landscape mode, docked either on the left or on the right.
872            if (bounds.left - mTmpRect.left < mTmpRect.right - bounds.right) {
873                return DOCKED_LEFT;
874            } else {
875                return DOCKED_RIGHT;
876            }
877        } else {
878            return DOCKED_INVALID;
879        }
880    }
881
882    boolean isVisibleLocked() {
883        return isVisibleLocked(false);
884    }
885
886    boolean isVisibleLocked(boolean ignoreVisibilityOnKeyguardShowing) {
887        final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded();
888        if (keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) {
889            // The keyguard is showing and the stack shouldn't show on top of the keyguard.
890            // We return false for visibility except in cases where the caller wants us to return
891            // true for visibility when the keyguard is showing. One example, is if the docked
892            // is being resized due to orientation while the keyguard is on.
893            return ignoreVisibilityOnKeyguardShowing;
894        }
895
896        for (int i = mTasks.size() - 1; i >= 0; i--) {
897            Task task = mTasks.get(i);
898            for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
899                if (!task.mAppTokens.get(j).hidden) {
900                    return true;
901                }
902            }
903        }
904        return false;
905    }
906}
907