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.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 
     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 
    110     DisplayContent getDisplayContent() {
    111         return mDisplayContent;
    112     }
    113 
    114     ArrayList<Task> getTasks() {
    115         return mTasks;
    116     }
    117 
    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. */
    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 
    172     void getBounds(Rect out) {
    173         out.set(mBounds);
    174     }
    175 
    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 
    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 
    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      */
    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 
    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 
    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      */
    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 
    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 
    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      */
    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 
    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 
    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 
    393     void resetAnimationBackgroundAnimator() {
    394         mAnimationBackgroundAnimator = null;
    395         mAnimationBackgroundSurface.hide();
    396     }
    397 
    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 
    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 
    447     void resetDimmingTag() {
    448         mDimmingTag = false;
    449     }
    450 
    451     void setDimmingTag() {
    452         mDimmingTag = true;
    453     }
    454 
    455     boolean testDimmingTag() {
    456         return mDimmingTag;
    457     }
    458 
    459     boolean isDimming() {
    460         return mDimLayer.isDimming();
    461     }
    462 
    463     boolean isDimming(WindowStateAnimator winAnimator) {
    464         return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
    465     }
    466 
    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 
    483     void stopDimmingIfNeeded() {
    484         if (!mDimmingTag && isDimming()) {
    485             mDimWinAnimator = null;
    486             mDimLayer.setBounds(mBounds);
    487         }
    488     }
    489 
    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 
    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 
    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 
    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
    554     public String toString() {
    555         return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
    556     }
    557 }
    558