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.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
20 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
21 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
22 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
24 import static android.content.res.Configuration.EMPTY;
25 import static android.view.SurfaceControl.METADATA_TASK_ID;
26 
27 import static com.android.server.EventLogTags.WM_TASK_REMOVED;
28 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
29 import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
30 import static com.android.server.wm.TaskProto.BOUNDS;
31 import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
32 import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
33 import static com.android.server.wm.TaskProto.FILLS_PARENT;
34 import static com.android.server.wm.TaskProto.ID;
35 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
36 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
37 import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
38 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
39 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
40 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
41 
42 import android.annotation.CallSuper;
43 import android.app.ActivityManager;
44 import android.app.ActivityManager.TaskDescription;
45 import android.content.pm.ActivityInfo;
46 import android.content.res.Configuration;
47 import android.graphics.Rect;
48 import android.os.IBinder;
49 import android.util.EventLog;
50 import android.util.Slog;
51 import android.util.proto.ProtoOutputStream;
52 import android.view.Display;
53 import android.view.Surface;
54 import android.view.SurfaceControl;
55 
56 import com.android.internal.annotations.VisibleForTesting;
57 
58 import java.io.PrintWriter;
59 import java.util.function.Consumer;
60 
61 class Task extends WindowContainer<AppWindowToken> implements ConfigurationContainerListener{
62     static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
63 
64     // TODO: Track parent marks like this in WindowContainer.
65     TaskStack mStack;
66     final int mTaskId;
67     final int mUserId;
68     private boolean mDeferRemoval = false;
69 
70     final Rect mPreparedFrozenBounds = new Rect();
71     final Configuration mPreparedFrozenMergedConfig = new Configuration();
72 
73     // If non-empty, bounds used to display the task during animations/interactions.
74     private final Rect mOverrideDisplayedBounds = new Rect();
75 
76     /** ID of the display which rotation {@link #mRotation} has. */
77     private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
78     /**
79      * Display rotation as of the last time {@link #setBounds(Rect)} was called or this task was
80      * moved to a new display.
81      */
82     private int mRotation;
83 
84     // For comparison with DisplayContent bounds.
85     private Rect mTmpRect = new Rect();
86     // For handling display rotations.
87     private Rect mTmpRect2 = new Rect();
88     // For retrieving dim bounds
89     private Rect mTmpRect3 = new Rect();
90 
91     // Resize mode of the task. See {@link ActivityInfo#resizeMode}
92     private int mResizeMode;
93 
94     // Whether the task supports picture-in-picture.
95     // See {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE}
96     private boolean mSupportsPictureInPicture;
97 
98     // Whether the task is currently being drag-resized
99     private boolean mDragResizing;
100     private int mDragResizeMode;
101 
102     private TaskDescription mTaskDescription;
103 
104     // If set to true, the task will report that it is not in the floating
105     // state regardless of it's stack affiliation. As the floating state drives
106     // production of content insets this can be used to preserve them across
107     // stack moves and we in fact do so when moving from full screen to pinned.
108     private boolean mPreserveNonFloatingState = false;
109 
110     private Dimmer mDimmer = new Dimmer(this);
111     private final Rect mTmpDimBoundsRect = new Rect();
112 
113     /** @see #setCanAffectSystemUiFlags */
114     private boolean mCanAffectSystemUiFlags = true;
115 
116     // TODO: remove after unification
117     TaskRecord mTaskRecord;
118 
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription, TaskRecord taskRecord)119     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
120             boolean supportsPictureInPicture, TaskDescription taskDescription,
121             TaskRecord taskRecord) {
122         super(service);
123         mTaskId = taskId;
124         mStack = stack;
125         mUserId = userId;
126         mResizeMode = resizeMode;
127         mSupportsPictureInPicture = supportsPictureInPicture;
128         mTaskRecord = taskRecord;
129         if (mTaskRecord != null) {
130             // This can be null when we call createTaskInStack in WindowTestUtils. Remove this after
131             // unification.
132             mTaskRecord.registerConfigurationChangeListener(this);
133         }
134         setBounds(getRequestedOverrideBounds());
135         mTaskDescription = taskDescription;
136 
137         // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
138         setOrientation(SCREEN_ORIENTATION_UNSET);
139     }
140 
141     @Override
getDisplayContent()142     DisplayContent getDisplayContent() {
143         return mStack != null ? mStack.getDisplayContent() : null;
144     }
145 
getAdjustedAddPosition(int suggestedPosition)146     private int getAdjustedAddPosition(int suggestedPosition) {
147         final int size = mChildren.size();
148         if (suggestedPosition >= size) {
149             return Math.min(size, suggestedPosition);
150         }
151 
152         for (int pos = 0; pos < size && pos < suggestedPosition; ++pos) {
153             // TODO: Confirm that this is the behavior we want long term.
154             if (mChildren.get(pos).removed) {
155                 // suggestedPosition assumes removed tokens are actually gone.
156                 ++suggestedPosition;
157             }
158         }
159         return Math.min(size, suggestedPosition);
160     }
161 
162     @Override
addChild(AppWindowToken wtoken, int position)163     void addChild(AppWindowToken wtoken, int position) {
164         position = getAdjustedAddPosition(position);
165         super.addChild(wtoken, position);
166         mDeferRemoval = false;
167     }
168 
169     @Override
positionChildAt(int position, AppWindowToken child, boolean includingParents)170     void positionChildAt(int position, AppWindowToken child, boolean includingParents) {
171         position = getAdjustedAddPosition(position);
172         super.positionChildAt(position, child, includingParents);
173         mDeferRemoval = false;
174     }
175 
hasWindowsAlive()176     private boolean hasWindowsAlive() {
177         for (int i = mChildren.size() - 1; i >= 0; i--) {
178             if (mChildren.get(i).hasWindowsAlive()) {
179                 return true;
180             }
181         }
182         return false;
183     }
184 
185     @VisibleForTesting
shouldDeferRemoval()186     boolean shouldDeferRemoval() {
187         // TODO: This should probably return false if mChildren.isEmpty() regardless if the stack
188         // is animating...
189         return hasWindowsAlive() && mStack.isSelfOrChildAnimating();
190     }
191 
192     @Override
removeIfPossible()193     void removeIfPossible() {
194         if (shouldDeferRemoval()) {
195             if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
196             mDeferRemoval = true;
197             return;
198         }
199         removeImmediately();
200     }
201 
202     @Override
removeImmediately()203     void removeImmediately() {
204         if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
205         EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
206         mDeferRemoval = false;
207         if (mTaskRecord != null) {
208             mTaskRecord.unregisterConfigurationChangeListener(this);
209         }
210 
211         super.removeImmediately();
212     }
213 
reparent(TaskStack stack, int position, boolean moveParents)214     void reparent(TaskStack stack, int position, boolean moveParents) {
215         if (stack == mStack) {
216             throw new IllegalArgumentException(
217                     "task=" + this + " already child of stack=" + mStack);
218         }
219         if (stack == null) {
220             throw new IllegalArgumentException("reparent: could not find stack.");
221         }
222         if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
223                 + " from stack=" + mStack);
224         EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask");
225         final DisplayContent prevDisplayContent = getDisplayContent();
226 
227         // If we are moving from the fullscreen stack to the pinned stack
228         // then we want to preserve our insets so that there will not
229         // be a jump in the area covered by system decorations. We rely
230         // on the pinned animation to later unset this value.
231         if (stack.inPinnedWindowingMode()) {
232             mPreserveNonFloatingState = true;
233         } else {
234             mPreserveNonFloatingState = false;
235         }
236 
237         getParent().removeChild(this);
238         stack.addTask(this, position, showForAllUsers(), moveParents);
239 
240         // Relayout display(s).
241         final DisplayContent displayContent = stack.getDisplayContent();
242         displayContent.setLayoutNeeded();
243         if (prevDisplayContent != displayContent) {
244             onDisplayChanged(displayContent);
245             prevDisplayContent.setLayoutNeeded();
246         }
247         getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
248     }
249 
250     /** @see ActivityTaskManagerService#positionTaskInStack(int, int, int). */
positionAt(int position)251     void positionAt(int position) {
252         mStack.positionChildAt(position, this, false /* includingParents */);
253     }
254 
255     @Override
onParentChanged()256     void onParentChanged() {
257         super.onParentChanged();
258 
259         // Update task bounds if needed.
260         adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
261 
262         if (getWindowConfiguration().windowsAreScaleable()) {
263             // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
264             // while a resize is pending.
265             forceWindowsScaleable(true /* force */);
266         } else {
267             forceWindowsScaleable(false /* force */);
268         }
269     }
270 
271     @Override
removeChild(AppWindowToken token)272     void removeChild(AppWindowToken token) {
273         if (!mChildren.contains(token)) {
274             Slog.e(TAG, "removeChild: token=" + this + " not found.");
275             return;
276         }
277 
278         super.removeChild(token);
279 
280         if (mChildren.isEmpty()) {
281             EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
282             if (mDeferRemoval) {
283                 removeIfPossible();
284             }
285         }
286     }
287 
setSendingToBottom(boolean toBottom)288     void setSendingToBottom(boolean toBottom) {
289         for (int appTokenNdx = 0; appTokenNdx < mChildren.size(); appTokenNdx++) {
290             mChildren.get(appTokenNdx).sendingToBottom = toBottom;
291         }
292     }
293 
setBounds(Rect bounds, boolean forceResize)294     public int setBounds(Rect bounds, boolean forceResize) {
295         final int boundsChanged = setBounds(bounds);
296 
297         if (forceResize && (boundsChanged & BOUNDS_CHANGE_SIZE) != BOUNDS_CHANGE_SIZE) {
298             onResize();
299             return BOUNDS_CHANGE_SIZE | boundsChanged;
300         }
301 
302         return boundsChanged;
303     }
304 
305     /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
306     @Override
setBounds(Rect bounds)307     public int setBounds(Rect bounds) {
308         int rotation = Surface.ROTATION_0;
309         final DisplayContent displayContent = mStack.getDisplayContent();
310         if (displayContent != null) {
311             rotation = displayContent.getDisplayInfo().rotation;
312         } else if (bounds == null) {
313             return super.setBounds(bounds);
314         }
315 
316         final int boundsChange = super.setBounds(bounds);
317 
318         mRotation = rotation;
319 
320         updateSurfacePosition();
321         return boundsChange;
322     }
323 
324     @Override
onDescendantOrientationChanged(IBinder freezeDisplayToken, ConfigurationContainer requestingContainer)325     public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
326             ConfigurationContainer requestingContainer) {
327         if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
328             return true;
329         }
330 
331         // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill
332         // it if possible.
333         // TODO: Move to TaskRecord after unification is done.
334         if (mTaskRecord != null && mTaskRecord.getParent() != null) {
335             mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration());
336             return true;
337         }
338         return false;
339     }
340 
resize(boolean relayout, boolean forced)341     void resize(boolean relayout, boolean forced) {
342         if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
343             getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
344         }
345     }
346 
347     @Override
onDisplayChanged(DisplayContent dc)348     void onDisplayChanged(DisplayContent dc) {
349         adjustBoundsForDisplayChangeIfNeeded(dc);
350         super.onDisplayChanged(dc);
351         final int displayId = (dc != null) ? dc.getDisplayId() : Display.INVALID_DISPLAY;
352         mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
353                 mTaskId, displayId);
354     }
355 
356     /**
357      * Sets bounds that override where the task is displayed. Used during transient operations
358      * like animation / interaction.
359      */
setOverrideDisplayedBounds(Rect overrideDisplayedBounds)360     void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
361         if (overrideDisplayedBounds != null) {
362             mOverrideDisplayedBounds.set(overrideDisplayedBounds);
363         } else {
364             mOverrideDisplayedBounds.setEmpty();
365         }
366         updateSurfacePosition();
367     }
368 
369     /**
370      * Gets the bounds that override where the task is displayed. See
371      * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
372      */
getOverrideDisplayedBounds()373     Rect getOverrideDisplayedBounds() {
374         return mOverrideDisplayedBounds;
375     }
376 
setResizeable(int resizeMode)377     void setResizeable(int resizeMode) {
378         mResizeMode = resizeMode;
379     }
380 
isResizeable()381     boolean isResizeable() {
382         return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
383                 || mWmService.mForceResizableTasks;
384     }
385 
386     /**
387      * Tests if the orientation should be preserved upon user interactive resizig operations.
388 
389      * @return true if orientation should not get changed upon resizing operation.
390      */
preserveOrientationOnResize()391     boolean preserveOrientationOnResize() {
392         return mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
393                 || mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
394                 || mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
395     }
396 
cropWindowsToStackBounds()397     boolean cropWindowsToStackBounds() {
398         return isResizeable();
399     }
400 
401     /**
402      * Prepares the task bounds to be frozen with the current size. See
403      * {@link AppWindowToken#freezeBounds}.
404      */
prepareFreezingBounds()405     void prepareFreezingBounds() {
406         mPreparedFrozenBounds.set(getBounds());
407         mPreparedFrozenMergedConfig.setTo(getConfiguration());
408     }
409 
410     /**
411      * Align the task to the adjusted bounds.
412      *
413      * @param adjustedBounds Adjusted bounds to which the task should be aligned.
414      * @param tempInsetBounds Insets bounds for the task.
415      * @param alignBottom True if the task's bottom should be aligned to the adjusted
416      *                    bounds's bottom; false if the task's top should be aligned
417      *                    the adjusted bounds's top.
418      */
alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom)419     void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
420         if (!isResizeable() || EMPTY.equals(getRequestedOverrideConfiguration())) {
421             return;
422         }
423 
424         getBounds(mTmpRect2);
425         if (alignBottom) {
426             int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
427             mTmpRect2.offset(0, offsetY);
428         } else {
429             mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
430         }
431         if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
432             setOverrideDisplayedBounds(null);
433             setBounds(mTmpRect2);
434         } else {
435             setOverrideDisplayedBounds(mTmpRect2);
436             setBounds(tempInsetBounds);
437         }
438     }
439 
440     @Override
getDisplayedBounds()441     public Rect getDisplayedBounds() {
442         if (mOverrideDisplayedBounds.isEmpty()) {
443             return super.getDisplayedBounds();
444         } else {
445             return mOverrideDisplayedBounds;
446         }
447     }
448 
449     /**
450      * Calculate the maximum visible area of this task. If the task has only one app,
451      * the result will be visible frame of that app. If the task has more than one apps,
452      * we search from top down if the next app got different visible area.
453      *
454      * This effort is to handle the case where some task (eg. GMail composer) might pop up
455      * a dialog that's different in size from the activity below, in which case we should
456      * be dimming the entire task area behind the dialog.
457      *
458      * @param out Rect containing the max visible bounds.
459      * @return true if the task has some visible app windows; false otherwise.
460      */
getMaxVisibleBounds(Rect out)461     private boolean getMaxVisibleBounds(Rect out) {
462         boolean foundTop = false;
463         for (int i = mChildren.size() - 1; i >= 0; i--) {
464             final AppWindowToken token = mChildren.get(i);
465             // skip hidden (or about to hide) apps
466             if (token.mIsExiting || token.isClientHidden() || token.hiddenRequested) {
467                 continue;
468             }
469             final WindowState win = token.findMainWindow();
470             if (win == null) {
471                 continue;
472             }
473             if (!foundTop) {
474                 foundTop = true;
475                 out.setEmpty();
476             }
477 
478             win.getMaxVisibleBounds(out);
479         }
480         return foundTop;
481     }
482 
483     /** Bounds of the task to be used for dimming, as well as touch related tests. */
getDimBounds(Rect out)484     public void getDimBounds(Rect out) {
485         final DisplayContent displayContent = mStack.getDisplayContent();
486         // It doesn't matter if we in particular are part of the resize, since we couldn't have
487         // a DimLayer anyway if we weren't visible.
488         final boolean dockedResizing = displayContent != null
489                 && displayContent.mDividerControllerLocked.isResizing();
490         if (inFreeformWindowingMode() && getMaxVisibleBounds(out)) {
491             return;
492         }
493 
494         if (!matchParentBounds()) {
495             // When minimizing the docked stack when going home, we don't adjust the task bounds
496             // so we need to intersect the task bounds with the stack bounds here.
497             //
498             // If we are Docked Resizing with snap points, the task bounds could be smaller than the
499             // stack bounds and so we don't even want to use them. Even if the app should not be
500             // resized the Dim should keep up with the divider.
501             if (dockedResizing) {
502                 mStack.getBounds(out);
503             } else {
504                 mStack.getBounds(mTmpRect);
505                 mTmpRect.intersect(getBounds());
506                 out.set(mTmpRect);
507             }
508         } else {
509             out.set(getBounds());
510         }
511         return;
512     }
513 
setDragResizing(boolean dragResizing, int dragResizeMode)514     void setDragResizing(boolean dragResizing, int dragResizeMode) {
515         if (mDragResizing != dragResizing) {
516             // No need to check if the mode is allowed if it's leaving dragResize
517             if (dragResizing && !DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) {
518                 throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
519                         + mStack.mStackId + " dragResizeMode=" + dragResizeMode);
520             }
521             mDragResizing = dragResizing;
522             mDragResizeMode = dragResizeMode;
523             resetDragResizingChangeReported();
524         }
525     }
526 
isDragResizing()527     boolean isDragResizing() {
528         return mDragResizing;
529     }
530 
getDragResizeMode()531     int getDragResizeMode() {
532         return mDragResizeMode;
533     }
534 
535     /**
536      * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
537      *
538      * @param resizing Whether to put the task into drag resize mode.
539      */
setTaskDockedResizing(boolean resizing)540     public void setTaskDockedResizing(boolean resizing) {
541         setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
542     }
543 
adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent)544     private void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
545         if (displayContent == null) {
546             return;
547         }
548         if (matchParentBounds()) {
549             // TODO: Yeah...not sure if this works with WindowConfiguration, but shouldn't be a
550             // problem once we move mBounds into WindowConfiguration.
551             setBounds(null);
552             return;
553         }
554         final int displayId = displayContent.getDisplayId();
555         final int newRotation = displayContent.getDisplayInfo().rotation;
556         if (displayId != mLastRotationDisplayId) {
557             // This task is on a display that it wasn't on. There is no point to keep the relative
558             // position if display rotations for old and new displays are different. Just keep these
559             // values.
560             mLastRotationDisplayId = displayId;
561             mRotation = newRotation;
562             return;
563         }
564 
565         if (mRotation == newRotation) {
566             // Rotation didn't change. We don't need to adjust the bounds to keep the relative
567             // position.
568             return;
569         }
570 
571         // Device rotation changed.
572         // - We don't want the task to move around on the screen when this happens, so update the
573         //   task bounds so it stays in the same place.
574         // - Rotate the bounds and notify activity manager if the task can be resized independently
575         //   from its stack. The stack will take care of task rotation for the other case.
576         mTmpRect2.set(getBounds());
577 
578         if (!getWindowConfiguration().canResizeTask()) {
579             setBounds(mTmpRect2);
580             return;
581         }
582 
583         displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
584         if (setBounds(mTmpRect2) != BOUNDS_CHANGE_NONE) {
585             if (mTaskRecord != null) {
586                 mTaskRecord.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
587             }
588         }
589     }
590 
591     /** Cancels any running app transitions associated with the task. */
cancelTaskWindowTransition()592     void cancelTaskWindowTransition() {
593         for (int i = mChildren.size() - 1; i >= 0; --i) {
594             mChildren.get(i).cancelAnimation();
595         }
596     }
597 
showForAllUsers()598     boolean showForAllUsers() {
599         final int tokensCount = mChildren.size();
600         return (tokensCount != 0) && mChildren.get(tokensCount - 1).mShowForAllUsers;
601     }
602 
603     /**
604      * When we are in a floating stack (Freeform, Pinned, ...) we calculate
605      * insets differently. However if we are animating to the fullscreen stack
606      * we need to begin calculating insets as if we were fullscreen, otherwise
607      * we will have a jump at the end.
608      */
isFloating()609     boolean isFloating() {
610         return getWindowConfiguration().tasksAreFloating()
611                 && !mStack.isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
612     }
613 
614     @Override
getAnimationLeashParent()615     public SurfaceControl getAnimationLeashParent() {
616         // Currently, only the recents animation will create animation leashes for tasks. In this
617         // case, reparent the task to the home animation layer while it is being animated to allow
618         // the home activity to reorder the app windows relative to its own.
619         return getAppAnimationLayer(ANIMATION_LAYER_HOME);
620     }
621 
622     @Override
makeSurface()623     SurfaceControl.Builder makeSurface() {
624         return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId);
625     }
626 
isTaskAnimating()627     boolean isTaskAnimating() {
628         final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController();
629         if (recentsAnim != null) {
630             if (recentsAnim.isAnimatingTask(this)) {
631                 return true;
632             }
633         }
634         return false;
635     }
636 
getTopVisibleAppMainWindow()637     WindowState getTopVisibleAppMainWindow() {
638         final AppWindowToken token = getTopVisibleAppToken();
639         return token != null ? token.findMainWindow() : null;
640     }
641 
getTopFullscreenAppToken()642     AppWindowToken getTopFullscreenAppToken() {
643         for (int i = mChildren.size() - 1; i >= 0; i--) {
644             final AppWindowToken token = mChildren.get(i);
645             final WindowState win = token.findMainWindow();
646             if (win != null && win.mAttrs.isFullscreen()) {
647                 return token;
648             }
649         }
650         return null;
651     }
652 
getTopVisibleAppToken()653     AppWindowToken getTopVisibleAppToken() {
654         for (int i = mChildren.size() - 1; i >= 0; i--) {
655             final AppWindowToken token = mChildren.get(i);
656             // skip hidden (or about to hide) apps
657             if (!token.mIsExiting && !token.isClientHidden() && !token.hiddenRequested) {
658                 return token;
659             }
660         }
661         return null;
662     }
663 
positionChildAtTop(AppWindowToken aToken)664     void positionChildAtTop(AppWindowToken aToken) {
665         positionChildAt(aToken, POSITION_TOP);
666     }
667 
positionChildAt(AppWindowToken aToken, int position)668     void positionChildAt(AppWindowToken aToken, int position) {
669         if (aToken == null) {
670             Slog.w(TAG_WM,
671                     "Attempted to position of non-existing app");
672             return;
673         }
674 
675         positionChildAt(position, aToken, false /* includeParents */);
676     }
677 
forceWindowsScaleable(boolean force)678     void forceWindowsScaleable(boolean force) {
679         mWmService.openSurfaceTransaction();
680         try {
681             for (int i = mChildren.size() - 1; i >= 0; i--) {
682                 mChildren.get(i).forceWindowsScaleableInTransaction(force);
683             }
684         } finally {
685             mWmService.closeSurfaceTransaction("forceWindowsScaleable");
686         }
687     }
688 
setTaskDescription(TaskDescription taskDescription)689     void setTaskDescription(TaskDescription taskDescription) {
690         mTaskDescription = taskDescription;
691     }
692 
onSnapshotChanged(ActivityManager.TaskSnapshot snapshot)693     void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
694         mTaskRecord.onSnapshotChanged(snapshot);
695     }
696 
getTaskDescription()697     TaskDescription getTaskDescription() {
698         return mTaskDescription;
699     }
700 
701     @Override
fillsParent()702     boolean fillsParent() {
703         return matchParentBounds() || !getWindowConfiguration().canResizeTask();
704     }
705 
706     @Override
forAllTasks(Consumer<Task> callback)707     void forAllTasks(Consumer<Task> callback) {
708         callback.accept(this);
709     }
710 
711     /**
712      * @param canAffectSystemUiFlags If false, all windows in this task can not affect SystemUI
713      *                               flags. See {@link WindowState#canAffectSystemUiFlags()}.
714      */
setCanAffectSystemUiFlags(boolean canAffectSystemUiFlags)715     void setCanAffectSystemUiFlags(boolean canAffectSystemUiFlags) {
716         mCanAffectSystemUiFlags = canAffectSystemUiFlags;
717     }
718 
719     /**
720      * @see #setCanAffectSystemUiFlags
721      */
canAffectSystemUiFlags()722     boolean canAffectSystemUiFlags() {
723         return mCanAffectSystemUiFlags;
724     }
725 
dontAnimateDimExit()726     void dontAnimateDimExit() {
727         mDimmer.dontAnimateExit();
728     }
729 
730     @Override
toString()731     public String toString() {
732         return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}";
733     }
734 
getName()735     String getName() {
736         return toShortString();
737     }
738 
clearPreserveNonFloatingState()739     void clearPreserveNonFloatingState() {
740         mPreserveNonFloatingState = false;
741     }
742 
743     @Override
getDimmer()744     Dimmer getDimmer() {
745         return mDimmer;
746     }
747 
748     @Override
prepareSurfaces()749     void prepareSurfaces() {
750         mDimmer.resetDimStates();
751         super.prepareSurfaces();
752         getDimBounds(mTmpDimBoundsRect);
753 
754         // Bounds need to be relative, as the dim layer is a child.
755         mTmpDimBoundsRect.offsetTo(0, 0);
756         if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
757             scheduleAnimation();
758         }
759     }
760 
761     @CallSuper
762     @Override
writeToProto(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)763     public void writeToProto(ProtoOutputStream proto, long fieldId,
764             @WindowTraceLogLevel int logLevel) {
765         if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
766             return;
767         }
768 
769         final long token = proto.start(fieldId);
770         super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
771         proto.write(ID, mTaskId);
772         for (int i = mChildren.size() - 1; i >= 0; i--) {
773             final AppWindowToken appWindowToken = mChildren.get(i);
774             appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
775         }
776         proto.write(FILLS_PARENT, matchParentBounds());
777         getBounds().writeToProto(proto, BOUNDS);
778         mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
779         proto.write(DEFER_REMOVAL, mDeferRemoval);
780         proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
781         proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
782         proto.end(token);
783     }
784 
785     @Override
dump(PrintWriter pw, String prefix, boolean dumpAll)786     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
787         super.dump(pw, prefix, dumpAll);
788         final String doublePrefix = prefix + "  ";
789 
790         pw.println(prefix + "taskId=" + mTaskId);
791         pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
792         pw.println(doublePrefix + "mdr=" + mDeferRemoval);
793         pw.println(doublePrefix + "appTokens=" + mChildren);
794         pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
795 
796         final String triplePrefix = doublePrefix + "  ";
797         final String quadruplePrefix = triplePrefix + "  ";
798 
799         for (int i = mChildren.size() - 1; i >= 0; i--) {
800             final AppWindowToken wtoken = mChildren.get(i);
801             pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
802             wtoken.dump(pw, quadruplePrefix, dumpAll);
803         }
804     }
805 
toShortString()806     String toShortString() {
807         return "Task=" + mTaskId;
808     }
809 }
810