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.content.res.Configuration;
23 import android.graphics.Rect;
24 import android.os.Debug;
25 import android.util.DisplayMetrics;
26 import android.util.EventLog;
27 import android.util.Slog;
28 import android.util.TypedValue;
29 import android.view.Surface;
30 
31 import com.android.server.EventLogTags;
32 
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 
36 public class TaskStack {
37     /** Amount of time in milliseconds to animate the dim surface from one value to another,
38      * when no window animation is driving it. */
39     private static final int DEFAULT_DIM_DURATION = 200;
40 
41     /** Unique identifier */
42     final int mStackId;
43 
44     /** The service */
45     private final WindowManagerService mService;
46 
47     /** The display this stack sits under. */
48     private DisplayContent mDisplayContent;
49 
50     /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
51      * mTaskHistory in the ActivityStack with the same mStackId */
52     private final ArrayList<Task> mTasks = new ArrayList<Task>();
53 
54     /** For comparison with DisplayContent bounds. */
55     private Rect mTmpRect = new Rect();
56     /** For handling display rotations. */
57     private Rect mTmpRect2 = new Rect();
58 
59     /** Content limits relative to the DisplayContent this sits in. */
60     private Rect mBounds = new Rect();
61 
62     /** Whether mBounds is fullscreen */
63     private boolean mFullscreen = true;
64 
65     /** Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} */
66     private DimLayer mDimLayer;
67 
68     /** The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer. */
69     WindowStateAnimator mDimWinAnimator;
70 
71     /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
72     DimLayer mAnimationBackgroundSurface;
73 
74     /** The particular window with an Animation with non-zero background color. */
75     WindowStateAnimator mAnimationBackgroundAnimator;
76 
77     /** Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
78      * then stop any dimming. */
79     boolean mDimmingTag;
80 
81     /** Application tokens that are exiting, but still on screen for animations. */
82     final AppTokenList mExitingAppTokens = new AppTokenList();
83 
84     /** Detach this stack from its display when animation completes. */
85     boolean mDeferDetach;
86 
87     // Contains configurations settings that are different from the global configuration due to
88     // stack specific operations. E.g. {@link #setBounds}.
89     Configuration mOverrideConfig;
90     // True if the stack was forced to fullscreen disregarding the override configuration.
91     private boolean mForceFullscreen;
92     // The {@link #mBounds} before the stack was forced to fullscreen. Will be restored as the
93     // stack bounds once the stack is no longer forced to fullscreen.
94     final private Rect mPreForceFullscreenBounds;
95 
96     // Device rotation as of the last time {@link #mBounds} was set.
97     int mRotation;
98 
TaskStack(WindowManagerService service, int stackId)99     TaskStack(WindowManagerService service, int stackId) {
100         mService = service;
101         mStackId = stackId;
102         mOverrideConfig = Configuration.EMPTY;
103         mForceFullscreen = false;
104         mPreForceFullscreenBounds = new Rect();
105         // TODO: remove bounds from log, they are always 0.
106         EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
107                 mBounds.right, mBounds.bottom);
108     }
109 
getDisplayContent()110     DisplayContent getDisplayContent() {
111         return mDisplayContent;
112     }
113 
getTasks()114     ArrayList<Task> getTasks() {
115         return mTasks;
116     }
117 
resizeWindows()118     void resizeWindows() {
119         final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
120         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
121             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
122             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
123                 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
124                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
125                     final WindowState win = windows.get(winNdx);
126                     if (!resizingWindows.contains(win)) {
127                         if (WindowManagerService.DEBUG_RESIZE) Slog.d(TAG,
128                                 "setBounds: Resizing " + win);
129                         resizingWindows.add(win);
130                     }
131                 }
132             }
133         }
134     }
135 
136     /** Set the stack bounds. Passing in null sets the bounds to fullscreen. */
setBounds(Rect bounds)137     boolean setBounds(Rect bounds) {
138         boolean oldFullscreen = mFullscreen;
139         int rotation = Surface.ROTATION_0;
140         if (mDisplayContent != null) {
141             mDisplayContent.getLogicalDisplayRect(mTmpRect);
142             rotation = mDisplayContent.getDisplayInfo().rotation;
143             if (bounds == null) {
144                 bounds = mTmpRect;
145                 mFullscreen = true;
146             } else {
147                 // ensure bounds are entirely within the display rect
148                 if (!bounds.intersect(mTmpRect)) {
149                     // Can't set bounds outside the containing display.. Sorry!
150                     return false;
151                 }
152                 mFullscreen = mTmpRect.equals(bounds);
153             }
154         }
155 
156         if (bounds == null) {
157             // Can't set to fullscreen if we don't have a display to get bounds from...
158             return false;
159         }
160         if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
161             return false;
162         }
163 
164         mDimLayer.setBounds(bounds);
165         mAnimationBackgroundSurface.setBounds(bounds);
166         mBounds.set(bounds);
167         mRotation = rotation;
168         updateOverrideConfiguration();
169         return true;
170     }
171 
getBounds(Rect out)172     void getBounds(Rect out) {
173         out.set(mBounds);
174     }
175 
updateOverrideConfiguration()176     private void updateOverrideConfiguration() {
177         final Configuration serviceConfig = mService.mCurConfiguration;
178         if (mFullscreen) {
179             mOverrideConfig = Configuration.EMPTY;
180             return;
181         }
182 
183         if (mOverrideConfig == Configuration.EMPTY) {
184             mOverrideConfig  = new Configuration();
185         }
186 
187         // TODO(multidisplay): Update Dp to that of display stack is on.
188         final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
189         mOverrideConfig.screenWidthDp =
190                 Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
191         mOverrideConfig.screenHeightDp =
192                 Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
193         mOverrideConfig.smallestScreenWidthDp =
194                 Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
195         mOverrideConfig.orientation =
196                 (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
197                         ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
198     }
199 
updateDisplayInfo()200     void updateDisplayInfo() {
201         if (mFullscreen) {
202             setBounds(null);
203         } else if (mDisplayContent != null) {
204             final int newRotation = mDisplayContent.getDisplayInfo().rotation;
205             if (mRotation == newRotation) {
206                 return;
207             }
208 
209             // Device rotation changed. We don't want the stack to move around on the screen when
210             // this happens, so update the stack bounds so it stays in the same place.
211             final int rotationDelta = DisplayContent.deltaRotation(mRotation, newRotation);
212             mDisplayContent.getLogicalDisplayRect(mTmpRect);
213             switch (rotationDelta) {
214                 case Surface.ROTATION_0:
215                     mTmpRect2.set(mBounds);
216                     break;
217                 case Surface.ROTATION_90:
218                     mTmpRect2.top = mTmpRect.bottom - mBounds.right;
219                     mTmpRect2.left = mBounds.top;
220                     mTmpRect2.right = mTmpRect2.left + mBounds.height();
221                     mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
222                     break;
223                 case Surface.ROTATION_180:
224                     mTmpRect2.top = mTmpRect.bottom - mBounds.bottom;
225                     mTmpRect2.left = mTmpRect.right - mBounds.right;
226                     mTmpRect2.right = mTmpRect2.left + mBounds.width();
227                     mTmpRect2.bottom = mTmpRect2.top + mBounds.height();
228                     break;
229                 case Surface.ROTATION_270:
230                     mTmpRect2.top = mBounds.left;
231                     mTmpRect2.left = mTmpRect.right - mBounds.bottom;
232                     mTmpRect2.right = mTmpRect2.left + mBounds.height();
233                     mTmpRect2.bottom = mTmpRect2.top + mBounds.width();
234                     break;
235             }
236             setBounds(mTmpRect2);
237         }
238     }
239 
isFullscreen()240     boolean isFullscreen() {
241         return mFullscreen;
242     }
243 
244     /** Forces the stack to fullscreen if input is true, else un-forces the stack from fullscreen.
245      * Returns true if something happened.
246      */
forceFullscreen(boolean forceFullscreen)247     boolean forceFullscreen(boolean forceFullscreen) {
248         if (mForceFullscreen == forceFullscreen) {
249             return false;
250         }
251         mForceFullscreen = forceFullscreen;
252         if (forceFullscreen) {
253             if (mFullscreen) {
254                 return false;
255             }
256             mPreForceFullscreenBounds.set(mBounds);
257             return setBounds(null);
258         } else {
259             if (!mFullscreen || mPreForceFullscreenBounds.isEmpty()) {
260                 return false;
261             }
262             return setBounds(mPreForceFullscreenBounds);
263         }
264     }
265 
isAnimating()266     boolean isAnimating() {
267         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
268             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
269             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
270                 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
271                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
272                     final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
273                     if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
274                         return true;
275                     }
276                 }
277             }
278         }
279         return false;
280     }
281 
addTask(Task task, boolean toTop)282     void addTask(Task task, boolean toTop) {
283         addTask(task, toTop, task.showForAllUsers());
284     }
285 
286     /**
287      * Put a Task in this stack. Used for adding and moving.
288      * @param task The task to add.
289      * @param toTop Whether to add it to the top or bottom.
290      * @param showForAllUsers Whether to show the task regardless of the current user.
291      */
addTask(Task task, boolean toTop, boolean showForAllUsers)292     void addTask(Task task, boolean toTop, boolean showForAllUsers) {
293         int stackNdx;
294         if (!toTop) {
295             stackNdx = 0;
296         } else {
297             stackNdx = mTasks.size();
298             if (!showForAllUsers && !mService.isCurrentProfileLocked(task.mUserId)) {
299                 // Place the task below all current user tasks.
300                 while (--stackNdx >= 0) {
301                     final Task tmpTask = mTasks.get(stackNdx);
302                     if (!tmpTask.showForAllUsers()
303                             || !mService.isCurrentProfileLocked(tmpTask.mUserId)) {
304                         break;
305                     }
306                 }
307                 // Put it above first non-current user task.
308                 ++stackNdx;
309             }
310         }
311         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
312                 + " pos=" + stackNdx);
313         mTasks.add(stackNdx, task);
314 
315         task.mStack = this;
316         if (toTop) {
317             mDisplayContent.moveStack(this, true);
318         }
319         EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
320     }
321 
moveTaskToTop(Task task)322     void moveTaskToTop(Task task) {
323         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToTop: task=" + task + " Callers="
324                 + Debug.getCallers(6));
325         mTasks.remove(task);
326         addTask(task, true);
327     }
328 
moveTaskToBottom(Task task)329     void moveTaskToBottom(Task task) {
330         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "moveTaskToBottom: task=" + task);
331         mTasks.remove(task);
332         addTask(task, false);
333     }
334 
335     /**
336      * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
337      * back.
338      * @param task The Task to delete.
339      */
removeTask(Task task)340     void removeTask(Task task) {
341         if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "removeTask: task=" + task);
342         mTasks.remove(task);
343         if (mDisplayContent != null) {
344             if (mTasks.isEmpty()) {
345                 mDisplayContent.moveStack(this, false);
346             }
347             mDisplayContent.layoutNeeded = true;
348         }
349         for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
350             final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
351             if (wtoken.mTask == task) {
352                 wtoken.mIsExiting = false;
353                 mExitingAppTokens.remove(appNdx);
354             }
355         }
356     }
357 
attachDisplayContent(DisplayContent displayContent)358     void attachDisplayContent(DisplayContent displayContent) {
359         if (mDisplayContent != null) {
360             throw new IllegalStateException("attachDisplayContent: Already attached");
361         }
362 
363         mDisplayContent = displayContent;
364         mDimLayer = new DimLayer(mService, this, displayContent);
365         mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
366         updateDisplayInfo();
367     }
368 
detachDisplay()369     void detachDisplay() {
370         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
371 
372         boolean doAnotherLayoutPass = false;
373         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
374             final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
375             for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
376                 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
377                 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
378                     // We are in the middle of changing the state of displays/stacks/tasks. We need
379                     // to finish that, before we let layout interfere with it.
380                     mService.removeWindowInnerLocked(appWindows.get(winNdx),
381                             false /* performLayout */);
382                     doAnotherLayoutPass = true;
383                 }
384             }
385         }
386         if (doAnotherLayoutPass) {
387             mService.requestTraversalLocked();
388         }
389 
390         close();
391     }
392 
resetAnimationBackgroundAnimator()393     void resetAnimationBackgroundAnimator() {
394         mAnimationBackgroundAnimator = null;
395         mAnimationBackgroundSurface.hide();
396     }
397 
getDimBehindFadeDuration(long duration)398     private long getDimBehindFadeDuration(long duration) {
399         TypedValue tv = new TypedValue();
400         mService.mContext.getResources().getValue(
401                 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
402         if (tv.type == TypedValue.TYPE_FRACTION) {
403             duration = (long)tv.getFraction(duration, duration);
404         } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
405             duration = tv.data;
406         }
407         return duration;
408     }
409 
animateDimLayers()410     boolean animateDimLayers() {
411         final int dimLayer;
412         final float dimAmount;
413         if (mDimWinAnimator == null) {
414             dimLayer = mDimLayer.getLayer();
415             dimAmount = 0;
416         } else {
417             dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
418             dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
419         }
420         final float targetAlpha = mDimLayer.getTargetAlpha();
421         if (targetAlpha != dimAmount) {
422             if (mDimWinAnimator == null) {
423                 mDimLayer.hide(DEFAULT_DIM_DURATION);
424             } else {
425                 long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
426                         ? mDimWinAnimator.mAnimation.computeDurationHint()
427                         : DEFAULT_DIM_DURATION;
428                 if (targetAlpha > dimAmount) {
429                     duration = getDimBehindFadeDuration(duration);
430                 }
431                 mDimLayer.show(dimLayer, dimAmount, duration);
432             }
433         } else if (mDimLayer.getLayer() != dimLayer) {
434             mDimLayer.setLayer(dimLayer);
435         }
436         if (mDimLayer.isAnimating()) {
437             if (!mService.okToDisplay()) {
438                 // Jump to the end of the animation.
439                 mDimLayer.show();
440             } else {
441                 return mDimLayer.stepAnimation();
442             }
443         }
444         return false;
445     }
446 
resetDimmingTag()447     void resetDimmingTag() {
448         mDimmingTag = false;
449     }
450 
setDimmingTag()451     void setDimmingTag() {
452         mDimmingTag = true;
453     }
454 
testDimmingTag()455     boolean testDimmingTag() {
456         return mDimmingTag;
457     }
458 
isDimming()459     boolean isDimming() {
460         return mDimLayer.isDimming();
461     }
462 
isDimming(WindowStateAnimator winAnimator)463     boolean isDimming(WindowStateAnimator winAnimator) {
464         return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
465     }
466 
startDimmingIfNeeded(WindowStateAnimator newWinAnimator)467     void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
468         // Only set dim params on the highest dimmed layer.
469         // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
470         if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
471                 || !mDimWinAnimator.mSurfaceShown
472                 || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
473             mDimWinAnimator = newWinAnimator;
474             if (mDimWinAnimator.mWin.mAppToken == null
475                     && !mFullscreen && mDisplayContent != null) {
476                 // Dim should cover the entire screen for system windows.
477                 mDisplayContent.getLogicalDisplayRect(mTmpRect);
478                 mDimLayer.setBounds(mTmpRect);
479             }
480         }
481     }
482 
stopDimmingIfNeeded()483     void stopDimmingIfNeeded() {
484         if (!mDimmingTag && isDimming()) {
485             mDimWinAnimator = null;
486             mDimLayer.setBounds(mBounds);
487         }
488     }
489 
setAnimationBackground(WindowStateAnimator winAnimator, int color)490     void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
491         int animLayer = winAnimator.mAnimLayer;
492         if (mAnimationBackgroundAnimator == null
493                 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
494             mAnimationBackgroundAnimator = winAnimator;
495             animLayer = mService.adjustAnimationBackground(winAnimator);
496             mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
497                     ((color >> 24) & 0xff) / 255f, 0);
498         }
499     }
500 
switchUser()501     void switchUser() {
502         int top = mTasks.size();
503         for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
504             Task task = mTasks.get(taskNdx);
505             if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
506                 mTasks.remove(taskNdx);
507                 mTasks.add(task);
508                 --top;
509             }
510         }
511     }
512 
close()513     void close() {
514         if (mAnimationBackgroundSurface != null) {
515             mAnimationBackgroundSurface.destroySurface();
516             mAnimationBackgroundSurface = null;
517         }
518         if (mDimLayer != null) {
519             mDimLayer.destroySurface();
520             mDimLayer = null;
521         }
522         mDisplayContent = null;
523     }
524 
dump(String prefix, PrintWriter pw)525     public void dump(String prefix, PrintWriter pw) {
526         pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
527         pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
528         for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
529             pw.print(prefix); pw.println(mTasks.get(taskNdx));
530         }
531         if (mAnimationBackgroundSurface.isDimming()) {
532             pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
533             mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
534         }
535         if (mDimLayer.isDimming()) {
536             pw.print(prefix); pw.println("mDimLayer:");
537             mDimLayer.printTo(prefix + " ", pw);
538             pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
539         }
540         if (!mExitingAppTokens.isEmpty()) {
541             pw.println();
542             pw.println("  Exiting application tokens:");
543             for (int i=mExitingAppTokens.size()-1; i>=0; i--) {
544                 WindowToken token = mExitingAppTokens.get(i);
545                 pw.print("  Exiting App #"); pw.print(i);
546                 pw.print(' '); pw.print(token);
547                 pw.println(':');
548                 token.dump(pw, "    ");
549             }
550         }
551     }
552 
553     @Override
toString()554     public String toString() {
555         return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
556     }
557 }
558