Task.java revision 112eb8c1f76fff6a670d7c5f85e8c3d656cd3aa8
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.StackId.DOCKED_STACK_ID;
20import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
21import static android.app.ActivityManager.StackId.HOME_STACK_ID;
22import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
23import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
24import static com.android.server.wm.WindowManagerService.TAG;
25import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
26import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
27import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
28
29import android.app.ActivityManager.StackId;
30import android.content.res.Configuration;
31import android.graphics.Rect;
32import android.util.EventLog;
33import android.util.Slog;
34import android.view.DisplayInfo;
35import android.view.Surface;
36
37import com.android.server.EventLogTags;
38
39import java.io.PrintWriter;
40import java.util.ArrayList;
41
42class Task implements DimLayer.DimLayerUser {
43    // Return value from {@link setBounds} indicating no change was made to the Task bounds.
44    static final int BOUNDS_CHANGE_NONE = 0;
45    // Return value from {@link setBounds} indicating the position of the Task bounds changed.
46    static final int BOUNDS_CHANGE_POSITION = 1;
47    // Return value from {@link setBounds} indicating the size of the Task bounds changed.
48    static final int BOUNDS_CHANGE_SIZE = 1 << 1;
49
50    TaskStack mStack;
51    final AppTokenList mAppTokens = new AppTokenList();
52    final int mTaskId;
53    final int mUserId;
54    boolean mDeferRemoval = false;
55    final WindowManagerService mService;
56
57    // Content limits relative to the DisplayContent this sits in.
58    private Rect mBounds = new Rect();
59
60    // Device rotation as of the last time {@link #mBounds} was set.
61    int mRotation;
62
63    // Whether mBounds is fullscreen
64    private boolean mFullscreen = true;
65
66    // Contains configurations settings that are different from the global configuration due to
67    // stack specific operations. E.g. {@link #setBounds}.
68    Configuration mOverrideConfig;
69
70    // For comparison with DisplayContent bounds.
71    private Rect mTmpRect = new Rect();
72    // For handling display rotations.
73    private Rect mTmpRect2 = new Rect();
74
75    // Whether the task is currently being drag-resized
76    private boolean mDragResizing;
77
78    Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
79            Configuration config) {
80        mTaskId = taskId;
81        mStack = stack;
82        mUserId = userId;
83        mService = service;
84        setBounds(bounds, config);
85    }
86
87    DisplayContent getDisplayContent() {
88        return mStack.getDisplayContent();
89    }
90
91    void addAppToken(int addPos, AppWindowToken wtoken) {
92        final int lastPos = mAppTokens.size();
93        if (addPos >= lastPos) {
94            addPos = lastPos;
95        } else {
96            for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
97                if (mAppTokens.get(pos).removed) {
98                    // addPos assumes removed tokens are actually gone.
99                    ++addPos;
100                }
101            }
102        }
103        mAppTokens.add(addPos, wtoken);
104        wtoken.mTask = this;
105        mDeferRemoval = false;
106    }
107
108    void removeLocked() {
109        if (!mAppTokens.isEmpty() && mStack.isAnimating()) {
110            if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
111            mDeferRemoval = true;
112            return;
113        }
114        if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
115        EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask");
116        mDeferRemoval = false;
117        DisplayContent content = getDisplayContent();
118        if (content != null) {
119            content.mDimLayerController.removeDimLayerUser(this);
120        }
121        mStack.removeTask(this);
122        mService.mTaskIdToTask.delete(mTaskId);
123    }
124
125    void moveTaskToStack(TaskStack stack, boolean toTop) {
126        if (stack == mStack) {
127            return;
128        }
129        if (DEBUG_STACK) Slog.i(TAG, "moveTaskToStack: removing taskId=" + mTaskId
130                + " from stack=" + mStack);
131        EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
132        if (mStack != null) {
133            mStack.removeTask(this);
134        }
135        stack.addTask(this, toTop);
136    }
137
138    void positionTaskInStack(TaskStack stack, int position) {
139        if (mStack != null && stack != mStack) {
140            if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId
141                    + " from stack=" + mStack);
142            EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
143            mStack.removeTask(this);
144        }
145        stack.positionTask(this, position, showForAllUsers());
146    }
147
148    boolean removeAppToken(AppWindowToken wtoken) {
149        boolean removed = mAppTokens.remove(wtoken);
150        if (mAppTokens.size() == 0) {
151            EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId,
152                    "removeAppToken: last token");
153            if (mDeferRemoval) {
154                removeLocked();
155            }
156        }
157        wtoken.mTask = null;
158        /* Leave mTaskId for now, it might be useful for debug
159        wtoken.mTaskId = -1;
160         */
161        return removed;
162    }
163
164    void setSendingToBottom(boolean toBottom) {
165        for (int appTokenNdx = 0; appTokenNdx < mAppTokens.size(); appTokenNdx++) {
166            mAppTokens.get(appTokenNdx).sendingToBottom = toBottom;
167        }
168    }
169
170    /** Set the task bounds. Passing in null sets the bounds to fullscreen. */
171    int setBounds(Rect bounds, Configuration config) {
172        if (config == null) {
173            config = Configuration.EMPTY;
174        }
175        if (bounds == null && !Configuration.EMPTY.equals(config)) {
176            throw new IllegalArgumentException("null bounds but non empty configuration: "
177                    + config);
178        }
179        if (bounds != null && Configuration.EMPTY.equals(config)) {
180            throw new IllegalArgumentException("non null bounds, but empty configuration");
181        }
182        boolean oldFullscreen = mFullscreen;
183        int rotation = Surface.ROTATION_0;
184        final DisplayContent displayContent = mStack.getDisplayContent();
185        if (displayContent != null) {
186            displayContent.getLogicalDisplayRect(mTmpRect);
187            rotation = displayContent.getDisplayInfo().rotation;
188            if (bounds == null) {
189                bounds = mTmpRect;
190                mFullscreen = true;
191            } else {
192                if ((mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
193                        && mStack.mStackId != PINNED_STACK_ID) || bounds.isEmpty()) {
194                    // ensure bounds are entirely within the display rect
195                    if (!bounds.intersect(mTmpRect)) {
196                        // Can't set bounds outside the containing display...Sorry!
197                        return BOUNDS_CHANGE_NONE;
198                    }
199                }
200                mFullscreen = mTmpRect.equals(bounds);
201            }
202        }
203
204        if (bounds == null) {
205            // Can't set to fullscreen if we don't have a display to get bounds from...
206            return BOUNDS_CHANGE_NONE;
207        }
208        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
209            return BOUNDS_CHANGE_NONE;
210        }
211
212        int boundsChange = BOUNDS_CHANGE_NONE;
213        if (mBounds.left != bounds.left || mBounds.right != bounds.right) {
214            boundsChange |= BOUNDS_CHANGE_POSITION;
215        }
216        if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) {
217            boundsChange |= BOUNDS_CHANGE_SIZE;
218        }
219
220        mBounds.set(bounds);
221        mRotation = rotation;
222        if (displayContent != null) {
223            displayContent.mDimLayerController.updateDimLayer(this);
224        }
225        mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
226        return boundsChange;
227    }
228
229    boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) {
230        int boundsChanged = setBounds(bounds, configuration);
231        if (forced) {
232            boundsChanged |= BOUNDS_CHANGE_SIZE;
233        }
234        if (boundsChanged == BOUNDS_CHANGE_NONE) {
235            return false;
236        }
237        if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
238            resizeWindows();
239        }
240        return true;
241    }
242
243    /** Return true if the current bound can get outputted to the rest of the system as-is. */
244    private boolean useCurrentBounds() {
245        final DisplayContent displayContent = mStack.getDisplayContent();
246        if (mFullscreen
247                || !StackId.isTaskResizeableByDockedStack(mStack.mStackId)
248                || displayContent == null
249                || displayContent.getDockedStackLocked() != null) {
250            return true;
251        }
252        return false;
253    }
254
255    /** Bounds of the task with other system factors taken into consideration. */
256    @Override
257    public void getBounds(Rect out) {
258        if (useCurrentBounds()) {
259            // No need to adjust the output bounds if fullscreen or the docked stack is visible
260            // since it is already what we want to represent to the rest of the system.
261            out.set(mBounds);
262            return;
263        }
264
265        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
266        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
267        // system.
268        mStack.getDisplayContent().getLogicalDisplayRect(out);
269    }
270
271    void setDragResizing(boolean dragResizing) {
272        mDragResizing = dragResizing;
273    }
274
275    boolean isDragResizing() {
276        return mDragResizing;
277    }
278
279    void updateDisplayInfo(final DisplayContent displayContent) {
280        if (displayContent == null) {
281            return;
282        }
283        if (mFullscreen) {
284            setBounds(null, Configuration.EMPTY);
285            return;
286        }
287        final int newRotation = displayContent.getDisplayInfo().rotation;
288        if (mRotation == newRotation) {
289            return;
290        }
291
292        // Device rotation changed. We don't want the task to move around on the screen when
293        // this happens, so update the task bounds so it stays in the same place.
294        mTmpRect2.set(mBounds);
295        displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
296        if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
297            // Post message to inform activity manager of the bounds change simulating
298            // a one-way call. We do this to prevent a deadlock between window manager
299            // lock and activity manager lock been held. Only tasks within the freeform stack
300            // are resizeable independently of their stack resizing.
301            if (mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
302                mService.mH.sendMessage(mService.mH.obtainMessage(
303                        RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
304            }
305        }
306    }
307
308    void resizeWindows() {
309        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
310        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
311            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
312            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
313                final WindowState win = windows.get(winNdx);
314                if (!resizingWindows.contains(win)) {
315                    if (DEBUG_RESIZE) Slog.d(TAG, "setBounds: Resizing " + win);
316                    resizingWindows.add(win);
317                }
318            }
319        }
320    }
321
322    /**
323     * Cancels any running app transitions associated with the task.
324     */
325    void cancelTaskWindowTransition() {
326        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
327            mAppTokens.get(activityNdx).mAppAnimator.clearAnimation();
328        }
329    }
330
331    boolean showForAllUsers() {
332        final int tokensCount = mAppTokens.size();
333        return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
334    }
335
336    boolean inHomeStack() {
337        return mStack != null && mStack.mStackId == HOME_STACK_ID;
338    }
339
340    boolean inFreeformWorkspace() {
341        return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
342    }
343
344    boolean inDockedWorkspace() {
345        return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
346    }
347
348    WindowState getTopAppMainWindow() {
349        final int tokensCount = mAppTokens.size();
350        return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
351    }
352
353    AppWindowToken getTopAppWindowToken() {
354        final int tokensCount = mAppTokens.size();
355        return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null;
356    }
357
358    @Override
359    public boolean isFullscreen() {
360        if (useCurrentBounds()) {
361            return mFullscreen;
362        }
363        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
364        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
365        // system.
366        return true;
367    }
368
369    @Override
370    public DisplayInfo getDisplayInfo() {
371        return mStack.getDisplayContent().getDisplayInfo();
372    }
373
374    @Override
375    public String toString() {
376        return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
377    }
378
379    @Override
380    public String toShortString() {
381        return "Task=" + mTaskId;
382    }
383
384    public void printTo(String prefix, PrintWriter pw) {
385        pw.print(prefix); pw.print("taskId="); pw.print(mTaskId);
386                pw.print(prefix); pw.print("appTokens="); pw.print(mAppTokens);
387                pw.print(prefix); pw.print("mdr="); pw.println(mDeferRemoval);
388    }
389}
390