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 
17 package com.android.server.wm;
18 
19 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
20 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
25 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
26 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
27 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
29 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
30 import static android.view.Display.DEFAULT_DISPLAY;
31 import static android.view.WindowManager.DOCKED_BOTTOM;
32 import static android.view.WindowManager.DOCKED_INVALID;
33 import static android.view.WindowManager.DOCKED_LEFT;
34 import static android.view.WindowManager.DOCKED_RIGHT;
35 import static android.view.WindowManager.DOCKED_TOP;
36 
37 import static com.android.server.wm.BoundsAnimationController.FADE_IN;
38 import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
39 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
40 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
41 import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
42 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
43 import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
44 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
45 import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
46 import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
47 import static com.android.server.wm.StackProto.ANIMATING_BOUNDS;
48 import static com.android.server.wm.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
49 import static com.android.server.wm.StackProto.BOUNDS;
50 import static com.android.server.wm.StackProto.DEFER_REMOVAL;
51 import static com.android.server.wm.StackProto.FILLS_PARENT;
52 import static com.android.server.wm.StackProto.ID;
53 import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT;
54 import static com.android.server.wm.StackProto.TASKS;
55 import static com.android.server.wm.StackProto.WINDOW_CONTAINER;
56 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
57 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
58 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
59 
60 import android.annotation.CallSuper;
61 import android.app.RemoteAction;
62 import android.content.res.Configuration;
63 import android.graphics.Point;
64 import android.graphics.Rect;
65 import android.graphics.Region;
66 import android.os.RemoteException;
67 import android.util.DisplayMetrics;
68 import android.util.EventLog;
69 import android.util.Slog;
70 import android.util.proto.ProtoOutputStream;
71 import android.view.DisplayCutout;
72 import android.view.DisplayInfo;
73 import android.view.SurfaceControl;
74 
75 import com.android.internal.annotations.VisibleForTesting;
76 import com.android.internal.policy.DividerSnapAlgorithm;
77 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
78 import com.android.internal.policy.DockedDividerUtils;
79 import com.android.server.EventLogTags;
80 
81 import java.io.PrintWriter;
82 import java.util.List;
83 
84 public class TaskStack extends WindowContainer<Task> implements
85         BoundsAnimationTarget, ConfigurationContainerListener {
86     /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
87      * restrict IME adjustment so that a min portion of top stack remains visible.*/
88     private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
89 
90     /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
91     private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
92 
93     /** Unique identifier */
94     final int mStackId;
95 
96     /** For comparison with DisplayContent bounds. */
97     private Rect mTmpRect = new Rect();
98     private Rect mTmpRect2 = new Rect();
99     private Rect mTmpRect3 = new Rect();
100 
101     /** For Pinned stack controlling. */
102     private Rect mTmpFromBounds = new Rect();
103     private Rect mTmpToBounds = new Rect();
104 
105     /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
106     private final Rect mAdjustedBounds = new Rect();
107 
108     /**
109      * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
110      * represent the state when the animation has ended.
111      */
112     private final Rect mFullyAdjustedImeBounds = new Rect();
113 
114     private SurfaceControl mAnimationBackgroundSurface;
115     private boolean mAnimationBackgroundSurfaceIsShown = false;
116 
117     /** The particular window with an Animation with non-zero background color. */
118     private WindowStateAnimator mAnimationBackgroundAnimator;
119 
120     /** Application tokens that are exiting, but still on screen for animations. */
121     final AppTokenList mExitingAppTokens = new AppTokenList();
122     final AppTokenList mTmpAppTokens = new AppTokenList();
123 
124     /** Detach this stack from its display when animation completes. */
125     // TODO: maybe tie this to WindowContainer#removeChild some how...
126     boolean mDeferRemoval;
127 
128     private final Rect mTmpAdjustedBounds = new Rect();
129     private boolean mAdjustedForIme;
130     private boolean mImeGoingAway;
131     private WindowState mImeWin;
132     private float mMinimizeAmount;
133     private float mAdjustImeAmount;
134     private float mAdjustDividerAmount;
135     private final int mDockedStackMinimizeThickness;
136 
137     // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
138     // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
139     // would otherwise apply while resizing, while resizing in the bounds animating mode.
140     private boolean mBoundsAnimating = false;
141     // Set when an animation has been requested but has not yet started from the UI thread. This is
142     // cleared when the animation actually starts.
143     private boolean mBoundsAnimatingRequested = false;
144     private boolean mBoundsAnimatingToFullscreen = false;
145     private boolean mCancelCurrentBoundsAnimation = false;
146     private Rect mBoundsAnimationTarget = new Rect();
147     private Rect mBoundsAnimationSourceHintBounds = new Rect();
148     private @BoundsAnimationController.AnimationType int mAnimationType;
149 
150     Rect mPreAnimationBounds = new Rect();
151 
152     private Dimmer mDimmer = new Dimmer(this);
153 
154     // TODO: remove after unification.
155     ActivityStack mActivityStack;
156 
157     /**
158      * For {@link #prepareSurfaces}.
159      */
160     final Rect mTmpDimBoundsRect = new Rect();
161     private final Point mLastSurfaceSize = new Point();
162 
163     private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry =
164             new AnimatingAppWindowTokenRegistry();
165 
TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack)166     TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack) {
167         super(service);
168         mStackId = stackId;
169         mActivityStack = activityStack;
170         activityStack.registerConfigurationChangeListener(this);
171         mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
172                 com.android.internal.R.dimen.docked_stack_minimize_thickness);
173         EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
174     }
175 
findHomeTask()176     Task findHomeTask() {
177         if (!isActivityTypeHome() || mChildren.isEmpty()) {
178             return null;
179         }
180         return mChildren.get(mChildren.size() - 1);
181     }
182 
prepareFreezingTaskBounds()183     void prepareFreezingTaskBounds() {
184         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
185             final Task task = mChildren.get(taskNdx);
186             task.prepareFreezingBounds();
187         }
188     }
189 
190     /**
191      * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
192      * the normal task bounds.
193      *
194      * @param bounds The adjusted bounds.
195      */
setAdjustedBounds(Rect bounds)196     private void setAdjustedBounds(Rect bounds) {
197         if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
198             return;
199         }
200 
201         mAdjustedBounds.set(bounds);
202         final boolean adjusted = !mAdjustedBounds.isEmpty();
203         Rect insetBounds = null;
204         if (adjusted && isAdjustedForMinimizedDockedStack()) {
205             insetBounds = getRawBounds();
206         } else if (adjusted && mAdjustedForIme) {
207             if (mImeGoingAway) {
208                 insetBounds = getRawBounds();
209             } else {
210                 insetBounds = mFullyAdjustedImeBounds;
211             }
212         }
213         alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : getRawBounds(), insetBounds);
214         mDisplayContent.setLayoutNeeded();
215 
216         updateSurfaceBounds();
217     }
218 
alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds)219     private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
220         if (matchParentBounds()) {
221             return;
222         }
223 
224         final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
225 
226         // Update bounds of containing tasks.
227         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
228             final Task task = mChildren.get(taskNdx);
229             task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
230         }
231     }
232 
updateAnimationBackgroundBounds()233     private void updateAnimationBackgroundBounds() {
234         if (mAnimationBackgroundSurface == null) {
235             return;
236         }
237         getRawBounds(mTmpRect);
238         final Rect stackBounds = getBounds();
239         getPendingTransaction()
240                 .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
241                 .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
242                         mTmpRect.top - stackBounds.top);
243         scheduleAnimation();
244     }
245 
hideAnimationSurface()246     private void hideAnimationSurface() {
247         if (mAnimationBackgroundSurface == null) {
248             return;
249         }
250         getPendingTransaction().hide(mAnimationBackgroundSurface);
251         mAnimationBackgroundSurfaceIsShown = false;
252         scheduleAnimation();
253     }
254 
showAnimationSurface(float alpha)255     private void showAnimationSurface(float alpha) {
256         if (mAnimationBackgroundSurface == null) {
257             return;
258         }
259         getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
260                 .setAlpha(mAnimationBackgroundSurface, alpha)
261                 .show(mAnimationBackgroundSurface);
262         mAnimationBackgroundSurfaceIsShown = true;
263         scheduleAnimation();
264     }
265 
266     @Override
setBounds(Rect bounds)267     public int setBounds(Rect bounds) {
268         return setBounds(getRequestedOverrideBounds(), bounds);
269     }
270 
setBounds(Rect existing, Rect bounds)271     private int setBounds(Rect existing, Rect bounds) {
272         if (equivalentBounds(existing, bounds)) {
273             return BOUNDS_CHANGE_NONE;
274         }
275 
276         final int result = super.setBounds(bounds);
277 
278         if (getParent() != null) {
279             updateAnimationBackgroundBounds();
280         }
281 
282         updateAdjustedBounds();
283 
284         updateSurfaceBounds();
285         return result;
286     }
287 
288     /** Bounds of the stack without adjusting for other factors in the system like visibility
289      * of docked stack.
290      * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a
291      * it takes into consideration other system factors. */
getRawBounds(Rect out)292     void getRawBounds(Rect out) {
293         out.set(getRawBounds());
294     }
295 
getRawBounds()296     Rect getRawBounds() {
297         return super.getBounds();
298     }
299 
300     @Override
getBounds(Rect bounds)301     public void getBounds(Rect bounds) {
302         bounds.set(getBounds());
303     }
304 
305     @Override
getBounds()306     public Rect getBounds() {
307         // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
308         // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
309         // stack is visible since it is already what we want to represent to the rest of the
310         // system.
311         if (!mAdjustedBounds.isEmpty()) {
312             return mAdjustedBounds;
313         } else {
314             return super.getBounds();
315         }
316     }
317 
318     /**
319      * Sets the bounds animation target bounds ahead of an animation.  This can't currently be done
320      * in onAnimationStart() since that is started on the UiThread.
321      */
setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen)322     private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
323             boolean toFullscreen) {
324         mBoundsAnimatingRequested = true;
325         mBoundsAnimatingToFullscreen = toFullscreen;
326         if (destBounds != null) {
327             mBoundsAnimationTarget.set(destBounds);
328         } else {
329             mBoundsAnimationTarget.setEmpty();
330         }
331         if (sourceHintBounds != null) {
332             mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
333         } else if (!mBoundsAnimating) {
334             // If the bounds are already animating, we don't want to reset the source hint. This is
335             // because the source hint is sent when starting the animation from the client that
336             // requested to enter pip. Other requests can adjust the pip bounds during an animation,
337             // but could accidentally reset the source hint bounds.
338             mBoundsAnimationSourceHintBounds.setEmpty();
339         }
340 
341         mPreAnimationBounds.set(getRawBounds());
342     }
343 
344     /**
345      * @return the final bounds for the bounds animation.
346      */
getFinalAnimationBounds(Rect outBounds)347     void getFinalAnimationBounds(Rect outBounds) {
348         outBounds.set(mBoundsAnimationTarget);
349     }
350 
351     /**
352      * @return the final source bounds for the bounds animation.
353      */
getFinalAnimationSourceHintBounds(Rect outBounds)354     void getFinalAnimationSourceHintBounds(Rect outBounds) {
355         outBounds.set(mBoundsAnimationSourceHintBounds);
356     }
357 
358     /**
359      * @return the final animation bounds if the task stack is currently being animated, or the
360      *         current stack bounds otherwise.
361      */
getAnimationOrCurrentBounds(Rect outBounds)362     void getAnimationOrCurrentBounds(Rect outBounds) {
363         if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
364             getFinalAnimationBounds(outBounds);
365             return;
366         }
367         getBounds(outBounds);
368     }
369 
370     /** Bounds of the stack with other system factors taken into consideration. */
getDimBounds(Rect out)371     public void getDimBounds(Rect out) {
372         getBounds(out);
373     }
374 
375     /**
376      * Updates the passed-in {@code inOutBounds} based on the current state of the
377      * pinned controller. This gets run *after* the override configuration is updated, so it's
378      * safe to rely on the controller's state in here (though eventually this dependence should
379      * be removed).
380      *
381      * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
382      * update pinned controller state.
383      *
384      * @param inOutBounds the bounds to update (both input and output).
385      * @return true if bounds were updated to some non-empty value.
386      */
calculatePinnedBoundsForConfigChange(Rect inOutBounds)387     boolean calculatePinnedBoundsForConfigChange(Rect inOutBounds) {
388         boolean animating = false;
389         if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
390             animating = true;
391             getFinalAnimationBounds(mTmpRect2);
392         } else {
393             mTmpRect2.set(inOutBounds);
394         }
395         boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
396                 mTmpRect2, mTmpRect3);
397         if (updated) {
398             inOutBounds.set(mTmpRect3);
399 
400             // The final boundary is updated while there is an existing boundary animation. Let's
401             // cancel this animation to prevent the obsolete animation overwritten updated bounds.
402             if (animating && !inOutBounds.equals(mBoundsAnimationTarget)) {
403                 final DisplayContent displayContent = getDisplayContent();
404                 displayContent.mBoundsAnimationController.getHandler().post(() ->
405                         displayContent.mBoundsAnimationController.cancel(this));
406             }
407             // Once we've set the bounds based on the rotation of the old bounds in the new
408             // orientation, clear the animation target bounds since they are obsolete, and
409             // cancel any currently running animations
410             mBoundsAnimationTarget.setEmpty();
411             mBoundsAnimationSourceHintBounds.setEmpty();
412             mCancelCurrentBoundsAnimation = true;
413         }
414         return updated;
415     }
416 
417     /**
418      * Updates the passed-in {@code inOutBounds} based on the current state of the
419      * docked controller. This gets run *after* the override configuration is updated, so it's
420      * safe to rely on the controller's state in here (though eventually this dependence should
421      * be removed).
422      *
423      * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
424      * update docked controller state.
425      *
426      * @param parentConfig the parent configuration for reference.
427      * @param inOutBounds the bounds to update (both input and output).
428      */
calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds)429     void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) {
430         final boolean primary =
431                 getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
432         repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds);
433         final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout;
434         snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds);
435         if (primary) {
436             final int newDockSide = getDockSide(parentConfig, inOutBounds);
437             // Update the dock create mode and clear the dock create bounds, these
438             // might change after a rotation and the original values will be invalid.
439             mWmService.setDockedStackCreateStateLocked(
440                     (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
441                             ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
442                             : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
443                     null);
444             mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
445         }
446     }
447 
448     /**
449      * Some primary split screen sides are not allowed by the policy. This method queries the policy
450      * and moves the primary stack around if needed.
451      *
452      * @param parentConfig the configuration of the stack's parent.
453      * @param primary true if adjusting the primary docked stack, false for secondary.
454      * @param inOutBounds the bounds of the stack to adjust.
455      */
repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary, Rect inOutBounds)456     void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary,
457             Rect inOutBounds) {
458         final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds);
459         final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide);
460         final int primaryDockSide = primary ? dockSide : otherDockSide;
461         if (mDisplayContent.getDockedDividerController()
462                 .canPrimaryStackDockTo(primaryDockSide,
463                         parentConfig.windowConfiguration.getBounds(),
464                         parentConfig.windowConfiguration.getRotation())) {
465             return;
466         }
467         final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
468         switch (otherDockSide) {
469             case DOCKED_LEFT:
470                 int movement = inOutBounds.left;
471                 inOutBounds.left -= movement;
472                 inOutBounds.right -= movement;
473                 break;
474             case DOCKED_RIGHT:
475                 movement = parentBounds.right - inOutBounds.right;
476                 inOutBounds.left += movement;
477                 inOutBounds.right += movement;
478                 break;
479             case DOCKED_TOP:
480                 movement = inOutBounds.top;
481                 inOutBounds.top -= movement;
482                 inOutBounds.bottom -= movement;
483                 break;
484             case DOCKED_BOTTOM:
485                 movement = parentBounds.bottom - inOutBounds.bottom;
486                 inOutBounds.top += movement;
487                 inOutBounds.bottom += movement;
488                 break;
489         }
490     }
491 
492     /**
493      * Snaps the bounds after rotation to the closest snap target for the docked stack.
494      */
snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout, Rect outBounds)495     void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout,
496             Rect outBounds) {
497 
498         // Calculate the current position.
499         final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
500         final int dockSide = getDockSide(parentConfig, outBounds);
501         final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
502                 dockSide, dividerSize);
503         final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
504         final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
505 
506         // Snap the position to a target.
507         final int rotation = parentConfig.windowConfiguration.getRotation();
508         final int orientation = parentConfig.orientation;
509         mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
510                 displayCutout, outBounds);
511         final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
512                 mWmService.mContext.getResources(), displayWidth, displayHeight,
513                 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
514                 getDockSide(), isMinimizedDockAndHomeStackResizable());
515         final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
516 
517         // Recalculate the bounds based on the position of the target.
518         DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
519                 outBounds, displayWidth, displayHeight,
520                 dividerSize);
521     }
522 
523     // TODO: Checkout the call points of this method and the ones below to see how they can fit in WC.
addTask(Task task, int position)524     void addTask(Task task, int position) {
525         addTask(task, position, task.showForAllUsers(), true /* moveParents */);
526     }
527 
528     /**
529      * Put a Task in this stack. Used for adding only.
530      * When task is added to top of the stack, the entire branch of the hierarchy (including stack
531      * and display) will be brought to top.
532      * @param task The task to add.
533      * @param position Target position to add the task to.
534      * @param showForAllUsers Whether to show the task regardless of the current user.
535      */
addTask(Task task, int position, boolean showForAllUsers, boolean moveParents)536     void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) {
537         final TaskStack currentStack = task.mStack;
538         // TODO: We pass stack to task's constructor, but we still need to call this method.
539         // This doesn't make sense, mStack will already be set equal to "this" at this point.
540         if (currentStack != null && currentStack.mStackId != mStackId) {
541             throw new IllegalStateException("Trying to add taskId=" + task.mTaskId
542                     + " to stackId=" + mStackId
543                     + ", but it is already attached to stackId=" + task.mStack.mStackId);
544         }
545 
546         // Add child task.
547         task.mStack = this;
548         addChild(task, null);
549 
550         // Move child to a proper position, as some restriction for position might apply.
551         positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers);
552     }
553 
positionChildAt(Task child, int position)554     void positionChildAt(Task child, int position) {
555         if (DEBUG_STACK) {
556             Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position);
557         }
558         if (child == null) {
559             if (DEBUG_STACK) {
560                 Slog.i(TAG_WM, "positionChildAt: could not find task=" + this);
561             }
562             return;
563         }
564         child.positionAt(position);
565         getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
566     }
567 
positionChildAtTop(Task child, boolean includingParents)568     void positionChildAtTop(Task child, boolean includingParents) {
569         if (child == null) {
570             // TODO: Fix the call-points that cause this to happen.
571             return;
572         }
573 
574         positionChildAt(POSITION_TOP, child, includingParents);
575 
576         final DisplayContent displayContent = getDisplayContent();
577         if (displayContent.mAppTransition.isTransitionSet()) {
578             child.setSendingToBottom(false);
579         }
580         displayContent.layoutAndAssignWindowLayersIfNeeded();
581     }
582 
positionChildAtBottom(Task child, boolean includingParents)583     void positionChildAtBottom(Task child, boolean includingParents) {
584         if (child == null) {
585             // TODO: Fix the call-points that cause this to happen.
586             return;
587         }
588 
589         positionChildAt(POSITION_BOTTOM, child, includingParents);
590 
591         if (getDisplayContent().mAppTransition.isTransitionSet()) {
592             child.setSendingToBottom(true);
593         }
594         getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
595     }
596 
597     @Override
positionChildAt(int position, Task child, boolean includingParents)598     void positionChildAt(int position, Task child, boolean includingParents) {
599         positionChildAt(position, child, includingParents, child.showForAllUsers());
600     }
601 
602     /**
603      * Overridden version of {@link TaskStack#positionChildAt(int, Task, boolean)}. Used in
604      * {@link TaskStack#addTask(Task, int, boolean showForAllUsers, boolean)}, as it can receive
605      * showForAllUsers param from {@link AppWindowToken} instead of {@link Task#showForAllUsers()}.
606      */
positionChildAt(int position, Task child, boolean includingParents, boolean showForAllUsers)607     private void positionChildAt(int position, Task child, boolean includingParents,
608             boolean showForAllUsers) {
609         final int targetPosition = findPositionForTask(child, position, showForAllUsers,
610                 false /* addingNew */);
611         super.positionChildAt(targetPosition, child, includingParents);
612 
613         // Log positioning.
614         if (DEBUG_TASK_MOVEMENT)
615             Slog.d(TAG_WM, "positionTask: task=" + this + " position=" + position);
616 
617         final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0;
618         EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition);
619     }
620 
reparent(int displayId, Rect outStackBounds, boolean onTop)621     void reparent(int displayId, Rect outStackBounds, boolean onTop) {
622         final DisplayContent targetDc = mWmService.mRoot.getDisplayContent(displayId);
623         if (targetDc == null) {
624             throw new IllegalArgumentException("Trying to move stackId=" + mStackId
625                     + " to unknown displayId=" + displayId);
626         }
627 
628         targetDc.moveStackToDisplay(this, onTop);
629         if (matchParentBounds()) {
630             outStackBounds.setEmpty();
631         } else {
632             getRawBounds(outStackBounds);
633         }
634     }
635 
636     // TODO: We should really have users as a window container in the hierarchy so that we don't
637     // have to do complicated things like we are doing in this method.
findPositionForTask(Task task, int targetPosition, boolean showForAllUsers, boolean addingNew)638     private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
639             boolean addingNew) {
640         final boolean canShowTask =
641                 showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
642 
643         final int stackSize = mChildren.size();
644         int minPosition = 0;
645         int maxPosition = addingNew ? stackSize : stackSize - 1;
646 
647         if (canShowTask) {
648             minPosition = computeMinPosition(minPosition, stackSize);
649         } else {
650             maxPosition = computeMaxPosition(maxPosition);
651         }
652 
653         // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
654         if (targetPosition == POSITION_BOTTOM && minPosition == 0) {
655             return POSITION_BOTTOM;
656         } else if (targetPosition == POSITION_TOP
657                 && maxPosition == (addingNew ? stackSize : stackSize - 1)) {
658             return POSITION_TOP;
659         }
660         // Reset position based on minimum/maximum possible positions.
661         return Math.min(Math.max(targetPosition, minPosition), maxPosition);
662     }
663 
664     /** Calculate the minimum possible position for a task that can be shown to the user.
665      *  The minimum position will be above all other tasks that can't be shown.
666      *  @param minPosition The minimum position the caller is suggesting.
667      *                  We will start adjusting up from here.
668      *  @param size The size of the current task list.
669      */
computeMinPosition(int minPosition, int size)670     private int computeMinPosition(int minPosition, int size) {
671         while (minPosition < size) {
672             final Task tmpTask = mChildren.get(minPosition);
673             final boolean canShowTmpTask =
674                     tmpTask.showForAllUsers()
675                             || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
676             if (canShowTmpTask) {
677                 break;
678             }
679             minPosition++;
680         }
681         return minPosition;
682     }
683 
684     /** Calculate the maximum possible position for a task that can't be shown to the user.
685      *  The maximum position will be below all other tasks that can be shown.
686      *  @param maxPosition The maximum position the caller is suggesting.
687      *                  We will start adjusting down from here.
688      */
computeMaxPosition(int maxPosition)689     private int computeMaxPosition(int maxPosition) {
690         while (maxPosition > 0) {
691             final Task tmpTask = mChildren.get(maxPosition);
692             final boolean canShowTmpTask =
693                     tmpTask.showForAllUsers()
694                             || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
695             if (!canShowTmpTask) {
696                 break;
697             }
698             maxPosition--;
699         }
700         return maxPosition;
701     }
702 
703     /**
704      * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
705      * back.
706      * @param task The Task to delete.
707      */
708     @Override
removeChild(Task task)709     void removeChild(Task task) {
710         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeChild: task=" + task);
711 
712         super.removeChild(task);
713         task.mStack = null;
714 
715         if (mDisplayContent != null) {
716             if (mChildren.isEmpty()) {
717                 getParent().positionChildAt(POSITION_BOTTOM, this, false /* includingParents */);
718             }
719             mDisplayContent.setLayoutNeeded();
720         }
721         for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
722             final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
723             if (wtoken.getTask() == task) {
724                 wtoken.mIsExiting = false;
725                 mExitingAppTokens.remove(appNdx);
726             }
727         }
728     }
729 
730     @Override
onConfigurationChanged(Configuration newParentConfig)731     public void onConfigurationChanged(Configuration newParentConfig) {
732         final int prevWindowingMode = getWindowingMode();
733         super.onConfigurationChanged(newParentConfig);
734 
735         // Only need to update surface size here since the super method will handle updating
736         // surface position.
737         updateSurfaceSize(getPendingTransaction());
738         final int windowingMode = getWindowingMode();
739         final boolean isAlwaysOnTop = isAlwaysOnTop();
740 
741         if (mDisplayContent == null) {
742             return;
743         }
744 
745         if (prevWindowingMode != windowingMode) {
746             mDisplayContent.onStackWindowingModeChanged(this);
747 
748             if (inSplitScreenSecondaryWindowingMode()) {
749                 // When the stack is resized due to entering split screen secondary, offset the
750                 // windows to compensate for the new stack position.
751                 forAllWindows(w -> {
752                     w.mWinAnimator.setOffsetPositionForStackResize(true);
753                 }, true);
754             }
755         }
756     }
757 
updateSurfaceBounds()758     private void updateSurfaceBounds() {
759         updateSurfaceSize(getPendingTransaction());
760         updateSurfacePosition();
761         scheduleAnimation();
762     }
763 
764     /**
765      * Calculate an amount by which to expand the stack bounds in each direction.
766      * Used to make room for shadows in the pinned windowing mode.
767      */
getStackOutset()768     int getStackOutset() {
769         DisplayContent displayContent = getDisplayContent();
770         if (inPinnedWindowingMode() && displayContent != null) {
771             final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
772 
773             // We multiply by two to match the client logic for converting view elevation
774             // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
775             return (int)Math.ceil(mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
776                     displayMetrics) * 2);
777         }
778         return 0;
779     }
780 
781     @Override
getRelativeDisplayedPosition(Point outPos)782     void getRelativeDisplayedPosition(Point outPos) {
783         super.getRelativeDisplayedPosition(outPos);
784         final int outset = getStackOutset();
785         outPos.x -= outset;
786         outPos.y -= outset;
787     }
788 
updateSurfaceSize(SurfaceControl.Transaction transaction)789     private void updateSurfaceSize(SurfaceControl.Transaction transaction) {
790         if (mSurfaceControl == null) {
791             return;
792         }
793 
794         final Rect stackBounds = getDisplayedBounds();
795         int width = stackBounds.width();
796         int height = stackBounds.height();
797 
798         final int outset = getStackOutset();
799         width += 2*outset;
800         height += 2*outset;
801 
802         if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
803             return;
804         }
805         if (getWindowConfiguration().tasksAreFloating()) {
806             // Don't crop freeform windows to the stack.
807             transaction.setWindowCrop(mSurfaceControl, -1, -1);
808         } else {
809             transaction.setWindowCrop(mSurfaceControl, width, height);
810         }
811         mLastSurfaceSize.set(width, height);
812     }
813 
814     @VisibleForTesting
getLastSurfaceSize()815     Point getLastSurfaceSize() {
816         return mLastSurfaceSize;
817     }
818 
819     @Override
onDisplayChanged(DisplayContent dc)820     void onDisplayChanged(DisplayContent dc) {
821         if (mDisplayContent != null && mDisplayContent != dc) {
822             throw new IllegalStateException("onDisplayChanged: Already attached");
823         }
824 
825         super.onDisplayChanged(dc);
826 
827         updateSurfaceBounds();
828         if (mAnimationBackgroundSurface == null) {
829             mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer()
830                     .setName("animation background stackId=" + mStackId)
831                     .build();
832         }
833     }
834 
835     /**
836      * Determines the stack and task bounds of the other stack when in docked mode. The current task
837      * bounds is passed in but depending on the stack, the task and stack must match. Only in
838      * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds
839      * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds
840      * is calculated and is also used for its task bounds.
841      * If any of the out bounds are empty, it represents default bounds
842      *
843      * @param currentTempTaskBounds the current task bounds of the other stack
844      * @param outStackBounds the calculated stack bounds of the other stack
845      * @param outTempTaskBounds the calculated task bounds of the other stack
846      */
getStackDockedModeBoundsLocked(Configuration parentConfig, Rect dockedBounds, Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds)847     void getStackDockedModeBoundsLocked(Configuration parentConfig, Rect dockedBounds,
848             Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) {
849         outTempTaskBounds.setEmpty();
850 
851         if (dockedBounds == null || dockedBounds.isEmpty()) {
852             // Calculate the primary docked bounds.
853             final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
854                     == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
855             getStackDockedModeBounds(parentConfig,
856                     true /* primary */, outStackBounds, dockedBounds,
857                     mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
858             return;
859         }
860         final int dockedSide = getDockSide(parentConfig, dockedBounds);
861 
862         // When the home stack is resizable, should always have the same stack and task bounds
863         if (isActivityTypeHome()) {
864             final Task homeTask = findHomeTask();
865             if (homeTask != null && homeTask.isResizeable()) {
866                 // Calculate the home stack bounds when in docked mode and the home stack is
867                 // resizeable.
868                 getDisplayContent().mDividerControllerLocked
869                         .getHomeStackBoundsInDockedMode(parentConfig,
870                                 dockedSide, outStackBounds);
871             } else {
872                 // Home stack isn't resizeable, so don't specify stack bounds.
873                 outStackBounds.setEmpty();
874             }
875 
876             outTempTaskBounds.set(outStackBounds);
877             return;
878         }
879 
880         // When minimized state, the stack bounds for all non-home and docked stack bounds should
881         // match the passed task bounds
882         if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) {
883             outStackBounds.set(currentTempTaskBounds);
884             return;
885         }
886 
887         if (dockedSide == DOCKED_INVALID) {
888             // Not sure how you got here...Only thing we can do is return current bounds.
889             Slog.e(TAG_WM, "Failed to get valid docked side for docked stack");
890             outStackBounds.set(getRawBounds());
891             return;
892         }
893 
894         final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
895         getStackDockedModeBounds(parentConfig,
896                 false /* primary */, outStackBounds, dockedBounds,
897                 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
898     }
899 
900     /**
901      * Outputs the bounds a stack should be given the presence of a docked stack on the display.
902      * @param parentConfig The parent configuration.
903      * @param primary {@code true} if getting the primary stack bounds.
904      * @param outBounds Output bounds that should be used for the stack.
905      * @param dockedBounds Bounds of the docked stack.
906      * @param dockDividerWidth We need to know the width of the divider make to the output bounds
907      *                         close to the side of the dock.
908      * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
909      */
getStackDockedModeBounds(Configuration parentConfig, boolean primary, Rect outBounds, Rect dockedBounds, int dockDividerWidth, boolean dockOnTopOrLeft)910     private void getStackDockedModeBounds(Configuration parentConfig, boolean primary,
911             Rect outBounds, Rect dockedBounds, int dockDividerWidth,
912             boolean dockOnTopOrLeft) {
913         final Rect displayRect = parentConfig.windowConfiguration.getBounds();
914         final boolean splitHorizontally = displayRect.width() > displayRect.height();
915 
916         outBounds.set(displayRect);
917         if (primary) {
918             if (mWmService.mDockedStackCreateBounds != null) {
919                 outBounds.set(mWmService.mDockedStackCreateBounds);
920                 return;
921             }
922 
923             // The initial bounds of the docked stack when it is created about half the screen space
924             // and its bounds can be adjusted after that. The bounds of all other stacks are
925             // adjusted to occupy whatever screen space the docked stack isn't occupying.
926             final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
927             mDisplayContent.getDisplayPolicy().getStableInsetsLw(
928                     parentConfig.windowConfiguration.getRotation(),
929                     displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
930             final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
931                     displayRect.width(),
932                     displayRect.height(),
933                     dockDividerWidth,
934                     parentConfig.orientation == ORIENTATION_PORTRAIT,
935                     mTmpRect2).getMiddleTarget().position;
936 
937             if (dockOnTopOrLeft) {
938                 if (splitHorizontally) {
939                     outBounds.right = position;
940                 } else {
941                     outBounds.bottom = position;
942                 }
943             } else {
944                 if (splitHorizontally) {
945                     outBounds.left = position + dockDividerWidth;
946                 } else {
947                     outBounds.top = position + dockDividerWidth;
948                 }
949             }
950             return;
951         }
952 
953         // Other stacks occupy whatever space is left by the docked stack.
954         if (!dockOnTopOrLeft) {
955             if (splitHorizontally) {
956                 outBounds.right = dockedBounds.left - dockDividerWidth;
957             } else {
958                 outBounds.bottom = dockedBounds.top - dockDividerWidth;
959             }
960         } else {
961             if (splitHorizontally) {
962                 outBounds.left = dockedBounds.right + dockDividerWidth;
963             } else {
964                 outBounds.top = dockedBounds.bottom + dockDividerWidth;
965             }
966         }
967         DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
968     }
969 
resetDockedStackToMiddle()970     void resetDockedStackToMiddle() {
971         if (inSplitScreenPrimaryWindowingMode()) {
972             throw new IllegalStateException("Not a docked stack=" + this);
973         }
974 
975         mWmService.mDockedStackCreateBounds = null;
976 
977         final Rect bounds = new Rect();
978         final Rect tempBounds = new Rect();
979         TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
980         Rect dockedBounds =
981                 (dockedStack == null || dockedStack == this) ? null : dockedStack.getRawBounds();
982         getStackDockedModeBoundsLocked(mDisplayContent.getConfiguration(), dockedBounds,
983                 null /* currentTempTaskBounds */, bounds, tempBounds);
984         mActivityStack.requestResize(bounds);
985     }
986 
987     @Override
removeIfPossible()988     void removeIfPossible() {
989         if (isSelfOrChildAnimating()) {
990             mDeferRemoval = true;
991             return;
992         }
993         removeImmediately();
994     }
995 
996     @Override
removeImmediately()997     void removeImmediately() {
998         if (mActivityStack != null) {
999             mActivityStack.unregisterConfigurationChangeListener(this);
1000         }
1001         super.removeImmediately();
1002     }
1003 
1004     @Override
onParentChanged()1005     void onParentChanged() {
1006         super.onParentChanged();
1007 
1008         if (getParent() != null || mDisplayContent == null) {
1009             return;
1010         }
1011 
1012         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
1013 
1014         if (mAnimationBackgroundSurface != null) {
1015             mWmService.mTransactionFactory.make().remove(mAnimationBackgroundSurface).apply();
1016             mAnimationBackgroundSurface = null;
1017         }
1018 
1019         mDisplayContent = null;
1020         mWmService.mWindowPlacerLocked.requestTraversal();
1021     }
1022 
resetAnimationBackgroundAnimator()1023     void resetAnimationBackgroundAnimator() {
1024         mAnimationBackgroundAnimator = null;
1025         hideAnimationSurface();
1026     }
1027 
setAnimationBackground(WindowStateAnimator winAnimator, int color)1028     void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
1029         if (mAnimationBackgroundAnimator == null) {
1030             mAnimationBackgroundAnimator = winAnimator;
1031             showAnimationSurface(((color >> 24) & 0xff) / 255f);
1032         }
1033     }
1034 
1035     // TODO: Should each user have there own stacks?
1036     @Override
switchUser()1037     void switchUser() {
1038         super.switchUser();
1039         int top = mChildren.size();
1040         for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
1041             Task task = mChildren.get(taskNdx);
1042             if (mWmService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
1043                 mChildren.remove(taskNdx);
1044                 mChildren.add(task);
1045                 --top;
1046             }
1047         }
1048     }
1049 
1050     /**
1051      * Adjusts the stack bounds if the IME is visible.
1052      *
1053      * @param imeWin The IME window.
1054      * @param keepLastAmount Use {@code true} to keep the last adjusted amount from
1055      *                       {@link DockedStackDividerController} for adjusting the stack bounds,
1056      *                       Use {@code false} to reset adjusted amount as 0.
1057      * @see #updateAdjustForIme(float, float, boolean)
1058      */
setAdjustedForIme(WindowState imeWin, boolean keepLastAmount)1059     void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) {
1060         mImeWin = imeWin;
1061         mImeGoingAway = false;
1062         if (!mAdjustedForIme || keepLastAmount) {
1063             mAdjustedForIme = true;
1064             DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked;
1065             final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f;
1066             final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f;
1067             updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */);
1068         }
1069     }
1070 
isAdjustedForIme()1071     boolean isAdjustedForIme() {
1072         return mAdjustedForIme;
1073     }
1074 
isAnimatingForIme()1075     boolean isAnimatingForIme() {
1076         return mImeWin != null && mImeWin.isAnimatingLw();
1077     }
1078 
1079     /**
1080      * Update the stack's bounds (crop or position) according to the IME window's
1081      * current position. When IME window is animated, the bottom stack is animated
1082      * together to track the IME window's current position, and the top stack is
1083      * cropped as necessary.
1084      *
1085      * @return true if a traversal should be performed after the adjustment.
1086      */
updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force)1087     boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) {
1088         if (adjustAmount != mAdjustImeAmount
1089                 || adjustDividerAmount != mAdjustDividerAmount || force) {
1090             mAdjustImeAmount = adjustAmount;
1091             mAdjustDividerAmount = adjustDividerAmount;
1092             updateAdjustedBounds();
1093             return isVisible();
1094         } else {
1095             return false;
1096         }
1097     }
1098 
1099     /**
1100      * Resets the adjustment after it got adjusted for the IME.
1101      * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
1102      *                        animations; otherwise, set flag and animates the window away together
1103      *                        with IME window.
1104      */
resetAdjustedForIme(boolean adjustBoundsNow)1105     void resetAdjustedForIme(boolean adjustBoundsNow) {
1106         if (adjustBoundsNow) {
1107             mImeWin = null;
1108             mImeGoingAway = false;
1109             mAdjustImeAmount = 0f;
1110             mAdjustDividerAmount = 0f;
1111             if (!mAdjustedForIme) {
1112                 return;
1113             }
1114             mAdjustedForIme = false;
1115             updateAdjustedBounds();
1116             mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
1117         } else {
1118             mImeGoingAway |= mAdjustedForIme;
1119         }
1120     }
1121 
1122     /**
1123      * Sets the amount how much we currently minimize our stack.
1124      *
1125      * @param minimizeAmount The amount, between 0 and 1.
1126      * @return Whether the amount has changed and a layout is needed.
1127      */
setAdjustedForMinimizedDock(float minimizeAmount)1128     boolean setAdjustedForMinimizedDock(float minimizeAmount) {
1129         if (minimizeAmount != mMinimizeAmount) {
1130             mMinimizeAmount = minimizeAmount;
1131             updateAdjustedBounds();
1132             return isVisible();
1133         } else {
1134             return false;
1135         }
1136     }
1137 
shouldIgnoreInput()1138     boolean shouldIgnoreInput() {
1139         return isAdjustedForMinimizedDockedStack() ||
1140                 (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
1141     }
1142 
1143     /**
1144      * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
1145      * to the list of to be drawn windows the service is waiting for.
1146      */
beginImeAdjustAnimation()1147     void beginImeAdjustAnimation() {
1148         for (int j = mChildren.size() - 1; j >= 0; j--) {
1149             final Task task = mChildren.get(j);
1150             if (task.hasContentToDisplay()) {
1151                 task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
1152                 task.setWaitingForDrawnIfResizingChanged();
1153             }
1154         }
1155     }
1156 
1157     /**
1158      * Resets the resizing state of all windows.
1159      */
endImeAdjustAnimation()1160     void endImeAdjustAnimation() {
1161         for (int j = mChildren.size() - 1; j >= 0; j--) {
1162             mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
1163         }
1164     }
1165 
getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom)1166     int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
1167         return displayContentRect.top + (int)
1168                 ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
1169     }
1170 
adjustForIME(final WindowState imeWin)1171     private boolean adjustForIME(final WindowState imeWin) {
1172         // To prevent task stack resize animation may flicking when playing app transition
1173         // animation & IME window enter animation in parallel, we need to make sure app
1174         // transition is done and then adjust task size for IME, skip the new adjusted frame when
1175         // app transition is still running.
1176         if (getDisplayContent().mAppTransition.isRunning()) {
1177             return false;
1178         }
1179 
1180         final int dockedSide = getDockSide();
1181         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
1182         if (imeWin == null || !dockedTopOrBottom) {
1183             return false;
1184         }
1185 
1186         final Rect displayStableRect = mTmpRect;
1187         final Rect contentBounds = mTmpRect2;
1188 
1189         // Calculate the content bounds excluding the area occupied by IME
1190         getDisplayContent().getStableRect(displayStableRect);
1191         contentBounds.set(displayStableRect);
1192         int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
1193 
1194         imeTop += imeWin.getGivenContentInsetsLw().top;
1195         if (contentBounds.bottom > imeTop) {
1196             contentBounds.bottom = imeTop;
1197         }
1198 
1199         final int yOffset = displayStableRect.bottom - contentBounds.bottom;
1200 
1201         final int dividerWidth =
1202                 getDisplayContent().mDividerControllerLocked.getContentWidth();
1203         final int dividerWidthInactive =
1204                 getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
1205 
1206         if (dockedSide == DOCKED_TOP) {
1207             // If this stack is docked on top, we make it smaller so the bottom stack is not
1208             // occluded by IME. We shift its bottom up by the height of the IME, but
1209             // leaves at least 30% of the top stack visible.
1210             final int minTopStackBottom =
1211                     getMinTopStackBottom(displayStableRect, getRawBounds().bottom);
1212             final int bottom = Math.max(
1213                     getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive,
1214                     minTopStackBottom);
1215             mTmpAdjustedBounds.set(getRawBounds());
1216             mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount)
1217                     * getRawBounds().bottom);
1218             mFullyAdjustedImeBounds.set(getRawBounds());
1219         } else {
1220             // When the stack is on bottom and has no focus, it's only adjusted for divider width.
1221             final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
1222 
1223             // When the stack is on bottom and has focus, it needs to be moved up so as to
1224             // not occluded by IME, and at the same time adjusted for divider width.
1225             // We try to move it up by the height of the IME window, but only to the extent
1226             // that leaves at least 30% of the top stack visible.
1227             // 'top' is where the top of bottom stack will move to in this case.
1228             final int topBeforeImeAdjust =
1229                     getRawBounds().top - dividerWidth + dividerWidthInactive;
1230             final int minTopStackBottom =
1231                     getMinTopStackBottom(displayStableRect,
1232                             getRawBounds().top - dividerWidth);
1233             final int top = Math.max(
1234                     getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive);
1235 
1236             mTmpAdjustedBounds.set(getRawBounds());
1237             // Account for the adjustment for IME and divider width separately.
1238             // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
1239             // and dividerWidthDelta is due to divider width change only.
1240             mTmpAdjustedBounds.top = getRawBounds().top +
1241                     (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) +
1242                             mAdjustDividerAmount * dividerWidthDelta);
1243             mFullyAdjustedImeBounds.set(getRawBounds());
1244             mFullyAdjustedImeBounds.top = top;
1245             mFullyAdjustedImeBounds.bottom = top + getRawBounds().height();
1246         }
1247         return true;
1248     }
1249 
adjustForMinimizedDockedStack(float minimizeAmount)1250     private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
1251         final int dockSide = getDockSide();
1252         if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
1253             return false;
1254         }
1255 
1256         if (dockSide == DOCKED_TOP) {
1257             mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
1258             int topInset = mTmpRect.top;
1259             mTmpAdjustedBounds.set(getRawBounds());
1260             mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
1261                     * getRawBounds().bottom);
1262         } else if (dockSide == DOCKED_LEFT) {
1263             mTmpAdjustedBounds.set(getRawBounds());
1264             final int width = getRawBounds().width();
1265             mTmpAdjustedBounds.right =
1266                     (int) (minimizeAmount * mDockedStackMinimizeThickness
1267                             + (1 - minimizeAmount) * getRawBounds().right);
1268             mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
1269         } else if (dockSide == DOCKED_RIGHT) {
1270             mTmpAdjustedBounds.set(getRawBounds());
1271             mTmpAdjustedBounds.left = (int) (minimizeAmount *
1272                     (getRawBounds().right - mDockedStackMinimizeThickness)
1273                             + (1 - minimizeAmount) * getRawBounds().left);
1274         }
1275         return true;
1276     }
1277 
isMinimizedDockAndHomeStackResizable()1278     private boolean isMinimizedDockAndHomeStackResizable() {
1279         return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
1280                 && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
1281     }
1282 
1283     /**
1284      * @return the distance in pixels how much the stack gets minimized from it's original size
1285      */
getMinimizeDistance()1286     int getMinimizeDistance() {
1287         final int dockSide = getDockSide();
1288         if (dockSide == DOCKED_INVALID) {
1289             return 0;
1290         }
1291 
1292         if (dockSide == DOCKED_TOP) {
1293             mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
1294             int topInset = mTmpRect.top;
1295             return getRawBounds().bottom - topInset;
1296         } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
1297             return getRawBounds().width() - mDockedStackMinimizeThickness;
1298         } else {
1299             return 0;
1300         }
1301     }
1302 
1303     /**
1304      * Updates the adjustment depending on it's current state.
1305      */
updateAdjustedBounds()1306     private void updateAdjustedBounds() {
1307         boolean adjust = false;
1308         if (mMinimizeAmount != 0f) {
1309             adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
1310         } else if (mAdjustedForIme) {
1311             adjust = adjustForIME(mImeWin);
1312         }
1313         if (!adjust) {
1314             mTmpAdjustedBounds.setEmpty();
1315         }
1316         setAdjustedBounds(mTmpAdjustedBounds);
1317 
1318         final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
1319         if (mAdjustedForIme && adjust && !isImeTarget) {
1320             final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
1321                     * IME_ADJUST_DIM_AMOUNT;
1322             mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
1323         }
1324     }
1325 
applyAdjustForImeIfNeeded(Task task)1326     void applyAdjustForImeIfNeeded(Task task) {
1327         if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) {
1328             return;
1329         }
1330 
1331         final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds;
1332         task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
1333         mDisplayContent.setLayoutNeeded();
1334     }
1335 
1336 
isAdjustedForMinimizedDockedStack()1337     boolean isAdjustedForMinimizedDockedStack() {
1338         return mMinimizeAmount != 0f;
1339     }
1340 
1341     /**
1342      * @return {@code true} if we have a {@link Task} that is animating (currently only used for the
1343      *         recents animation); {@code false} otherwise.
1344      */
isTaskAnimating()1345     boolean isTaskAnimating() {
1346         for (int j = mChildren.size() - 1; j >= 0; j--) {
1347             final Task task = mChildren.get(j);
1348             if (task.isTaskAnimating()) {
1349                 return true;
1350             }
1351         }
1352         return false;
1353     }
1354 
1355     @CallSuper
1356     @Override
writeToProto(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)1357     public void writeToProto(ProtoOutputStream proto, long fieldId,
1358             @WindowTraceLogLevel int logLevel) {
1359         if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
1360             return;
1361         }
1362 
1363         final long token = proto.start(fieldId);
1364         super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
1365         proto.write(ID, mStackId);
1366         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
1367             mChildren.get(taskNdx).writeToProto(proto, TASKS, logLevel);
1368         }
1369         proto.write(FILLS_PARENT, matchParentBounds());
1370         getRawBounds().writeToProto(proto, BOUNDS);
1371         proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
1372         proto.write(DEFER_REMOVAL, mDeferRemoval);
1373         proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
1374         proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
1375         proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
1376         proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
1377         mAdjustedBounds.writeToProto(proto, ADJUSTED_BOUNDS);
1378         proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
1379         proto.end(token);
1380     }
1381 
1382     @Override
dump(PrintWriter pw, String prefix, boolean dumpAll)1383      void dump(PrintWriter pw, String prefix, boolean dumpAll) {
1384         pw.println(prefix + "mStackId=" + mStackId);
1385         pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
1386         pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
1387         if (mMinimizeAmount != 0f) {
1388             pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
1389         }
1390         if (mAdjustedForIme) {
1391             pw.println(prefix + "mAdjustedForIme=true");
1392             pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
1393             pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
1394         }
1395         if (!mAdjustedBounds.isEmpty()) {
1396             pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
1397         }
1398         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
1399             mChildren.get(taskNdx).dump(pw, prefix + "  ", dumpAll);
1400         }
1401         if (mAnimationBackgroundSurfaceIsShown) {
1402             pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
1403         }
1404         if (!mExitingAppTokens.isEmpty()) {
1405             pw.println();
1406             pw.println("  Exiting application tokens:");
1407             for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
1408                 WindowToken token = mExitingAppTokens.get(i);
1409                 pw.print("  Exiting App #"); pw.print(i);
1410                 pw.print(' '); pw.print(token);
1411                 pw.println(':');
1412                 token.dump(pw, "    ", dumpAll);
1413             }
1414         }
1415         mAnimatingAppWindowTokenRegistry.dump(pw, "AnimatingApps:", prefix);
1416     }
1417 
1418     @Override
fillsParent()1419     boolean fillsParent() {
1420         return matchParentBounds();
1421     }
1422 
1423     @Override
toString()1424     public String toString() {
1425         return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
1426     }
1427 
getName()1428     String getName() {
1429         return toShortString();
1430     }
1431 
toShortString()1432     public String toShortString() {
1433         return "Stack=" + mStackId;
1434     }
1435 
1436     /**
1437      * For docked workspace (or workspace that's side-by-side to the docked), provides
1438      * information which side of the screen was the dock anchored.
1439      */
getDockSide()1440     int getDockSide() {
1441         return getDockSide(mDisplayContent.getConfiguration(), getRawBounds());
1442     }
1443 
getDockSideForDisplay(DisplayContent dc)1444     int getDockSideForDisplay(DisplayContent dc) {
1445         return getDockSide(dc, dc.getConfiguration(), getRawBounds());
1446     }
1447 
getDockSide(Configuration parentConfig, Rect bounds)1448     int getDockSide(Configuration parentConfig, Rect bounds) {
1449         if (mDisplayContent == null) {
1450             return DOCKED_INVALID;
1451         }
1452         return getDockSide(mDisplayContent, parentConfig, bounds);
1453     }
1454 
getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds)1455     private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) {
1456         return dc.getDockedDividerController().getDockSide(bounds,
1457                 parentConfig.windowConfiguration.getBounds(),
1458                 parentConfig.orientation, parentConfig.windowConfiguration.getRotation());
1459     }
1460 
hasTaskForUser(int userId)1461     boolean hasTaskForUser(int userId) {
1462         for (int i = mChildren.size() - 1; i >= 0; i--) {
1463             final Task task = mChildren.get(i);
1464             if (task.mUserId == userId) {
1465                 return true;
1466             }
1467         }
1468         return false;
1469     }
1470 
findTaskForResizePoint(int x, int y, int delta, DisplayContent.TaskForResizePointSearchResult results)1471     void findTaskForResizePoint(int x, int y, int delta,
1472             DisplayContent.TaskForResizePointSearchResult results) {
1473         if (!getWindowConfiguration().canResizeTask()) {
1474             results.searchDone = true;
1475             return;
1476         }
1477 
1478         for (int i = mChildren.size() - 1; i >= 0; --i) {
1479             final Task task = mChildren.get(i);
1480             if (task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
1481                 results.searchDone = true;
1482                 return;
1483             }
1484 
1485             // We need to use the task's dim bounds (which is derived from the visible bounds of
1486             // its apps windows) for any touch-related tests. Can't use the task's original
1487             // bounds because it might be adjusted to fit the content frame. One example is when
1488             // the task is put to top-left quadrant, the actual visible area would not start at
1489             // (0,0) after it's adjusted for the status bar.
1490             task.getDimBounds(mTmpRect);
1491             mTmpRect.inset(-delta, -delta);
1492             if (mTmpRect.contains(x, y)) {
1493                 mTmpRect.inset(delta, delta);
1494 
1495                 results.searchDone = true;
1496 
1497                 if (!mTmpRect.contains(x, y)) {
1498                     results.taskForResize = task;
1499                     return;
1500                 }
1501                 // User touched inside the task. No need to look further,
1502                 // focus transfer will be handled in ACTION_UP.
1503                 return;
1504             }
1505         }
1506     }
1507 
setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion, Rect contentRect, Rect postExclude)1508     void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion,
1509             Rect contentRect, Rect postExclude) {
1510         for (int i = mChildren.size() - 1; i >= 0; --i) {
1511             final Task task = mChildren.get(i);
1512             AppWindowToken token = task.getTopVisibleAppToken();
1513             if (token == null || !token.hasContentToDisplay()) {
1514                 continue;
1515             }
1516 
1517             /**
1518              * Exclusion region is the region that TapDetector doesn't care about.
1519              * Here we want to remove all non-focused tasks from the exclusion region.
1520              * We also remove the outside touch area for resizing for all freeform
1521              * tasks (including the focused).
1522              *
1523              * We save the focused task region once we find it, and add it back at the end.
1524              *
1525              * If the task is home stack and it is resizable in the minimized state, we want to
1526              * exclude the docked stack from touch so we need the entire screen area and not just a
1527              * small portion which the home stack currently is resized to.
1528              */
1529 
1530             if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) {
1531                 mDisplayContent.getBounds(mTmpRect);
1532             } else {
1533                 task.getDimBounds(mTmpRect);
1534             }
1535 
1536             if (task == focusedTask) {
1537                 // Add the focused task rect back into the exclude region once we are done
1538                 // processing stacks.
1539                 postExclude.set(mTmpRect);
1540             }
1541 
1542             final boolean isFreeformed = task.inFreeformWindowingMode();
1543             if (task != focusedTask || isFreeformed) {
1544                 if (isFreeformed) {
1545                     // If the task is freeformed, enlarge the area to account for outside
1546                     // touch area for resize.
1547                     mTmpRect.inset(-delta, -delta);
1548                     // Intersect with display content rect. If we have system decor (status bar/
1549                     // navigation bar), we want to exclude that from the tap detection.
1550                     // Otherwise, if the app is partially placed under some system button (eg.
1551                     // Recents, Home), pressing that button would cause a full series of
1552                     // unwanted transfer focus/resume/pause, before we could go home.
1553                     mTmpRect.intersect(contentRect);
1554                 }
1555                 touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
1556             }
1557         }
1558     }
1559 
setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds)1560     public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
1561         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
1562         synchronized (mWmService.mGlobalLock) {
1563             if (mCancelCurrentBoundsAnimation) {
1564                 return false;
1565             }
1566         }
1567 
1568         try {
1569             mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
1570         } catch (RemoteException e) {
1571             // I don't believe you.
1572         }
1573         return true;
1574     }
1575 
onAllWindowsDrawn()1576     void onAllWindowsDrawn() {
1577         if (!mBoundsAnimating && !mBoundsAnimatingRequested) {
1578             return;
1579         }
1580 
1581         getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn();
1582     }
1583 
1584     @Override  // AnimatesBounds
onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType)1585     public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
1586             @BoundsAnimationController.AnimationType int animationType) {
1587         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
1588         synchronized (mWmService.mGlobalLock) {
1589             if (!isAttached()) {
1590                 // Don't run the animation if the stack is already detached
1591                 return false;
1592             }
1593 
1594             mBoundsAnimatingRequested = false;
1595             mBoundsAnimating = true;
1596             mAnimationType = animationType;
1597 
1598             // If we are changing UI mode, as in the PiP to fullscreen
1599             // transition, then we need to wait for the window to draw.
1600             if (schedulePipModeChangedCallback) {
1601                 forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); },
1602                         false /* traverseTopToBottom */);
1603             }
1604         }
1605 
1606         if (inPinnedWindowingMode()) {
1607             try {
1608                 mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
1609             } catch (RemoteException e) {
1610                 // I don't believe you...
1611             }
1612 
1613             if ((schedulePipModeChangedCallback || animationType == FADE_IN)
1614                     && mActivityStack != null) {
1615                 // We need to schedule the PiP mode change before the animation up. It is possible
1616                 // in this case for the animation down to not have been completed, so always
1617                 // force-schedule and update to the client to ensure that it is notified that it
1618                 // is no longer in picture-in-picture mode
1619                 mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(null,
1620                         forceUpdate);
1621             }
1622         }
1623         return true;
1624     }
1625 
1626     @Override  // AnimatesBounds
onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, boolean moveToFullscreen)1627     public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
1628             boolean moveToFullscreen) {
1629         if (inPinnedWindowingMode()) {
1630             // Update to the final bounds if requested. This is done here instead of in the bounds
1631             // animator to allow us to coordinate this after we notify the PiP mode changed
1632 
1633             if (schedulePipModeChangedCallback) {
1634                 // We need to schedule the PiP mode change after the animation down, so use the
1635                 // final bounds
1636                 mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(
1637                         mBoundsAnimationTarget, false /* forceUpdate */);
1638             }
1639 
1640             if (mAnimationType == BoundsAnimationController.FADE_IN) {
1641                 setPinnedStackAlpha(1f);
1642                 mActivityStack.mService.notifyPinnedStackAnimationEnded();
1643                 return;
1644             }
1645 
1646             if (finalStackSize != null && !mCancelCurrentBoundsAnimation) {
1647                 setPinnedStackSize(finalStackSize, null);
1648             } else {
1649                 // We have been canceled, so the final stack size is null, still run the
1650                 // animation-end logic
1651                 onPipAnimationEndResize();
1652             }
1653 
1654             mActivityStack.mService.notifyPinnedStackAnimationEnded();
1655             if (moveToFullscreen) {
1656                 mActivityStack.mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
1657             }
1658         } else {
1659             // No PiP animation, just run the normal animation-end logic
1660             onPipAnimationEndResize();
1661         }
1662     }
1663 
1664     /**
1665      * @return the current stack bounds transformed to the given {@param aspectRatio}. If
1666      *         the default bounds is {@code null}, then the {@param aspectRatio} is applied to the
1667      *         default bounds.
1668      */
getPictureInPictureBounds(float aspectRatio, Rect stackBounds)1669     Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
1670         if (!mWmService.mSupportsPictureInPicture) {
1671             return null;
1672         }
1673 
1674         final DisplayContent displayContent = getDisplayContent();
1675         if (displayContent == null) {
1676             return null;
1677         }
1678 
1679         if (!inPinnedWindowingMode()) {
1680             return null;
1681         }
1682 
1683         final PinnedStackController pinnedStackController =
1684                 displayContent.getPinnedStackController();
1685         if (stackBounds == null) {
1686             // Calculate the aspect ratio bounds from the default bounds
1687             stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
1688         }
1689 
1690         if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
1691             return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
1692                     true /* useCurrentMinEdgeSize */);
1693         } else {
1694             return stackBounds;
1695         }
1696     }
1697 
1698     /**
1699      * Animates the pinned stack.
1700      */
animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration, boolean fromFullscreen)1701     void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
1702             int animationDuration, boolean fromFullscreen) {
1703         if (!inPinnedWindowingMode()) {
1704             return;
1705         }
1706         // Get the from-bounds
1707         final Rect fromBounds = new Rect();
1708         getBounds(fromBounds);
1709 
1710         // Get non-null fullscreen to-bounds for animating if the bounds are null
1711         @SchedulePipModeChangedState int schedulePipModeChangedState =
1712                 NO_PIP_MODE_CHANGED_CALLBACKS;
1713         final boolean toFullscreen = toBounds == null;
1714         if (toFullscreen) {
1715             if (fromFullscreen) {
1716                 throw new IllegalArgumentException("Should not defer scheduling PiP mode"
1717                         + " change on animation to fullscreen.");
1718             }
1719             schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
1720 
1721             mWmService.getStackBounds(
1722                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
1723             if (!mTmpToBounds.isEmpty()) {
1724                 // If there is a fullscreen bounds, use that
1725                 toBounds = new Rect(mTmpToBounds);
1726             } else {
1727                 // Otherwise, use the display bounds
1728                 toBounds = new Rect();
1729                 getDisplayContent().getBounds(toBounds);
1730             }
1731         } else if (fromFullscreen) {
1732             schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
1733         }
1734 
1735         setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
1736 
1737         final Rect finalToBounds = toBounds;
1738         final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
1739                 schedulePipModeChangedState;
1740         final DisplayContent displayContent = getDisplayContent();
1741         @BoundsAnimationController.AnimationType int intendedAnimationType =
1742                 displayContent.mBoundsAnimationController.getAnimationType();
1743         if (intendedAnimationType == FADE_IN) {
1744             if (fromFullscreen) {
1745                 setPinnedStackAlpha(0f);
1746             }
1747             if (toBounds.width() == fromBounds.width()
1748                     && toBounds.height() == fromBounds.height()) {
1749                 intendedAnimationType = BoundsAnimationController.BOUNDS;
1750             } else if (!fromFullscreen && !toBounds.equals(fromBounds)) {
1751                 // intendedAnimationType may have been reset at the end of RecentsAnimation,
1752                 // force it to BOUNDS type if we know for certain we're animating to
1753                 // a different bounds, especially for expand and collapse of PiP window.
1754                 intendedAnimationType = BoundsAnimationController.BOUNDS;
1755             }
1756         }
1757 
1758         final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
1759         mCancelCurrentBoundsAnimation = false;
1760         displayContent.mBoundsAnimationController.getHandler().post(() -> {
1761             displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
1762                     finalToBounds, animationDuration, finalSchedulePipModeChangedState,
1763                     fromFullscreen, toFullscreen, animationType);
1764         });
1765     }
1766 
1767     /**
1768      * Sets the current picture-in-picture aspect ratio.
1769      */
setPictureInPictureAspectRatio(float aspectRatio)1770     void setPictureInPictureAspectRatio(float aspectRatio) {
1771         if (!mWmService.mSupportsPictureInPicture) {
1772             return;
1773         }
1774 
1775         if (!inPinnedWindowingMode()) {
1776             return;
1777         }
1778 
1779         final PinnedStackController pinnedStackController =
1780                 getDisplayContent().getPinnedStackController();
1781 
1782         if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
1783             return;
1784         }
1785         getAnimationOrCurrentBounds(mTmpFromBounds);
1786         mTmpToBounds.set(mTmpFromBounds);
1787         getPictureInPictureBounds(aspectRatio, mTmpToBounds);
1788         if (!mTmpToBounds.equals(mTmpFromBounds)) {
1789             animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
1790                     -1 /* duration */, false /* fromFullscreen */);
1791         }
1792         pinnedStackController.setAspectRatio(
1793                 pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
1794                         ? aspectRatio : -1f);
1795     }
1796 
1797     /**
1798      * Sets the current picture-in-picture actions.
1799      */
setPictureInPictureActions(List<RemoteAction> actions)1800     void setPictureInPictureActions(List<RemoteAction> actions) {
1801         if (!mWmService.mSupportsPictureInPicture) {
1802             return;
1803         }
1804 
1805         if (!inPinnedWindowingMode()) {
1806             return;
1807         }
1808 
1809         getDisplayContent().getPinnedStackController().setActions(actions);
1810     }
1811 
1812     @Override
isAttached()1813     public boolean isAttached() {
1814         synchronized (mWmService.mGlobalLock) {
1815             return mDisplayContent != null;
1816         }
1817     }
1818 
1819     /**
1820      * Called immediately prior to resizing the tasks at the end of the pinned stack animation.
1821      */
onPipAnimationEndResize()1822     public void onPipAnimationEndResize() {
1823         synchronized (mWmService.mGlobalLock) {
1824             mBoundsAnimating = false;
1825             for (int i = 0; i < mChildren.size(); i++) {
1826                 final Task t = mChildren.get(i);
1827                 t.clearPreserveNonFloatingState();
1828             }
1829             mWmService.requestTraversal();
1830         }
1831     }
1832 
1833     @Override
shouldDeferStartOnMoveToFullscreen()1834     public boolean shouldDeferStartOnMoveToFullscreen() {
1835         synchronized (mWmService.mGlobalLock) {
1836             if (!isAttached()) {
1837                 // Unnecessary to pause the animation because the stack is detached.
1838                 return false;
1839             }
1840 
1841             // Workaround for the recents animation -- normally we need to wait for the new activity
1842             // to show before starting the PiP animation, but because we start and show the home
1843             // activity early for the recents animation prior to the PiP animation starting, there
1844             // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
1845             // stack is already visible and drawn.
1846             final TaskStack homeStack = mDisplayContent.getHomeStack();
1847             if (homeStack == null) {
1848                 return true;
1849             }
1850             final Task homeTask = homeStack.getTopChild();
1851             if (homeTask == null) {
1852                 return true;
1853             }
1854             final AppWindowToken homeApp = homeTask.getTopVisibleAppToken();
1855             if (!homeTask.isVisible() || homeApp == null) {
1856                 return true;
1857             }
1858             return !homeApp.allDrawn;
1859         }
1860     }
1861 
1862     /**
1863      * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
1864      *         bounds and we have a deferred PiP mode changed callback set with the animation.
1865      */
deferScheduleMultiWindowModeChanged()1866     public boolean deferScheduleMultiWindowModeChanged() {
1867         if (inPinnedWindowingMode()) {
1868             return (mBoundsAnimatingRequested || mBoundsAnimating);
1869         }
1870         return false;
1871     }
1872 
isForceScaled()1873     public boolean isForceScaled() {
1874         return mBoundsAnimating;
1875     }
1876 
isAnimatingBounds()1877     public boolean isAnimatingBounds() {
1878         return mBoundsAnimating;
1879     }
1880 
lastAnimatingBoundsWasToFullscreen()1881     public boolean lastAnimatingBoundsWasToFullscreen() {
1882         return mBoundsAnimatingToFullscreen;
1883     }
1884 
isAnimatingBoundsToFullscreen()1885     public boolean isAnimatingBoundsToFullscreen() {
1886         return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen();
1887     }
1888 
pinnedStackResizeDisallowed()1889     public boolean pinnedStackResizeDisallowed() {
1890         if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
1891             return true;
1892         }
1893         return false;
1894     }
1895 
1896     /** Returns true if a removal action is still being deferred. */
checkCompleteDeferredRemoval()1897     boolean checkCompleteDeferredRemoval() {
1898         if (isSelfOrChildAnimating()) {
1899             return true;
1900         }
1901         if (mDeferRemoval) {
1902             removeImmediately();
1903         }
1904 
1905         return super.checkCompleteDeferredRemoval();
1906     }
1907 
1908     @Override
getOrientation()1909     int getOrientation() {
1910         return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
1911     }
1912 
canSpecifyOrientation()1913     private boolean canSpecifyOrientation() {
1914         final int windowingMode = getWindowingMode();
1915         final int activityType = getActivityType();
1916         return windowingMode == WINDOWING_MODE_FULLSCREEN
1917                 || activityType == ACTIVITY_TYPE_HOME
1918                 || activityType == ACTIVITY_TYPE_RECENTS
1919                 || activityType == ACTIVITY_TYPE_ASSISTANT;
1920     }
1921 
1922     @Override
getDimmer()1923     Dimmer getDimmer() {
1924         return mDimmer;
1925     }
1926 
1927     @Override
prepareSurfaces()1928     void prepareSurfaces() {
1929         mDimmer.resetDimStates();
1930         super.prepareSurfaces();
1931         getDimBounds(mTmpDimBoundsRect);
1932 
1933         // Bounds need to be relative, as the dim layer is a child.
1934         mTmpDimBoundsRect.offsetTo(0, 0);
1935         if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
1936             scheduleAnimation();
1937         }
1938     }
1939 
1940     @Override
setPinnedStackAlpha(float alpha)1941     public boolean setPinnedStackAlpha(float alpha) {
1942         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
1943         synchronized (mWmService.mGlobalLock) {
1944             final SurfaceControl sc = getSurfaceControl();
1945             if (sc == null || !sc.isValid()) {
1946                 // If the stack is already removed, don't bother updating any stack animation
1947                 return false;
1948             }
1949             getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
1950             scheduleAnimation();
1951             return !mCancelCurrentBoundsAnimation;
1952         }
1953     }
1954 
getDisplayInfo()1955     public DisplayInfo getDisplayInfo() {
1956         return mDisplayContent.getDisplayInfo();
1957     }
1958 
dim(float alpha)1959     void dim(float alpha) {
1960         mDimmer.dimAbove(getPendingTransaction(), alpha);
1961         scheduleAnimation();
1962     }
1963 
stopDimming()1964     void stopDimming() {
1965         mDimmer.stopDim(getPendingTransaction());
1966         scheduleAnimation();
1967     }
1968 
getAnimatingAppWindowTokenRegistry()1969     AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
1970         return mAnimatingAppWindowTokenRegistry;
1971     }
1972 }
1973