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