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