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