TaskStack.java revision eb88d83fc5da8ea05033c03bbb9f0d4b804ba162
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
19import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
21import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
22import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
23import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
24import static android.view.WindowManager.DOCKED_BOTTOM;
25import static android.view.WindowManager.DOCKED_INVALID;
26import static android.view.WindowManager.DOCKED_LEFT;
27import static android.view.WindowManager.DOCKED_RIGHT;
28import static android.view.WindowManager.DOCKED_TOP;
29import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
30import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
31import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
32
33import android.app.ActivityManager.StackId;
34import android.content.res.Configuration;
35import android.graphics.Rect;
36import android.os.Debug;
37import android.os.RemoteException;
38import android.util.EventLog;
39import android.util.Slog;
40import android.util.SparseArray;
41import android.view.DisplayInfo;
42import android.view.Surface;
43import android.view.animation.PathInterpolator;
44
45import com.android.internal.policy.DividerSnapAlgorithm;
46import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
47import com.android.internal.policy.DockedDividerUtils;
48import com.android.server.EventLogTags;
49
50import java.io.PrintWriter;
51import java.util.ArrayList;
52
53public class TaskStack implements DimLayer.DimLayerUser,
54        BoundsAnimationController.AnimateBoundsUser {
55
56    /** Unique identifier */
57    final int mStackId;
58
59    /** The service */
60    private final WindowManagerService mService;
61
62    /** The display this stack sits under. */
63    private DisplayContent mDisplayContent;
64
65    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
66     * mTaskHistory in the ActivityStack with the same mStackId */
67    private final ArrayList<Task> mTasks = new ArrayList<>();
68
69    /** For comparison with DisplayContent bounds. */
70    private Rect mTmpRect = new Rect();
71    private Rect mTmpRect2 = new Rect();
72
73    /** Content limits relative to the DisplayContent this sits in. */
74    private Rect mBounds = new Rect();
75
76    /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
77    private final Rect mAdjustedBounds = new Rect();
78
79    /**
80     * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
81     * represent the state when the animation has ended.
82     */
83    private final Rect mFullyAdjustedImeBounds = new Rect();
84
85    /** Whether mBounds is fullscreen */
86    private boolean mFullscreen = true;
87
88    // Device rotation as of the last time {@link #mBounds} was set.
89    int mRotation;
90
91    /** Density as of last time {@link #mBounds} was set. */
92    int mDensity;
93
94    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
95    DimLayer mAnimationBackgroundSurface;
96
97    /** The particular window with an Animation with non-zero background color. */
98    WindowStateAnimator mAnimationBackgroundAnimator;
99
100    /** Application tokens that are exiting, but still on screen for animations. */
101    final AppTokenList mExitingAppTokens = new AppTokenList();
102
103    /** Detach this stack from its display when animation completes. */
104    boolean mDeferDetach;
105
106    // Display rotation as of the last time the display information was updated for this stack.
107    private int mLastUpdateDisplayInfoRotation = -1;
108    // Display rotation as of the last time the configuration was updated for this stack.
109    private int mLastConfigChangedRotation = -1;
110
111    // Whether the stack and all its tasks is currently being drag-resized
112    private boolean mDragResizing;
113
114    private final Rect mLastContentBounds = new Rect();
115    private final Rect mTmpAdjustedBounds = new Rect();
116    private boolean mAdjustedForIme;
117    private boolean mImeGoingAway;
118    private WindowState mImeWin;
119    private float mMinimizeAmount;
120    private float mAdjustImeAmount;
121    private final int mDockedStackMinimizeThickness;
122
123    // If this is true, the task will be down or upscaled
124    // to perfectly fit the region it would have been cropped
125    // to.
126    private boolean mForceScaleToCrop = false;
127    // By default, movement animations are applied to all
128    // window movement. If this is true, animations will not
129    // be applied within this stack. This is useful for example
130    // if the windows are moving as the result of a stack animation,
131    // in which case a second window animation would cause jitter.
132    private boolean mFreezeMovementAnimations = false;
133
134    // Temporary storage for the new bounds that should be used after the configuration change.
135    // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration().
136    private final Rect mBoundsAfterRotation = new Rect();
137
138    TaskStack(WindowManagerService service, int stackId) {
139        mService = service;
140        mStackId = stackId;
141        mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
142                com.android.internal.R.dimen.docked_stack_minimize_thickness);
143        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
144    }
145
146    DisplayContent getDisplayContent() {
147        return mDisplayContent;
148    }
149
150    ArrayList<Task> getTasks() {
151        return mTasks;
152    }
153
154    /**
155     * Set the bounds of the stack and its containing tasks.
156     * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
157     * @param configs Configuration for individual tasks, keyed by task id.
158     * @param taskBounds Bounds for individual tasks, keyed by task id.
159     * @return True if the stack bounds was changed.
160     * */
161    boolean setBounds(
162            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
163            SparseArray<Rect> taskTempInsetBounds) {
164        setBounds(stackBounds);
165
166        // Update bounds of containing tasks.
167        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
168            final Task task = mTasks.get(taskNdx);
169            Configuration config = configs.get(task.mTaskId);
170            if (config != null) {
171                Rect bounds = taskBounds.get(task.mTaskId);
172                if (task.isTwoFingerScrollMode()) {
173                    // This is a non-resizeable task that's docked (or side-by-side to the docked
174                    // stack). It might have been scrolled previously, and after the stack resizing,
175                    // it might no longer fully cover the stack area.
176                    // Save the old bounds and re-apply the scroll. This adjusts the bounds to
177                    // fit the new stack bounds.
178                    task.resizeLocked(bounds, config, false /* forced */);
179                    task.getBounds(mTmpRect);
180                    task.scrollLocked(mTmpRect);
181                } else {
182                    task.resizeLocked(bounds, config, false /* forced */);
183                    task.setTempInsetBounds(
184                            taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId)
185                                    : null);
186                }
187            } else {
188                Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?");
189            }
190        }
191        return true;
192    }
193
194    void prepareFreezingTaskBounds() {
195        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
196            final Task task = mTasks.get(taskNdx);
197            task.prepareFreezingBounds();
198        }
199    }
200
201    boolean isFullscreenBounds(Rect bounds) {
202        if (mDisplayContent == null || bounds == null) {
203            return true;
204        }
205        mDisplayContent.getLogicalDisplayRect(mTmpRect);
206        return mTmpRect.equals(bounds);
207    }
208
209    /**
210     * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
211     * the normal task bounds.
212     *
213     * @param bounds The adjusted bounds.
214     */
215    private void setAdjustedBounds(Rect bounds) {
216        if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
217            return;
218        }
219
220        mAdjustedBounds.set(bounds);
221        final boolean adjusted = !mAdjustedBounds.isEmpty();
222        Rect insetBounds = null;
223        if (adjusted && isAdjustedForMinimizedDock()) {
224            insetBounds = mBounds;
225        } else if (adjusted && isAdjustedForIme()) {
226            if (mImeGoingAway) {
227                insetBounds = mBounds;
228            } else {
229                insetBounds = mFullyAdjustedImeBounds;
230            }
231        }
232        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
233        mDisplayContent.layoutNeeded = true;
234    }
235
236    private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
237        if (mFullscreen) {
238            return;
239        }
240        // Update bounds of containing tasks.
241        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
242            final Task task = mTasks.get(taskNdx);
243            if (task.isTwoFingerScrollMode()) {
244                // If we're scrolling we don't care about your bounds or configs,
245                // they should be null as if we were in fullscreen.
246                task.resizeLocked(null, null, false /* forced */);
247                task.getBounds(mTmpRect2);
248                task.scrollLocked(mTmpRect2);
249            } else if (task.isResizeable() && task.mOverrideConfig != Configuration.EMPTY) {
250                task.getBounds(mTmpRect2);
251                mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
252                task.setTempInsetBounds(tempInsetBounds);
253                task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */);
254            }
255        }
256    }
257
258    private boolean setBounds(Rect bounds) {
259        boolean oldFullscreen = mFullscreen;
260        int rotation = Surface.ROTATION_0;
261        int density = DENSITY_DPI_UNDEFINED;
262        if (mDisplayContent != null) {
263            mDisplayContent.getLogicalDisplayRect(mTmpRect);
264            rotation = mDisplayContent.getDisplayInfo().rotation;
265            density = mDisplayContent.getDisplayInfo().logicalDensityDpi;
266            mFullscreen = bounds == null;
267            if (mFullscreen) {
268                bounds = mTmpRect;
269            }
270        }
271
272        if (bounds == null) {
273            // Can't set to fullscreen if we don't have a display to get bounds from...
274            return false;
275        }
276        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
277            return false;
278        }
279
280        if (mDisplayContent != null) {
281            mDisplayContent.mDimLayerController.updateDimLayer(this);
282            mAnimationBackgroundSurface.setBounds(bounds);
283        }
284
285        mBounds.set(bounds);
286        mRotation = rotation;
287        mDensity = density;
288
289        updateAdjustedBounds();
290
291        return true;
292    }
293
294    /** Bounds of the stack without adjusting for other factors in the system like visibility
295     * of docked stack.
296     * Most callers should be using {@link #getBounds} as it take into consideration other system
297     * factors. */
298    void getRawBounds(Rect out) {
299        out.set(mBounds);
300    }
301
302    /** Return true if the current bound can get outputted to the rest of the system as-is. */
303    private boolean useCurrentBounds() {
304        if (mFullscreen
305                || !StackId.isResizeableByDockedStack(mStackId)
306                || mDisplayContent == null
307                || mDisplayContent.getDockedStackLocked() != null) {
308            return true;
309        }
310        return false;
311    }
312
313    public void getBounds(Rect out) {
314        if (useCurrentBounds()) {
315            // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
316            // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
317            // stack is visible since it is already what we want to represent to the rest of the
318            // system.
319            if (!mAdjustedBounds.isEmpty()) {
320                out.set(mAdjustedBounds);
321            } else {
322                out.set(mBounds);
323            }
324            return;
325        }
326
327        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
328        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
329        // system.
330        mDisplayContent.getLogicalDisplayRect(out);
331    }
332
333    /** Bounds of the stack with other system factors taken into consideration. */
334    @Override
335    public void getDimBounds(Rect out) {
336        getBounds(out);
337    }
338
339    void updateDisplayInfo(Rect bounds) {
340        if (mDisplayContent == null) {
341            return;
342        }
343
344        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
345            mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
346        }
347        if (bounds != null) {
348            setBounds(bounds);
349            return;
350        } else if (mFullscreen) {
351            setBounds(null);
352            return;
353        }
354
355        mTmpRect2.set(mBounds);
356        final int newRotation = mDisplayContent.getDisplayInfo().rotation;
357        final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi;
358        if (mRotation == newRotation && mDensity == newDensity) {
359            setBounds(mTmpRect2);
360        } else {
361            mLastUpdateDisplayInfoRotation = newRotation;
362            updateBoundsAfterConfigChange(true);
363        }
364    }
365
366    boolean onConfigurationChanged() {
367        mLastConfigChangedRotation = getDisplayInfo().rotation;
368        return updateBoundsAfterConfigChange(false);
369    }
370
371    boolean updateBoundsAfterConfigChange(boolean scheduleResize) {
372        if (mLastConfigChangedRotation != mLastUpdateDisplayInfoRotation) {
373            // We wait for the rotation values after configuration change and display info. update
374            // to be equal before updating the bounds due to rotation change otherwise things might
375            // get out of alignment...
376            return false;
377        }
378
379        final int newRotation = getDisplayInfo().rotation;
380        final int newDensity = getDisplayInfo().logicalDensityDpi;
381
382        if (mRotation == newRotation && mDensity == newDensity) {
383            // Nothing to do here if the rotation didn't change
384            return false;
385        }
386
387        final int oldDockSide = mStackId == DOCKED_STACK_ID ? getDockSide() : DOCKED_INVALID;
388        mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
389        if (mStackId == DOCKED_STACK_ID) {
390            repositionDockedStackAfterRotation(mTmpRect2);
391            snapDockedStackAfterRotation(mTmpRect2);
392            final int newDockSide = getDockSide(mTmpRect2);
393            if (oldDockSide != newDockSide) {
394                mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
395            }
396        }
397
398        if (scheduleResize) {
399            // Post message to inform activity manager of the bounds change simulating
400            // a one-way call. We do this to prevent a deadlock between window manager
401            // lock and activity manager lock been held.
402            mService.mH.obtainMessage(RESIZE_STACK, mStackId,
403                    0 /*allowResizeInDockedMode*/, mTmpRect2).sendToTarget();
404        } else {
405            mBoundsAfterRotation.set(mTmpRect2);
406        }
407
408        return true;
409    }
410
411    void getBoundsForNewConfiguration(Rect outBounds) {
412        outBounds.set(mBoundsAfterRotation);
413        mBoundsAfterRotation.setEmpty();
414    }
415
416    /**
417     * Some dock sides are not allowed by the policy. This method queries the policy and moves
418     * the docked stack around if needed.
419     *
420     * @param inOutBounds the bounds of the docked stack to adjust
421     */
422    private void repositionDockedStackAfterRotation(Rect inOutBounds) {
423        int dockSide = getDockSide(inOutBounds);
424        if (mService.mPolicy.isDockSideAllowed(dockSide)) {
425            return;
426        }
427        mDisplayContent.getLogicalDisplayRect(mTmpRect);
428        dockSide = DockedDividerUtils.invertDockSide(dockSide);
429        switch (dockSide) {
430            case DOCKED_LEFT:
431                int movement = inOutBounds.left;
432                inOutBounds.left -= movement;
433                inOutBounds.right -= movement;
434                break;
435            case DOCKED_RIGHT:
436                movement = mTmpRect.right - inOutBounds.right;
437                inOutBounds.left += movement;
438                inOutBounds.right += movement;
439                break;
440            case DOCKED_TOP:
441                movement = inOutBounds.top;
442                inOutBounds.top -= movement;
443                inOutBounds.bottom -= movement;
444                break;
445            case DOCKED_BOTTOM:
446                movement = mTmpRect.bottom - inOutBounds.bottom;
447                inOutBounds.top += movement;
448                inOutBounds.bottom += movement;
449                break;
450        }
451    }
452
453    /**
454     * Snaps the bounds after rotation to the closest snap target for the docked stack.
455     */
456    private void snapDockedStackAfterRotation(Rect outBounds) {
457
458        // Calculate the current position.
459        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
460        final int dividerSize = mService.getDefaultDisplayContentLocked()
461                .getDockedDividerController().getContentWidth();
462        final int dockSide = getDockSide(outBounds);
463        final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
464                dockSide, dividerSize);
465        final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
466        final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
467
468        // Snap the position to a target.
469        final int rotation = displayInfo.rotation;
470        final int orientation = mService.mCurConfiguration.orientation;
471        mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
472        final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
473                mService.mContext.getResources(), displayWidth, displayHeight,
474                dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
475        final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
476
477        // Recalculate the bounds based on the position of the target.
478        DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
479                outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
480                dividerSize);
481    }
482
483    boolean isAnimating() {
484        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
485            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
486            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
487                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
488                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
489                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
490                    if (winAnimator.isAnimating() || winAnimator.mWin.mAnimatingExit) {
491                        return true;
492                    }
493                }
494            }
495        }
496        return false;
497    }
498
499    void addTask(Task task, boolean toTop) {
500        addTask(task, toTop, task.showForAllUsers());
501    }
502
503    /**
504     * Put a Task in this stack. Used for adding and moving.
505     * @param task The task to add.
506     * @param toTop Whether to add it to the top or bottom.
507     * @param showForAllUsers Whether to show the task regardless of the current user.
508     */
509    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
510        positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
511    }
512
513    void positionTask(Task task, int position, boolean showForAllUsers) {
514        final boolean canShowTask =
515                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
516        mTasks.remove(task);
517        int stackSize = mTasks.size();
518        int minPosition = 0;
519        int maxPosition = stackSize;
520
521        if (canShowTask) {
522            minPosition = computeMinPosition(minPosition, stackSize);
523        } else {
524            maxPosition = computeMaxPosition(maxPosition);
525        }
526        // Reset position based on minimum/maximum possible positions.
527        position = Math.min(Math.max(position, minPosition), maxPosition);
528
529        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM,
530                "positionTask: task=" + task + " position=" + position);
531        mTasks.add(position, task);
532
533        // If we are moving the task across stacks, the scroll is no longer valid.
534        if (task.mStack != this) {
535            task.resetScrollLocked();
536        }
537        task.mStack = this;
538        task.updateDisplayInfo(mDisplayContent);
539        boolean toTop = position == mTasks.size() - 1;
540        if (toTop) {
541            mDisplayContent.moveStack(this, true);
542        }
543        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
544    }
545
546    /** Calculate the minimum possible position for a task that can be shown to the user.
547     *  The minimum position will be above all other tasks that can't be shown.
548     *  @param minPosition The minimum position the caller is suggesting.
549     *                  We will start adjusting up from here.
550     *  @param size The size of the current task list.
551     */
552    private int computeMinPosition(int minPosition, int size) {
553        while (minPosition < size) {
554            final Task tmpTask = mTasks.get(minPosition);
555            final boolean canShowTmpTask =
556                    tmpTask.showForAllUsers()
557                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
558            if (canShowTmpTask) {
559                break;
560            }
561            minPosition++;
562        }
563        return minPosition;
564    }
565
566    /** Calculate the maximum possible position for a task that can't be shown to the user.
567     *  The maximum position will be below all other tasks that can be shown.
568     *  @param maxPosition The maximum position the caller is suggesting.
569     *                  We will start adjusting down from here.
570     */
571    private int computeMaxPosition(int maxPosition) {
572        while (maxPosition > 0) {
573            final Task tmpTask = mTasks.get(maxPosition - 1);
574            final boolean canShowTmpTask =
575                    tmpTask.showForAllUsers()
576                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
577            if (!canShowTmpTask) {
578                break;
579            }
580            maxPosition--;
581        }
582        return maxPosition;
583    }
584
585    void moveTaskToTop(Task task) {
586        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers="
587                + Debug.getCallers(6));
588        mTasks.remove(task);
589        addTask(task, true);
590    }
591
592    void moveTaskToBottom(Task task) {
593        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + task);
594        mTasks.remove(task);
595        addTask(task, false);
596    }
597
598    /**
599     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
600     * back.
601     * @param task The Task to delete.
602     */
603    void removeTask(Task task) {
604        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeTask: task=" + task);
605        mTasks.remove(task);
606        if (mDisplayContent != null) {
607            if (mTasks.isEmpty()) {
608                mDisplayContent.moveStack(this, false);
609            }
610            mDisplayContent.layoutNeeded = true;
611        }
612        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
613            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
614            if (wtoken.mTask == task) {
615                wtoken.mIsExiting = false;
616                mExitingAppTokens.remove(appNdx);
617            }
618        }
619    }
620
621    void attachDisplayContent(DisplayContent displayContent) {
622        if (mDisplayContent != null) {
623            throw new IllegalStateException("attachDisplayContent: Already attached");
624        }
625
626        mDisplayContent = displayContent;
627        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
628                "animation background stackId=" + mStackId);
629
630        Rect bounds = null;
631        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
632        if (mStackId == DOCKED_STACK_ID
633                || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId))) {
634            // The existence of a docked stack affects the size of other static stack created since
635            // the docked stack occupies a dedicated region on screen.
636            bounds = new Rect();
637            displayContent.getLogicalDisplayRect(mTmpRect);
638            mTmpRect2.setEmpty();
639            if (dockedStack != null) {
640                dockedStack.getRawBounds(mTmpRect2);
641            }
642            final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
643                    == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
644            getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
645                    mDisplayContent.mDividerControllerLocked.getContentWidth(),
646                    dockedOnTopOrLeft);
647        }
648
649        updateDisplayInfo(bounds);
650    }
651
652    void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) {
653        if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId))
654                || mDisplayContent == null) {
655            outBounds.set(mBounds);
656            return;
657        }
658
659        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
660        if (dockedStack == null) {
661            // Not sure why you are calling this method when there is no docked stack...
662            throw new IllegalStateException(
663                    "Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
664        }
665        if (!ignoreVisibility && !dockedStack.isVisibleLocked()) {
666            // The docked stack is being dismissed, but we caught before it finished being
667            // dismissed. In that case we want to treat it as if it is not occupying any space and
668            // let others occupy the whole display.
669            mDisplayContent.getLogicalDisplayRect(outBounds);
670            return;
671        }
672
673        final int dockedSide = dockedStack.getDockSide();
674        if (dockedSide == DOCKED_INVALID) {
675            // Not sure how you got here...Only thing we can do is return current bounds.
676            Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack);
677            outBounds.set(mBounds);
678            return;
679        }
680
681        mDisplayContent.getLogicalDisplayRect(mTmpRect);
682        dockedStack.getRawBounds(mTmpRect2);
683        final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
684        getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
685                mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
686
687    }
688
689    /**
690     * Outputs the bounds a stack should be given the presence of a docked stack on the display.
691     * @param displayRect The bounds of the display the docked stack is on.
692     * @param outBounds Output bounds that should be used for the stack.
693     * @param stackId Id of stack we are calculating the bounds for.
694     * @param dockedBounds Bounds of the docked stack.
695     * @param dockDividerWidth We need to know the width of the divider make to the output bounds
696     *                         close to the side of the dock.
697     * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
698     */
699    private void getStackDockedModeBounds(
700            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
701            boolean dockOnTopOrLeft) {
702        final boolean dockedStack = stackId == DOCKED_STACK_ID;
703        final boolean splitHorizontally = displayRect.width() > displayRect.height();
704
705        outBounds.set(displayRect);
706        if (dockedStack) {
707            if (mService.mDockedStackCreateBounds != null) {
708                outBounds.set(mService.mDockedStackCreateBounds);
709                return;
710            }
711
712            // The initial bounds of the docked stack when it is created about half the screen space
713            // and its bounds can be adjusted after that. The bounds of all other stacks are
714            // adjusted to occupy whatever screen space the docked stack isn't occupying.
715            final DisplayInfo di = mDisplayContent.getDisplayInfo();
716            mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
717                    mTmpRect2);
718            final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
719                    di.logicalWidth,
720                    di.logicalHeight,
721                    dockDividerWidth,
722                    mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT,
723                    mTmpRect2).getMiddleTarget().position;
724
725            if (dockOnTopOrLeft) {
726                if (splitHorizontally) {
727                    outBounds.right = position;
728                } else {
729                    outBounds.bottom = position;
730                }
731            } else {
732                if (splitHorizontally) {
733                    outBounds.left = position + dockDividerWidth;
734                } else {
735                    outBounds.top = position + dockDividerWidth;
736                }
737            }
738            return;
739        }
740
741        // Other stacks occupy whatever space is left by the docked stack.
742        if (!dockOnTopOrLeft) {
743            if (splitHorizontally) {
744                outBounds.right = dockedBounds.left - dockDividerWidth;
745            } else {
746                outBounds.bottom = dockedBounds.top - dockDividerWidth;
747            }
748        } else {
749            if (splitHorizontally) {
750                outBounds.left = dockedBounds.right + dockDividerWidth;
751            } else {
752                outBounds.top = dockedBounds.bottom + dockDividerWidth;
753            }
754        }
755        DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
756    }
757
758    void resetDockedStackToMiddle() {
759        if (mStackId != DOCKED_STACK_ID) {
760            throw new IllegalStateException("Not a docked stack=" + this);
761        }
762
763        mService.mDockedStackCreateBounds = null;
764
765        final Rect bounds = new Rect();
766        getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/);
767        mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID,
768                1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
769    }
770
771    void detachDisplay() {
772        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
773
774        boolean doAnotherLayoutPass = false;
775        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
776            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
777            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
778                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
779                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
780                    // We are in the middle of changing the state of displays/stacks/tasks. We need
781                    // to finish that, before we let layout interfere with it.
782                    mService.removeWindowLocked(appWindows.get(winNdx));
783                    doAnotherLayoutPass = true;
784                }
785            }
786        }
787        if (doAnotherLayoutPass) {
788            mService.mWindowPlacerLocked.requestTraversal();
789        }
790
791        close();
792    }
793
794    void resetAnimationBackgroundAnimator() {
795        mAnimationBackgroundAnimator = null;
796        mAnimationBackgroundSurface.hide();
797    }
798
799    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
800        int animLayer = winAnimator.mAnimLayer;
801        if (mAnimationBackgroundAnimator == null
802                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
803            mAnimationBackgroundAnimator = winAnimator;
804            animLayer = mService.adjustAnimationBackground(winAnimator);
805            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
806                    ((color >> 24) & 0xff) / 255f, 0);
807        }
808    }
809
810    void switchUser() {
811        int top = mTasks.size();
812        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
813            Task task = mTasks.get(taskNdx);
814            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
815                mTasks.remove(taskNdx);
816                mTasks.add(task);
817                --top;
818            }
819        }
820    }
821
822    void close() {
823        if (mAnimationBackgroundSurface != null) {
824            mAnimationBackgroundSurface.destroySurface();
825            mAnimationBackgroundSurface = null;
826        }
827        mDisplayContent = null;
828    }
829
830    /**
831     * Adjusts the stack bounds if the IME is visible.
832     *
833     * @param imeWin The IME window.
834     */
835    void setAdjustedForIme(WindowState imeWin) {
836        mAdjustedForIme = true;
837        mAdjustImeAmount = 0f;
838        mImeWin = imeWin;
839        mImeGoingAway = false;
840    }
841
842    boolean isAdjustedForIme() {
843        return mAdjustedForIme || mImeGoingAway;
844    }
845
846    boolean isAnimatingForIme() {
847        return mImeWin != null && mImeWin.isAnimatingLw();
848    }
849
850    /**
851     * Update the stack's bounds (crop or position) according to the IME window's
852     * current position. When IME window is animated, the bottom stack is animated
853     * together to track the IME window's current position, and the top stack is
854     * cropped as necessary.
855     *
856     * @return true if a traversal should be performed after the adjustment.
857     */
858    boolean updateAdjustForIme(float adjustAmount) {
859        if (adjustAmount != mAdjustImeAmount) {
860            mAdjustImeAmount = adjustAmount;
861            updateAdjustedBounds();
862            return isVisibleForUserLocked();
863        } else {
864            return false;
865        }
866    }
867
868    /**
869     * Resets the adjustment after it got adjusted for the IME.
870     * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
871     *                        animations; otherwise, set flag and animates the window away together
872     *                        with IME window.
873     */
874    void resetAdjustedForIme(boolean adjustBoundsNow) {
875        if (adjustBoundsNow) {
876            mImeWin = null;
877            mAdjustedForIme = false;
878            mImeGoingAway = false;
879            mAdjustImeAmount = 0f;
880            updateAdjustedBounds();
881        } else {
882            mImeGoingAway |= mAdjustedForIme;
883        }
884    }
885
886    /**
887     * Sets the amount how much we currently minimize our stack.
888     *
889     * @param minimizeAmount The amount, between 0 and 1.
890     * @return Whether the amount has changed and a layout is needed.
891     */
892    boolean setAdjustedForMinimizedDock(float minimizeAmount) {
893        if (minimizeAmount != mMinimizeAmount) {
894            mMinimizeAmount = minimizeAmount;
895            updateAdjustedBounds();
896            return isVisibleForUserLocked();
897        } else {
898            return false;
899        }
900    }
901
902    boolean isAdjustedForMinimizedDock() {
903        return mMinimizeAmount != 0f;
904    }
905
906    private boolean adjustForIME(final WindowState imeWin) {
907        final int dockedSide = getDockSide();
908        final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
909        if (imeWin == null || !dockedTopOrBottom) {
910            return false;
911        }
912
913        final Rect displayContentRect = mTmpRect;
914        final Rect contentBounds = mTmpRect2;
915
916        // Calculate the content bounds excluding the area occupied by IME
917        getDisplayContent().getContentRect(displayContentRect);
918        contentBounds.set(displayContentRect);
919        int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
920
921        imeTop += imeWin.getGivenContentInsetsLw().top;
922        if (contentBounds.bottom > imeTop) {
923            contentBounds.bottom = imeTop;
924        }
925
926        mLastContentBounds.set(contentBounds);
927        final int yOffset = displayContentRect.bottom - contentBounds.bottom;
928
929        if (dockedSide == DOCKED_TOP) {
930            // If this stack is docked on top, we make it smaller so the bottom stack is not
931            // occluded by IME. We shift its bottom up by the height of the IME (capped by
932            // the display content rect). Note that we don't change the task bounds.
933            int bottom = Math.max(
934                    mBounds.bottom - yOffset, displayContentRect.top);
935            mTmpAdjustedBounds.set(mBounds);
936            mTmpAdjustedBounds.bottom =
937                    (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
938            mFullyAdjustedImeBounds.set(mBounds);
939        } else {
940            // If this stack is docked on bottom, we shift it up so that it's not occluded by
941            // IME. We try to move it up by the height of the IME window (although the best
942            // we could do is to make the top stack fully collapsed).
943            final int dividerWidth = getDisplayContent().mDividerControllerLocked
944                    .getContentWidth();
945            int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
946            mTmpAdjustedBounds.set(mBounds);
947            mTmpAdjustedBounds.top =
948                    (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top);
949            mTmpAdjustedBounds.bottom = mTmpAdjustedBounds.top + mBounds.height();
950            mFullyAdjustedImeBounds.set(mBounds);
951            mFullyAdjustedImeBounds.top = top;
952            mFullyAdjustedImeBounds.bottom = top + mBounds.height();
953        }
954        return true;
955    }
956
957    private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
958        final int dockSide = getDockSide();
959        if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
960            return false;
961        }
962
963        if (dockSide == DOCKED_TOP) {
964            mService.getStableInsetsLocked(mTmpRect);
965            int topInset = mTmpRect.top;
966            mTmpAdjustedBounds.set(mBounds);
967            mTmpAdjustedBounds.bottom =
968                    (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom);
969        } else if (dockSide == DOCKED_LEFT) {
970            mTmpAdjustedBounds.set(mBounds);
971            final int width = mBounds.width();
972            mTmpAdjustedBounds.right =
973                    (int) (minimizeAmount * mDockedStackMinimizeThickness
974                            + (1 - minimizeAmount) * mBounds.right);
975            mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
976        } else if (dockSide == DOCKED_RIGHT) {
977            mTmpAdjustedBounds.set(mBounds);
978            mTmpAdjustedBounds.left =
979                    (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness)
980                            + (1 - minimizeAmount) * mBounds.left);
981        }
982        return true;
983    }
984
985    /**
986     * @return the distance in pixels how much the stack gets minimized from it's original size
987     */
988    int getMinimizeDistance() {
989        final int dockSide = getDockSide();
990        if (dockSide == DOCKED_INVALID) {
991            return 0;
992        }
993
994        if (dockSide == DOCKED_TOP) {
995            mService.getStableInsetsLocked(mTmpRect);
996            int topInset = mTmpRect.top;
997            return mBounds.bottom - topInset;
998        } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
999            return mBounds.width() - mDockedStackMinimizeThickness;
1000        } else {
1001            return 0;
1002        }
1003    }
1004
1005    /**
1006     * Updates the adjustment depending on it's current state.
1007     */
1008    void updateAdjustedBounds() {
1009        boolean adjust = false;
1010        if (mMinimizeAmount != 0f) {
1011            adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
1012        } else if (mAdjustedForIme) {
1013            adjust = adjustForIME(mImeWin);
1014        }
1015        if (!adjust) {
1016            mTmpAdjustedBounds.setEmpty();
1017            mLastContentBounds.setEmpty();
1018        }
1019        setAdjustedBounds(mTmpAdjustedBounds);
1020    }
1021
1022    boolean isAdjustedForMinimizedDockedStack() {
1023        return mMinimizeAmount != 0f;
1024    }
1025
1026    public void dump(String prefix, PrintWriter pw) {
1027        pw.println(prefix + "mStackId=" + mStackId);
1028        pw.println(prefix + "mDeferDetach=" + mDeferDetach);
1029        pw.println(prefix + "mFullscreen=" + mFullscreen);
1030        pw.println(prefix + "mBounds=" + mBounds.toShortString());
1031        if (!mAdjustedBounds.isEmpty()) {
1032            pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
1033        }
1034        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
1035            mTasks.get(taskNdx).dump(prefix + "  ", pw);
1036        }
1037        if (mAnimationBackgroundSurface.isDimming()) {
1038            pw.println(prefix + "mWindowAnimationBackgroundSurface:");
1039            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
1040        }
1041        if (!mExitingAppTokens.isEmpty()) {
1042            pw.println();
1043            pw.println("  Exiting application tokens:");
1044            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
1045                WindowToken token = mExitingAppTokens.get(i);
1046                pw.print("  Exiting App #"); pw.print(i);
1047                pw.print(' '); pw.print(token);
1048                pw.println(':');
1049                token.dump(pw, "    ");
1050            }
1051        }
1052    }
1053
1054    /** Fullscreen status of the stack without adjusting for other factors in the system like
1055     * visibility of docked stack.
1056     * Most callers should be using {@link #isFullscreen} as it take into consideration other
1057     * system factors. */
1058    boolean getRawFullscreen() {
1059        return mFullscreen;
1060    }
1061
1062    @Override
1063    public boolean isFullscreen() {
1064        if (useCurrentBounds()) {
1065            return mFullscreen;
1066        }
1067        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
1068        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
1069        // system.
1070        return true;
1071    }
1072
1073    @Override
1074    public DisplayInfo getDisplayInfo() {
1075        return mDisplayContent.getDisplayInfo();
1076    }
1077
1078    @Override
1079    public String toString() {
1080        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
1081    }
1082
1083    @Override
1084    public String toShortString() {
1085        return "Stack=" + mStackId;
1086    }
1087
1088    /**
1089     * For docked workspace (or workspace that's side-by-side to the docked), provides
1090     * information which side of the screen was the dock anchored.
1091     */
1092    int getDockSide() {
1093        return getDockSide(mBounds);
1094    }
1095
1096    int getDockSide(Rect bounds) {
1097        if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
1098            return DOCKED_INVALID;
1099        }
1100        if (mDisplayContent == null) {
1101            return DOCKED_INVALID;
1102        }
1103        mDisplayContent.getLogicalDisplayRect(mTmpRect);
1104        final int orientation = mService.mCurConfiguration.orientation;
1105        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
1106            // Portrait mode, docked either at the top or the bottom.
1107            if (bounds.top - mTmpRect.top <= mTmpRect.bottom - bounds.bottom) {
1108                return DOCKED_TOP;
1109            } else {
1110                return DOCKED_BOTTOM;
1111            }
1112        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
1113            // Landscape mode, docked either on the left or on the right.
1114            if (bounds.left - mTmpRect.left <= mTmpRect.right - bounds.right) {
1115                return DOCKED_LEFT;
1116            } else {
1117                return DOCKED_RIGHT;
1118            }
1119        } else {
1120            return DOCKED_INVALID;
1121        }
1122    }
1123
1124    boolean isVisibleLocked() {
1125        final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded()
1126                && !mService.mAnimator.mKeyguardGoingAway;
1127        if (keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) {
1128            // The keyguard is showing and the stack shouldn't show on top of the keyguard.
1129            return false;
1130        }
1131
1132        for (int i = mTasks.size() - 1; i >= 0; i--) {
1133            final Task task = mTasks.get(i);
1134            for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
1135                if (!task.mAppTokens.get(j).hidden) {
1136                    return true;
1137                }
1138            }
1139        }
1140
1141        return false;
1142    }
1143
1144    /**
1145     * @return true if a the stack is visible for the current in user, ignoring any other visibility
1146     *         aspects, and false otherwise
1147     */
1148    boolean isVisibleForUserLocked() {
1149        for (int i = mTasks.size() - 1; i >= 0; i--) {
1150            final Task task = mTasks.get(i);
1151            if (task.isVisibleForUser()) {
1152                return true;
1153            }
1154        }
1155        return false;
1156    }
1157
1158    boolean isDragResizing() {
1159        return mDragResizing;
1160    }
1161
1162    private void setDragResizingLocked(boolean resizing) {
1163        if (mDragResizing == resizing) {
1164            return;
1165        }
1166        mDragResizing = resizing;
1167        for (int i = mTasks.size() - 1; i >= 0 ; i--) {
1168            mTasks.get(i).resetDragResizingChangeReported();
1169        }
1170    }
1171
1172    @Override  // AnimatesBounds
1173    public boolean setSize(Rect bounds) {
1174        synchronized (mService.mWindowMap) {
1175            if (mDisplayContent == null) {
1176                return false;
1177            }
1178        }
1179        try {
1180            mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false, -1);
1181        } catch (RemoteException e) {
1182        }
1183        return true;
1184    }
1185
1186    public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) {
1187        synchronized (mService.mWindowMap) {
1188            if (mDisplayContent == null) {
1189                return false;
1190            }
1191            if (mStackId != PINNED_STACK_ID) {
1192                Slog.w(TAG_WM, "Attempt to use pinned stack resize animation helper on"
1193                        + "non pinned stack");
1194                return false;
1195            }
1196        }
1197        try {
1198            mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds);
1199        } catch (RemoteException e) {
1200            // I don't believe you.
1201        }
1202        return true;
1203    }
1204
1205    @Override  // AnimatesBounds
1206    public void onAnimationStart() {
1207        synchronized (mService.mWindowMap) {
1208            mFreezeMovementAnimations = true;
1209            mForceScaleToCrop = true;
1210        }
1211    }
1212
1213    @Override  // AnimatesBounds
1214    public void onAnimationEnd() {
1215        synchronized (mService.mWindowMap) {
1216            mFreezeMovementAnimations = false;
1217            mForceScaleToCrop = false;
1218            mService.requestTraversal();
1219        }
1220        if (mStackId == PINNED_STACK_ID) {
1221            try {
1222                mService.mActivityManager.notifyPinnedStackAnimationEnded();
1223            } catch (RemoteException e) {
1224                // I don't believe you...
1225            }
1226        }
1227    }
1228
1229    @Override
1230    public void moveToFullscreen() {
1231        try {
1232            mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true);
1233        } catch (RemoteException e) {
1234            e.printStackTrace();
1235        }
1236    }
1237
1238    @Override
1239    public void getFullScreenBounds(Rect bounds) {
1240        getDisplayContent().getContentRect(bounds);
1241    }
1242
1243    public boolean getFreezeMovementAnimations() {
1244        return mFreezeMovementAnimations;
1245    }
1246
1247    public boolean getForceScaleToCrop() {
1248        return mForceScaleToCrop;
1249    }
1250}
1251