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