Home | History | Annotate | Download | only in wm
      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 
     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 
     89     DisplayContent getDisplayContent() {
     90         return mDisplayContent;
     91     }
     92 
     93     ArrayList<Task> getTasks() {
     94         return mTasks;
     95     }
     96 
     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 
    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 
    136     void getBounds(Rect out) {
    137         out.set(mBounds);
    138     }
    139 
    140     void updateDisplayInfo() {
    141         if (mFullscreen && mDisplayContent != null) {
    142             mDisplayContent.getLogicalDisplayRect(mTmpRect);
    143             setBounds(mTmpRect);
    144         }
    145     }
    146 
    147     boolean isFullscreen() {
    148         return mFullscreen;
    149     }
    150 
    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      */
    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 
    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 
    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      */
    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 
    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 
    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 
    263     void resetAnimationBackgroundAnimator() {
    264         mAnimationBackgroundAnimator = null;
    265         mAnimationBackgroundSurface.hide();
    266     }
    267 
    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 
    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 
    317     void resetDimmingTag() {
    318         mDimmingTag = false;
    319     }
    320 
    321     void setDimmingTag() {
    322         mDimmingTag = true;
    323     }
    324 
    325     boolean testDimmingTag() {
    326         return mDimmingTag;
    327     }
    328 
    329     boolean isDimming() {
    330         return mDimLayer.isDimming();
    331     }
    332 
    333     boolean isDimming(WindowStateAnimator winAnimator) {
    334         return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
    335     }
    336 
    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 
    348     void stopDimmingIfNeeded() {
    349         if (!mDimmingTag && isDimming()) {
    350             mDimWinAnimator = null;
    351         }
    352     }
    353 
    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 
    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 
    377     void close() {
    378         mDimLayer.mDimSurface.destroy();
    379         mAnimationBackgroundSurface.mDimSurface.destroy();
    380     }
    381 
    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
    411     public String toString() {
    412         return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
    413     }
    414 }
    415