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