1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
20 import static com.android.server.wm.WindowManagerService.TAG;
21 
22 import android.graphics.Rect;
23 import android.os.Debug;
24 import android.util.EventLog;
25 import android.util.Slog;
26 import android.util.TypedValue;
27 import com.android.server.EventLogTags;
28 
29 import java.io.PrintWriter;
30 import java.util.ArrayList;
31 
32 public class TaskStack {
33     /** Amount of time in milliseconds to animate the dim surface from one value to another,
34      * when no window animation is driving it. */
35     private static final int DEFAULT_DIM_DURATION = 200;
36 
37     /** Unique identifier */
38     final int mStackId;
39 
40     /** The service */
41     private final WindowManagerService mService;
42 
43     /** The display this stack sits under. */
44     private DisplayContent mDisplayContent;
45 
46     /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
47      * mTaskHistory in the ActivityStack with the same mStackId */
48     private final ArrayList<Task> mTasks = new ArrayList<Task>();
49 
50     /** For comparison with DisplayContent bounds. */
51     private Rect mTmpRect = new Rect();
52 
53     /** Content limits relative to the DisplayContent this sits in. */
54     private Rect mBounds = new Rect();
55 
56     /** Whether mBounds is fullscreen */
57     private boolean mFullscreen = true;
58 
59     /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
60     private DimLayer mDimLayer;
61 
62     /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
63     WindowStateAnimator mDimWinAnimator;
64 
65     /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
66     DimLayer mAnimationBackgroundSurface;
67 
68     /** The particular window with an Animation with non-zero background color. */
69     WindowStateAnimator mAnimationBackgroundAnimator;
70 
71     /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
72      * then stop any dimming. */
73     boolean mDimmingTag;
74 
75     /** Application tokens that are exiting, but still on screen for animations. */
76     final AppTokenList mExitingAppTokens = new AppTokenList();
77 
78     /** Detach this stack from its display when animation completes. */
79     boolean mDeferDetach;
80 
TaskStack(WindowManagerService service, int stackId)81     TaskStack(WindowManagerService service, int stackId) {
82         mService = service;
83         mStackId = stackId;
84         // TODO: remove bounds from log, they are always 0.
85         EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
86                 mBounds.right, mBounds.bottom);
87     }
88 
getDisplayContent()89     DisplayContent getDisplayContent() {
90         return mDisplayContent;
91     }
92 
getTasks()93     ArrayList<Task> getTasks() {
94         return mTasks;
95     }
96 
resizeWindows()97     void resizeWindows() {
98         final boolean underStatusBar = mBounds.top == 0;
99 
100         final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
101         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
102             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
103             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
104                 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
105                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
106                     final WindowState win = windows.get(winNdx);
107                     if (!resizingWindows.contains(win)) {
108                         if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
109                                 "setBounds: Resizing " + win);
110                         resizingWindows.add(win);
111                     }
112                     win.mUnderStatusBar = underStatusBar;
113                 }
114             }
115         }
116     }
117 
setBounds(Rect bounds)118     boolean setBounds(Rect bounds) {
119         boolean oldFullscreen = mFullscreen;
120         if (mDisplayContent != null) {
121             mDisplayContent.getLogicalDisplayRect(mTmpRect);
122             mFullscreen = mTmpRect.equals(bounds);
123         }
124 
125         if (mBounds.equals(bounds) && oldFullscreen == mFullscreen) {
126             return false;
127         }
128 
129         mDimLayer.setBounds(bounds);
130         mAnimationBackgroundSurface.setBounds(bounds);
131         mBounds.set(bounds);
132 
133         return true;
134     }
135 
getBounds(Rect out)136     void getBounds(Rect out) {
137         out.set(mBounds);
138     }
139 
updateDisplayInfo()140     void updateDisplayInfo() {
141         if (mFullscreen && mDisplayContent != null) {
142             mDisplayContent.getLogicalDisplayRect(mTmpRect);
143             setBounds(mTmpRect);
144         }
145     }
146 
isFullscreen()147     boolean isFullscreen() {
148         return mFullscreen;
149     }
150 
isAnimating()151     boolean isAnimating() {
152         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
153             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
154             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
155                 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
156                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
157                     final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
158                     if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
159                         return true;
160                     }
161                 }
162             }
163         }
164         return false;
165     }
166 
167     /**
168      * Put a Task in this stack. Used for adding and moving.
169      * @param task The task to add.
170      * @param toTop Whether to add it to the top or bottom.
171      */
addTask(Task task, boolean toTop)172     void addTask(Task task, boolean toTop) {
173         int stackNdx;
174         if (!toTop) {
175             stackNdx = 0;
176         } else {
177             stackNdx = mTasks.size();
178             if (!mService.isCurrentProfileLocked(task.mUserId)) {
179                 // Place the task below all current user tasks.
180                 while (--stackNdx >= 0) {
181                     if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
182                         break;
183                     }
184                 }
185                 // Put it above first non-current user task.
186                 ++stackNdx;
187             }
188         }
189         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
190                 + " pos=" + stackNdx);
191         mTasks.add(stackNdx, task);
192 
193         task.mStack = this;
194         mDisplayContent.moveStack(this, true);
195         EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx);
196     }
197 
moveTaskToTop(Task task)198     void moveTaskToTop(Task task) {
199         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
200                 + Debug.getCallers(6));
201         mTasks.remove(task);
202         addTask(task, true);
203     }
204 
moveTaskToBottom(Task task)205     void moveTaskToBottom(Task task) {
206         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
207         mTasks.remove(task);
208         addTask(task, false);
209     }
210 
211     /**
212      * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
213      * back.
214      * @param task The Task to delete.
215      */
removeTask(Task task)216     void removeTask(Task task) {
217         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
218         mTasks.remove(task);
219         if (mDisplayContent != null) {
220             if (mTasks.isEmpty()) {
221                 mDisplayContent.moveStack(this, false);
222             }
223             mDisplayContent.layoutNeeded = true;
224         }
225     }
226 
attachDisplayContent(DisplayContent displayContent)227     void attachDisplayContent(DisplayContent displayContent) {
228         if (mDisplayContent != null) {
229             throw new IllegalStateException("attachDisplayContent: Already attached");
230         }
231 
232         mDisplayContent = displayContent;
233         mDimLayer = new DimLayer(mService, this, displayContent);
234         mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
235         updateDisplayInfo();
236     }
237 
detachDisplay()238     void detachDisplay() {
239         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
240 
241         boolean doAnotherLayoutPass = false;
242         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
243             final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
244             for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
245                 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
246                 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
247                     mService.removeWindowInnerLocked(null, appWindows.get(winNdx));
248                     doAnotherLayoutPass = true;
249                 }
250             }
251         }
252         if (doAnotherLayoutPass) {
253             mService.requestTraversalLocked();
254         }
255 
256         mAnimationBackgroundSurface.destroySurface();
257         mAnimationBackgroundSurface = null;
258         mDimLayer.destroySurface();
259         mDimLayer = null;
260         mDisplayContent = null;
261     }
262 
resetAnimationBackgroundAnimator()263     void resetAnimationBackgroundAnimator() {
264         mAnimationBackgroundAnimator = null;
265         mAnimationBackgroundSurface.hide();
266     }
267 
getDimBehindFadeDuration(long duration)268     private long getDimBehindFadeDuration(long duration) {
269         TypedValue tv = new TypedValue();
270         mService.mContext.getResources().getValue(
271                 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
272         if (tv.type == TypedValue.TYPE_FRACTION) {
273             duration = (long)tv.getFraction(duration, duration);
274         } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
275             duration = tv.data;
276         }
277         return duration;
278     }
279 
animateDimLayers()280     boolean animateDimLayers() {
281         final int dimLayer;
282         final float dimAmount;
283         if (mDimWinAnimator == null) {
284             dimLayer = mDimLayer.getLayer();
285             dimAmount = 0;
286         } else {
287             dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
288             dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
289         }
290         final float targetAlpha = mDimLayer.getTargetAlpha();
291         if (targetAlpha != dimAmount) {
292             if (mDimWinAnimator == null) {
293                 mDimLayer.hide(DEFAULT_DIM_DURATION);
294             } else {
295                 long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
296                         ? mDimWinAnimator.mAnimation.computeDurationHint()
297                         : DEFAULT_DIM_DURATION;
298                 if (targetAlpha > dimAmount) {
299                     duration = getDimBehindFadeDuration(duration);
300                 }
301                 mDimLayer.show(dimLayer, dimAmount, duration);
302             }
303         } else if (mDimLayer.getLayer() != dimLayer) {
304             mDimLayer.setLayer(dimLayer);
305         }
306         if (mDimLayer.isAnimating()) {
307             if (!mService.okToDisplay()) {
308                 // Jump to the end of the animation.
309                 mDimLayer.show();
310             } else {
311                 return mDimLayer.stepAnimation();
312             }
313         }
314         return false;
315     }
316 
resetDimmingTag()317     void resetDimmingTag() {
318         mDimmingTag = false;
319     }
320 
setDimmingTag()321     void setDimmingTag() {
322         mDimmingTag = true;
323     }
324 
testDimmingTag()325     boolean testDimmingTag() {
326         return mDimmingTag;
327     }
328 
isDimming()329     boolean isDimming() {
330         return mDimLayer.isDimming();
331     }
332 
isDimming(WindowStateAnimator winAnimator)333     boolean isDimming(WindowStateAnimator winAnimator) {
334         return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
335     }
336 
startDimmingIfNeeded(WindowStateAnimator newWinAnimator)337     void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
338         // Only set dim params on the highest dimmed layer.
339         final WindowStateAnimator existingDimWinAnimator = mDimWinAnimator;
340         // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
341         if (newWinAnimator.mSurfaceShown && (existingDimWinAnimator == null
342                 || !existingDimWinAnimator.mSurfaceShown
343                 || existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
344             mDimWinAnimator = newWinAnimator;
345         }
346     }
347 
stopDimmingIfNeeded()348     void stopDimmingIfNeeded() {
349         if (!mDimmingTag && isDimming()) {
350             mDimWinAnimator = null;
351         }
352     }
353 
setAnimationBackground(WindowStateAnimator winAnimator, int color)354     void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
355         int animLayer = winAnimator.mAnimLayer;
356         if (mAnimationBackgroundAnimator == null
357                 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
358             mAnimationBackgroundAnimator = winAnimator;
359             animLayer = mService.adjustAnimationBackground(winAnimator);
360             mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
361                     ((color >> 24) & 0xff) / 255f, 0);
362         }
363     }
364 
switchUser(int userId)365     void switchUser(int userId) {
366         int top = mTasks.size();
367         for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
368             Task task = mTasks.get(taskNdx);
369             if (mService.isCurrentProfileLocked(task.mUserId)) {
370                 mTasks.remove(taskNdx);
371                 mTasks.add(task);
372                 --top;
373             }
374         }
375     }
376 
close()377     void close() {
378         mDimLayer.mDimSurface.destroy();
379         mAnimationBackgroundSurface.mDimSurface.destroy();
380     }
381 
dump(String prefix, PrintWriter pw)382     public void dump(String prefix, PrintWriter pw) {
383         pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
384         pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
385         for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
386             pw.print(prefix); pw.println(mTasks.get(taskNdx));
387         }
388         if (mAnimationBackgroundSurface.isDimming()) {
389             pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
390             mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
391         }
392         if (mDimLayer.isDimming()) {
393             pw.print(prefix); pw.println("mDimLayer:");
394             mDimLayer.printTo(prefix, pw);
395             pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
396         }
397         if (!mExitingAppTokens.isEmpty()) {
398             pw.println();
399             pw.println("  Exiting application tokens:");
400             for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
401                 WindowToken token = mExitingAppTokens.get(i);
402                 pw.print("  Exiting App #"); pw.print(i);
403                 pw.print(' '); pw.print(token);
404                 pw.println(':');
405                 token.dump(pw, "    ");
406             }
407         }
408     }
409 
410     @Override
toString()411     public String toString() {
412         return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
413     }
414 }
415