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