Task.java revision ff71d20ff357a33c30e1e3b474e51ad47d5c5d3a
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.DOCKED_STACK_ID;
21import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
22import static android.app.ActivityManager.StackId.HOME_STACK_ID;
23import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
24import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
25import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
26import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
27import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
28import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
29import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
30
31import android.app.ActivityManager.StackId;
32import android.content.pm.ActivityInfo;
33import android.content.res.Configuration;
34import android.graphics.Rect;
35import android.util.EventLog;
36import android.util.Slog;
37import android.view.DisplayInfo;
38import android.view.Surface;
39
40import com.android.server.EventLogTags;
41
42import java.io.PrintWriter;
43import java.util.ArrayList;
44
45class Task implements DimLayer.DimLayerUser {
46    static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
47    // Return value from {@link setBounds} indicating no change was made to the Task bounds.
48    static final int BOUNDS_CHANGE_NONE = 0;
49    // Return value from {@link setBounds} indicating the position of the Task bounds changed.
50    static final int BOUNDS_CHANGE_POSITION = 1;
51    // Return value from {@link setBounds} indicating the size of the Task bounds changed.
52    static final int BOUNDS_CHANGE_SIZE = 1 << 1;
53
54    TaskStack mStack;
55    final AppTokenList mAppTokens = new AppTokenList();
56    final int mTaskId;
57    final int mUserId;
58    boolean mDeferRemoval = false;
59    final WindowManagerService mService;
60
61    // Content limits relative to the DisplayContent this sits in.
62    private Rect mBounds = new Rect();
63    final Rect mPreparedFrozenBounds = new Rect();
64
65    private Rect mPreScrollBounds = new Rect();
66    private boolean mScrollValid;
67
68    // Bounds used to calculate the insets.
69    private final Rect mTempInsetBounds = new Rect();
70
71    // Device rotation as of the last time {@link #mBounds} was set.
72    int mRotation;
73
74    // Whether mBounds is fullscreen
75    private boolean mFullscreen = true;
76
77    // Contains configurations settings that are different from the global configuration due to
78    // stack specific operations. E.g. {@link #setBounds}.
79    Configuration mOverrideConfig;
80
81    // For comparison with DisplayContent bounds.
82    private Rect mTmpRect = new Rect();
83    // For handling display rotations.
84    private Rect mTmpRect2 = new Rect();
85
86    // Resize mode of the task. See {@link ActivityInfo#resizeMode}
87    private int mResizeMode;
88
89    // Whether the task is currently being drag-resized
90    private boolean mDragResizing;
91    private int mDragResizeMode;
92
93    private boolean mHomeTask;
94
95    Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
96            Configuration config) {
97        mTaskId = taskId;
98        mStack = stack;
99        mUserId = userId;
100        mService = service;
101        setBounds(bounds, config);
102    }
103
104    DisplayContent getDisplayContent() {
105        return mStack.getDisplayContent();
106    }
107
108    void addAppToken(int addPos, AppWindowToken wtoken, int resizeMode, boolean homeTask) {
109        final int lastPos = mAppTokens.size();
110        if (addPos >= lastPos) {
111            addPos = lastPos;
112        } else {
113            for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
114                if (mAppTokens.get(pos).removed) {
115                    // addPos assumes removed tokens are actually gone.
116                    ++addPos;
117                }
118            }
119        }
120        mAppTokens.add(addPos, wtoken);
121        wtoken.mTask = this;
122        mDeferRemoval = false;
123        mResizeMode = resizeMode;
124        mHomeTask = homeTask;
125    }
126
127    private boolean hasAppTokensAlive() {
128        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
129            if (!mAppTokens.get(i).appDied) {
130                return true;
131            }
132        }
133        return false;
134    }
135
136    void removeLocked() {
137        if (hasAppTokensAlive() && mStack.isAnimating()) {
138            if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
139            mDeferRemoval = true;
140            return;
141        }
142        if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
143        EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask");
144        mDeferRemoval = false;
145        DisplayContent content = getDisplayContent();
146        if (content != null) {
147            content.mDimLayerController.removeDimLayerUser(this);
148        }
149        mStack.removeTask(this);
150        mService.mTaskIdToTask.delete(mTaskId);
151    }
152
153    void moveTaskToStack(TaskStack stack, boolean toTop) {
154        if (stack == mStack) {
155            return;
156        }
157        if (DEBUG_STACK) Slog.i(TAG, "moveTaskToStack: removing taskId=" + mTaskId
158                + " from stack=" + mStack);
159        EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
160        if (mStack != null) {
161            mStack.removeTask(this);
162        }
163        stack.addTask(this, toTop);
164    }
165
166    void positionTaskInStack(TaskStack stack, int position, Rect bounds, Configuration config) {
167        if (mStack != null && stack != mStack) {
168            if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId
169                    + " from stack=" + mStack);
170            EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
171            mStack.removeTask(this);
172        }
173        stack.positionTask(this, position, showForAllUsers());
174        resizeLocked(bounds, config, false /* force */);
175
176        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
177            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
178            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
179                final WindowState win = windows.get(winNdx);
180                win.notifyMovedInStack();
181            }
182        }
183    }
184
185    boolean removeAppToken(AppWindowToken wtoken) {
186        boolean removed = mAppTokens.remove(wtoken);
187        if (mAppTokens.size() == 0) {
188            EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
189            if (mDeferRemoval) {
190                removeLocked();
191            }
192        }
193        wtoken.mTask = null;
194        /* Leave mTaskId for now, it might be useful for debug
195        wtoken.mTaskId = -1;
196         */
197        return removed;
198    }
199
200    void setSendingToBottom(boolean toBottom) {
201        for (int appTokenNdx = 0; appTokenNdx < mAppTokens.size(); appTokenNdx++) {
202            mAppTokens.get(appTokenNdx).sendingToBottom = toBottom;
203        }
204    }
205
206    /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
207    private int setBounds(Rect bounds, Configuration config) {
208        if (config == null) {
209            config = Configuration.EMPTY;
210        }
211        if (bounds == null && !Configuration.EMPTY.equals(config)) {
212            throw new IllegalArgumentException("null bounds but non empty configuration: "
213                    + config);
214        }
215        if (bounds != null && Configuration.EMPTY.equals(config)) {
216            throw new IllegalArgumentException("non null bounds, but empty configuration");
217        }
218        boolean oldFullscreen = mFullscreen;
219        int rotation = Surface.ROTATION_0;
220        final DisplayContent displayContent = mStack.getDisplayContent();
221        if (displayContent != null) {
222            displayContent.getLogicalDisplayRect(mTmpRect);
223            rotation = displayContent.getDisplayInfo().rotation;
224            mFullscreen = bounds == null;
225            if (mFullscreen) {
226                bounds = mTmpRect;
227            }
228        }
229
230        if (bounds == null) {
231            // Can't set to fullscreen if we don't have a display to get bounds from...
232            return BOUNDS_CHANGE_NONE;
233        }
234        if (mPreScrollBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
235            return BOUNDS_CHANGE_NONE;
236        }
237
238        int boundsChange = BOUNDS_CHANGE_NONE;
239        if (mPreScrollBounds.left != bounds.left || mPreScrollBounds.top != bounds.top) {
240            boundsChange |= BOUNDS_CHANGE_POSITION;
241        }
242        if (mPreScrollBounds.width() != bounds.width() || mPreScrollBounds.height() != bounds.height()) {
243            boundsChange |= BOUNDS_CHANGE_SIZE;
244        }
245
246
247        mPreScrollBounds.set(bounds);
248
249        resetScrollLocked();
250
251        mRotation = rotation;
252        if (displayContent != null) {
253            displayContent.mDimLayerController.updateDimLayer(this);
254        }
255        mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
256        return boundsChange;
257    }
258
259    /**
260     * Sets the bounds used to calculate the insets. See
261     * {@link android.app.IActivityManager#resizeDockedStack} why this is needed.
262     */
263    void setTempInsetBounds(Rect tempInsetBounds) {
264        if (tempInsetBounds != null) {
265            mTempInsetBounds.set(tempInsetBounds);
266        } else {
267            mTempInsetBounds.setEmpty();
268        }
269    }
270
271    /**
272     * Gets the bounds used to calculate the insets. See
273     * {@link android.app.IActivityManager#resizeDockedStack} why this is needed.
274     */
275    void getTempInsetBounds(Rect out) {
276        out.set(mTempInsetBounds);
277    }
278
279    void setResizeable(int resizeMode) {
280        mResizeMode = resizeMode;
281    }
282
283    boolean isResizeable() {
284        return !mHomeTask
285                && (ActivityInfo.isResizeableMode(mResizeMode) || mService.mForceResizableTasks);
286    }
287
288    boolean cropWindowsToStackBounds() {
289        return !mHomeTask && (isResizeable() || mResizeMode == RESIZE_MODE_CROP_WINDOWS);
290    }
291
292    boolean isHomeTask() {
293        return mHomeTask;
294    }
295
296    private boolean inCropWindowsResizeMode() {
297        return !mHomeTask && !isResizeable() && mResizeMode == RESIZE_MODE_CROP_WINDOWS;
298    }
299
300    boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) {
301        int boundsChanged = setBounds(bounds, configuration);
302        if (forced) {
303            boundsChanged |= BOUNDS_CHANGE_SIZE;
304        }
305        if (boundsChanged == BOUNDS_CHANGE_NONE) {
306            return false;
307        }
308        if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
309            resizeWindows();
310        } else {
311            moveWindows();
312        }
313        return true;
314    }
315
316    /**
317     * Prepares the task bounds to be frozen with the current size. See
318     * {@link AppWindowToken#freezeBounds}.
319     */
320    void prepareFreezingBounds() {
321        mPreparedFrozenBounds.set(mBounds);
322    }
323
324    void resetScrollLocked() {
325        if (mScrollValid) {
326            mScrollValid = false;
327            applyScrollToAllWindows(0, 0);
328        }
329        mBounds.set(mPreScrollBounds);
330    }
331
332    void applyScrollToAllWindows(final int xOffset, final int yOffset) {
333        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
334            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
335            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
336                final WindowState win = windows.get(winNdx);
337                win.mXOffset = xOffset;
338                win.mYOffset = yOffset;
339            }
340        }
341    }
342
343    void applyScrollToWindowIfNeeded(final WindowState win) {
344        if (mScrollValid) {
345            win.mXOffset = mBounds.left;
346            win.mYOffset = mBounds.top;
347        }
348    }
349
350    boolean scrollLocked(Rect bounds) {
351        // shift the task bound if it doesn't fully cover the stack area
352        mStack.getDimBounds(mTmpRect);
353        if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) {
354            if (bounds.left > mTmpRect.left) {
355                bounds.left = mTmpRect.left;
356                bounds.right = mTmpRect.left + mBounds.width();
357            } else if (bounds.right < mTmpRect.right) {
358                bounds.left = mTmpRect.right - mBounds.width();
359                bounds.right = mTmpRect.right;
360            }
361        } else {
362            if (bounds.top > mTmpRect.top) {
363                bounds.top = mTmpRect.top;
364                bounds.bottom = mTmpRect.top + mBounds.height();
365            } else if (bounds.bottom < mTmpRect.bottom) {
366                bounds.top = mTmpRect.bottom - mBounds.height();
367                bounds.bottom = mTmpRect.bottom;
368            }
369        }
370
371        // We can stop here if we're already scrolling and the scrolled bounds not changed.
372        if (mScrollValid && bounds.equals(mBounds)) {
373            return false;
374        }
375
376        // Normal setBounds() does not allow non-null bounds for fullscreen apps.
377        // We only change bounds for the scrolling case without change it size,
378        // on resizing path we should still want the validation.
379        mBounds.set(bounds);
380        mScrollValid = true;
381        applyScrollToAllWindows(bounds.left, bounds.top);
382        return true;
383    }
384
385    /** Return true if the current bound can get outputted to the rest of the system as-is. */
386    private boolean useCurrentBounds() {
387        final DisplayContent displayContent = mStack.getDisplayContent();
388        if (mFullscreen
389                || !StackId.isTaskResizeableByDockedStack(mStack.mStackId)
390                || displayContent == null
391                || displayContent.getDockedStackVisibleForUserLocked() != null) {
392            return true;
393        }
394        return false;
395    }
396
397    /** Original bounds of the task if applicable, otherwise fullscreen rect. */
398    void getBounds(Rect out) {
399        if (useCurrentBounds()) {
400            // No need to adjust the output bounds if fullscreen or the docked stack is visible
401            // since it is already what we want to represent to the rest of the system.
402            out.set(mBounds);
403            return;
404        }
405
406        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack is
407        // not currently visible. Go ahead a represent it as fullscreen to the rest of the system.
408        mStack.getDisplayContent().getLogicalDisplayRect(out);
409    }
410
411    /**
412     * Calculate the maximum visible area of this task. If the task has only one app,
413     * the result will be visible frame of that app. If the task has more than one apps,
414     * we search from top down if the next app got different visible area.
415     *
416     * This effort is to handle the case where some task (eg. GMail composer) might pop up
417     * a dialog that's different in size from the activity below, in which case we should
418     * be dimming the entire task area behind the dialog.
419     *
420     * @param out Rect containing the max visible bounds.
421     * @return true if the task has some visible app windows; false otherwise.
422     */
423    boolean getMaxVisibleBounds(Rect out) {
424        boolean foundTop = false;
425        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
426            final AppWindowToken token = mAppTokens.get(i);
427            // skip hidden (or about to hide) apps
428            if (token.mIsExiting || token.clientHidden || token.hiddenRequested) {
429                continue;
430            }
431            final WindowState win = token.findMainWindow();
432            if (win == null) {
433                continue;
434            }
435            if (!foundTop) {
436                out.set(win.mVisibleFrame);
437                foundTop = true;
438                continue;
439            }
440            if (win.mVisibleFrame.left < out.left) {
441                out.left = win.mVisibleFrame.left;
442            }
443            if (win.mVisibleFrame.top < out.top) {
444                out.top = win.mVisibleFrame.top;
445            }
446            if (win.mVisibleFrame.right > out.right) {
447                out.right = win.mVisibleFrame.right;
448            }
449            if (win.mVisibleFrame.bottom > out.bottom) {
450                out.bottom = win.mVisibleFrame.bottom;
451            }
452        }
453        return foundTop;
454    }
455
456    /** Bounds of the task to be used for dimming, as well as touch related tests. */
457    @Override
458    public void getDimBounds(Rect out) {
459        if (useCurrentBounds()) {
460            if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
461                return;
462            }
463
464            if (!mFullscreen) {
465                // When minimizing the docked stack when going home, we don't adjust the task bounds
466                // so we need to intersect the task bounds with the stack bounds here.
467                mStack.getBounds(mTmpRect);
468                mTmpRect.intersect(mBounds);
469                out.set(mTmpRect);
470            } else {
471                out.set(mBounds);
472            }
473            return;
474        }
475
476        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
477        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
478        // system.
479        mStack.getDisplayContent().getLogicalDisplayRect(out);
480    }
481
482    void setDragResizing(boolean dragResizing, int dragResizeMode) {
483        if (mDragResizing != dragResizing) {
484            if (!DragResizeMode.isModeAllowedForStack(mStack.mStackId, dragResizeMode)) {
485                throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
486                        + mStack.mStackId + " dragResizeMode=" + dragResizeMode);
487            }
488            mDragResizing = dragResizing;
489            mDragResizeMode = dragResizeMode;
490            resetDragResizingChangeReported();
491        }
492    }
493
494    void resetDragResizingChangeReported() {
495        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
496            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
497            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
498                final WindowState win = windows.get(winNdx);
499                win.resetDragResizingChangeReported();
500            }
501        }
502    }
503
504    boolean isDragResizing() {
505        return mDragResizing || (mStack != null && mStack.isDragResizing());
506    }
507
508    int getDragResizeMode() {
509        return mDragResizeMode;
510    }
511
512    /**
513     * Adds all of the tasks windows to {@link WindowManagerService#mWaitingForDrawn} if drag
514     * resizing state of the window has been changed.
515     */
516    void addWindowsWaitingForDrawnIfResizingChanged() {
517        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
518            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
519            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
520                final WindowState win = windows.get(winNdx);
521                if (win.isDragResizeChanged()) {
522                    mService.mWaitingForDrawn.add(win);
523                }
524            }
525        }
526    }
527
528    void updateDisplayInfo(final DisplayContent displayContent) {
529        if (displayContent == null) {
530            return;
531        }
532        if (mFullscreen) {
533            setBounds(null, Configuration.EMPTY);
534            return;
535        }
536        final int newRotation = displayContent.getDisplayInfo().rotation;
537        if (mRotation == newRotation) {
538            return;
539        }
540
541        // Device rotation changed.
542        // - Reset the bounds to the pre-scroll bounds as whatever scrolling was done is no longer
543        // valid.
544        // - Rotate the bounds and notify activity manager if the task can be resized independently
545        // from its stack. The stack will take care of task rotation for the other case.
546        mTmpRect2.set(mPreScrollBounds);
547
548        if (!StackId.isTaskResizeAllowed(mStack.mStackId)) {
549            setBounds(mTmpRect2, mOverrideConfig);
550            return;
551        }
552
553        displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
554        if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
555            // Post message to inform activity manager of the bounds change simulating a one-way
556            // call. We do this to prevent a deadlock between window manager lock and activity
557            // manager lock been held.
558            mService.mH.obtainMessage(RESIZE_TASK, mTaskId,
559                    RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mPreScrollBounds).sendToTarget();
560        }
561    }
562
563    void resizeWindows() {
564        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
565        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
566            final AppWindowToken atoken = mAppTokens.get(activityNdx);
567
568            // Some windows won't go through the resizing process, if they don't have a surface, so
569            // destroy all saved surfaces here.
570            atoken.destroySavedSurfaces();
571            final ArrayList<WindowState> windows = atoken.allAppWindows;
572            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
573                final WindowState win = windows.get(winNdx);
574                if (win.mHasSurface && !resizingWindows.contains(win)) {
575                    if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
576                    resizingWindows.add(win);
577
578                    // If we are not drag resizing, force recreating of a new surface so updating
579                    // the content and positioning that surface will be in sync.
580                    if (!win.computeDragResizing()) {
581                        win.mResizedWhileNotDragResizing = true;
582                    }
583                }
584                if (win.isGoneForLayoutLw()) {
585                    win.mResizedWhileGone = true;
586                }
587            }
588        }
589    }
590
591    void moveWindows() {
592        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
593            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
594            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
595                final WindowState win = windows.get(winNdx);
596                if (DEBUG_RESIZE) Slog.d(TAG, "moveWindows: Moving " + win);
597                win.mMovedByResize = true;
598            }
599        }
600    }
601
602    /**
603     * Cancels any running app transitions associated with the task.
604     */
605    void cancelTaskWindowTransition() {
606        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
607            mAppTokens.get(activityNdx).mAppAnimator.clearAnimation();
608        }
609    }
610
611    /**
612     * Cancels any running thumbnail transitions associated with the task.
613     */
614    void cancelTaskThumbnailTransition() {
615        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
616            mAppTokens.get(activityNdx).mAppAnimator.clearThumbnail();
617        }
618    }
619
620    boolean showForAllUsers() {
621        final int tokensCount = mAppTokens.size();
622        return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
623    }
624
625    boolean isVisibleForUser() {
626        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
627            final AppWindowToken appToken = mAppTokens.get(i);
628            for (int j = appToken.allAppWindows.size() - 1; j >= 0; j--) {
629                WindowState window = appToken.allAppWindows.get(j);
630                if (!window.isHiddenFromUserLocked()) {
631                    return true;
632                }
633            }
634        }
635        return false;
636    }
637
638    boolean isVisible() {
639        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
640            final AppWindowToken appToken = mAppTokens.get(i);
641            if (appToken.isVisible()) {
642                return true;
643            }
644        }
645        return false;
646    }
647
648    boolean inHomeStack() {
649        return mStack != null && mStack.mStackId == HOME_STACK_ID;
650    }
651
652    boolean inFreeformWorkspace() {
653        return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
654    }
655
656    boolean inDockedWorkspace() {
657        return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
658    }
659
660    boolean isResizeableByDockedStack() {
661        final DisplayContent displayContent = getDisplayContent();
662        return displayContent != null && displayContent.getDockedStackLocked() != null
663                && mStack != null && StackId.isTaskResizeableByDockedStack(mStack.mStackId);
664    }
665
666    boolean isFloating() {
667        return StackId.tasksAreFloating(mStack.mStackId);
668    }
669
670    /**
671     * Whether the task should be treated as if it's docked. Returns true if the task
672     * is currently in docked workspace, or it's side-by-side to a docked task.
673     */
674    boolean isDockedInEffect() {
675        return inDockedWorkspace() || isResizeableByDockedStack();
676    }
677
678    boolean isTwoFingerScrollMode() {
679        return inCropWindowsResizeMode() && isDockedInEffect();
680    }
681
682    WindowState getTopVisibleAppMainWindow() {
683        final AppWindowToken token = getTopVisibleAppToken();
684        return token != null ? token.findMainWindow() : null;
685    }
686
687    AppWindowToken getTopVisibleAppToken() {
688        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
689            final AppWindowToken token = mAppTokens.get(i);
690            // skip hidden (or about to hide) apps
691            if (!token.mIsExiting && !token.clientHidden && !token.hiddenRequested) {
692                return token;
693            }
694        }
695        return null;
696    }
697
698    @Override
699    public boolean isFullscreen() {
700        if (useCurrentBounds()) {
701            return mFullscreen;
702        }
703        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
704        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
705        // system.
706        return true;
707    }
708
709    @Override
710    public DisplayInfo getDisplayInfo() {
711        return mStack.getDisplayContent().getDisplayInfo();
712    }
713
714    @Override
715    public String toString() {
716        return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
717    }
718
719    @Override
720    public String toShortString() {
721        return "Task=" + mTaskId;
722    }
723
724    public void dump(String prefix, PrintWriter pw) {
725        final String doublePrefix = prefix + "  ";
726
727        pw.println(prefix + "taskId=" + mTaskId);
728        pw.println(doublePrefix + "mFullscreen=" + mFullscreen);
729        pw.println(doublePrefix + "mBounds=" + mBounds.toShortString());
730        pw.println(doublePrefix + "mdr=" + mDeferRemoval);
731        pw.println(doublePrefix + "appTokens=" + mAppTokens);
732        pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
733
734        final String triplePrefix = doublePrefix + "  ";
735
736        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
737            final AppWindowToken wtoken = mAppTokens.get(i);
738            pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
739            wtoken.dump(pw, triplePrefix);
740        }
741
742    }
743}
744