Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2014 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 android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
     20 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
     21 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
     22 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
     23 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
     24 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
     25 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
     26 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
     27 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
     28 import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
     29 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
     30 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
     31 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
     32 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
     33 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
     34 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
     35 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
     36 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
     37 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
     38 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
     39 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     40 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
     41 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
     42 import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
     43 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
     44 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
     45 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
     46 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
     47 
     48 import android.content.Context;
     49 import android.os.Trace;
     50 import android.util.Slog;
     51 import android.util.SparseArray;
     52 import android.util.TimeUtils;
     53 import android.view.Choreographer;
     54 import android.view.Display;
     55 import android.view.SurfaceControl;
     56 import android.view.WindowManagerPolicy;
     57 import android.view.animation.AlphaAnimation;
     58 import android.view.animation.Animation;
     59 
     60 import java.io.PrintWriter;
     61 import java.util.ArrayList;
     62 
     63 /**
     64  * Singleton class that carries out the animations and Surface operations in a separate task
     65  * on behalf of WindowManagerService.
     66  */
     67 public class WindowAnimator {
     68     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowAnimator" : TAG_WM;
     69 
     70     /** How long to give statusbar to clear the private keyguard flag when animating out */
     71     private static final long KEYGUARD_ANIM_TIMEOUT_MS = 1000;
     72 
     73     final WindowManagerService mService;
     74     final Context mContext;
     75     final WindowManagerPolicy mPolicy;
     76     private final WindowSurfacePlacer mWindowPlacerLocked;
     77 
     78     /** Is any window animating? */
     79     private boolean mAnimating;
     80 
     81     /** Is any app window animating? */
     82     boolean mAppWindowAnimating;
     83 
     84     final Choreographer.FrameCallback mAnimationFrameCallback;
     85 
     86     /** Time of current animation step. Reset on each iteration */
     87     long mCurrentTime;
     88 
     89     /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
     90      * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
     91     private int mAnimTransactionSequence;
     92 
     93     /** Window currently running an animation that has requested it be detached
     94      * from the wallpaper.  This means we need to ensure the wallpaper is
     95      * visible behind it in case it animates in a way that would allow it to be
     96      * seen. If multiple windows satisfy this, use the lowest window. */
     97     WindowState mWindowDetachedWallpaper = null;
     98 
     99     int mBulkUpdateParams = 0;
    100     Object mLastWindowFreezeSource;
    101 
    102     SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2);
    103 
    104     boolean mInitialized = false;
    105 
    106     boolean mKeyguardGoingAway;
    107     int mKeyguardGoingAwayFlags;
    108 
    109     /** Use one animation for all entering activities after keyguard is dismissed. */
    110     Animation mPostKeyguardExitAnimation;
    111 
    112     // forceHiding states.
    113     static final int KEYGUARD_NOT_SHOWN     = 0;
    114     static final int KEYGUARD_SHOWN         = 1;
    115     static final int KEYGUARD_ANIMATING_OUT = 2;
    116     int mForceHiding = KEYGUARD_NOT_SHOWN;
    117 
    118     // When set to true the animator will go over all windows after an animation frame is posted and
    119     // check if some got replaced and can be removed.
    120     private boolean mRemoveReplacedWindows = false;
    121 
    122     private final AppTokenList mTmpExitingAppTokens = new AppTokenList();
    123 
    124     private String forceHidingToString() {
    125         switch (mForceHiding) {
    126             case KEYGUARD_NOT_SHOWN:    return "KEYGUARD_NOT_SHOWN";
    127             case KEYGUARD_SHOWN:        return "KEYGUARD_SHOWN";
    128             case KEYGUARD_ANIMATING_OUT:return "KEYGUARD_ANIMATING_OUT";
    129             default: return "KEYGUARD STATE UNKNOWN " + mForceHiding;
    130         }
    131     }
    132 
    133     WindowAnimator(final WindowManagerService service) {
    134         mService = service;
    135         mContext = service.mContext;
    136         mPolicy = service.mPolicy;
    137         mWindowPlacerLocked = service.mWindowPlacerLocked;
    138 
    139         mAnimationFrameCallback = new Choreographer.FrameCallback() {
    140             public void doFrame(long frameTimeNs) {
    141                 synchronized (mService.mWindowMap) {
    142                     mService.mAnimationScheduled = false;
    143                     animateLocked(frameTimeNs);
    144                 }
    145             }
    146         };
    147     }
    148 
    149     void addDisplayLocked(final int displayId) {
    150         // Create the DisplayContentsAnimator object by retrieving it.
    151         getDisplayContentsAnimatorLocked(displayId);
    152         if (displayId == Display.DEFAULT_DISPLAY) {
    153             mInitialized = true;
    154         }
    155     }
    156 
    157     void removeDisplayLocked(final int displayId) {
    158         final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
    159         if (displayAnimator != null) {
    160             if (displayAnimator.mScreenRotationAnimation != null) {
    161                 displayAnimator.mScreenRotationAnimation.kill();
    162                 displayAnimator.mScreenRotationAnimation = null;
    163             }
    164         }
    165 
    166         mDisplayContentsAnimators.delete(displayId);
    167     }
    168 
    169     private void updateAppWindowsLocked(int displayId) {
    170         ArrayList<TaskStack> stacks = mService.getDisplayContentLocked(displayId).getStacks();
    171         for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
    172             final TaskStack stack = stacks.get(stackNdx);
    173             final ArrayList<Task> tasks = stack.getTasks();
    174             for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
    175                 final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
    176                 for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
    177                     final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator;
    178                     appAnimator.wasAnimating = appAnimator.animating;
    179                     if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
    180                         appAnimator.animating = true;
    181                         setAnimating(true);
    182                         mAppWindowAnimating = true;
    183                     } else if (appAnimator.wasAnimating) {
    184                         // stopped animating, do one more pass through the layout
    185                         setAppLayoutChanges(appAnimator,
    186                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
    187                                 "appToken " + appAnimator.mAppToken + " done", displayId);
    188                         if (DEBUG_ANIM) Slog.v(TAG,
    189                                 "updateWindowsApps...: done animating " + appAnimator.mAppToken);
    190                     }
    191                 }
    192             }
    193 
    194             mTmpExitingAppTokens.clear();
    195             mTmpExitingAppTokens.addAll(stack.mExitingAppTokens);
    196 
    197             final int exitingCount = mTmpExitingAppTokens.size();
    198             for (int i = 0; i < exitingCount; i++) {
    199                 final AppWindowAnimator appAnimator = mTmpExitingAppTokens.get(i).mAppAnimator;
    200                 // stepAnimation can trigger finishExit->removeWindowInnerLocked
    201                 // ->performSurfacePlacement
    202                 // performSurfacePlacement will directly manipulate the mExitingAppTokens list
    203                 // so we need to iterate over a copy and check for modifications.
    204                 if (!stack.mExitingAppTokens.contains(appAnimator)) {
    205                     continue;
    206                 }
    207                 appAnimator.wasAnimating = appAnimator.animating;
    208                 if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
    209                     setAnimating(true);
    210                     mAppWindowAnimating = true;
    211                 } else if (appAnimator.wasAnimating) {
    212                     // stopped animating, do one more pass through the layout
    213                     setAppLayoutChanges(appAnimator,
    214                             WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
    215                             "exiting appToken " + appAnimator.mAppToken + " done", displayId);
    216                     if (DEBUG_ANIM) Slog.v(TAG,
    217                             "updateWindowsApps...: done animating exiting "
    218                                     + appAnimator.mAppToken);
    219                 }
    220             }
    221         }
    222     }
    223 
    224     private boolean shouldForceHide(WindowState win) {
    225         final WindowState imeTarget = mService.mInputMethodTarget;
    226         final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() &&
    227                 ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
    228                         || !mPolicy.canBeForceHidden(imeTarget, imeTarget.mAttrs));
    229 
    230         final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
    231         final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
    232                 null : winShowWhenLocked.mAppToken;
    233 
    234         boolean allowWhenLocked = false;
    235         // Show IME over the keyguard if the target allows it
    236         allowWhenLocked |= (win.mIsImWindow || imeTarget == win) && showImeOverKeyguard;
    237         // Show SHOW_WHEN_LOCKED windows that turn on the screen
    238         allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.mTurnOnScreen;
    239 
    240         if (appShowWhenLocked != null) {
    241             allowWhenLocked |= appShowWhenLocked == win.mAppToken
    242                     // Show all SHOW_WHEN_LOCKED windows if some apps are shown over lockscreen
    243                     || (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
    244                     // Show error dialogs over apps that dismiss keyguard.
    245                     || (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
    246         }
    247 
    248         // Allow showing a window that dismisses Keyguard if the policy allows it. This is used for
    249         // when the policy knows that the Keyguard can be dismissed without user interaction to
    250         // provide a smooth transition in that case.
    251         allowWhenLocked |= (win.mAttrs.flags & FLAG_DISMISS_KEYGUARD) != 0
    252                 && mPolicy.canShowDismissingWindowWhileLockedLw();
    253 
    254         // Only hide windows if the keyguard is active and not animating away.
    255         boolean keyguardOn = mPolicy.isKeyguardShowingOrOccluded()
    256                 && mForceHiding != KEYGUARD_ANIMATING_OUT;
    257         boolean hideDockDivider = win.mAttrs.type == TYPE_DOCK_DIVIDER
    258                 && win.getDisplayContent().getDockedStackLocked() == null;
    259         return keyguardOn && !allowWhenLocked && (win.getDisplayId() == Display.DEFAULT_DISPLAY)
    260                 || hideDockDivider;
    261     }
    262 
    263     private void updateWindowsLocked(final int displayId) {
    264         ++mAnimTransactionSequence;
    265 
    266         final WindowList windows = mService.getWindowListLocked(displayId);
    267 
    268         final boolean keyguardGoingAwayToShade =
    269                 (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0;
    270         final boolean keyguardGoingAwayNoAnimation =
    271                 (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0;
    272         final boolean keyguardGoingAwayWithWallpaper =
    273                 (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0;
    274 
    275         if (mKeyguardGoingAway) {
    276             for (int i = windows.size() - 1; i >= 0; i--) {
    277                 WindowState win = windows.get(i);
    278                 if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) {
    279                     continue;
    280                 }
    281                 final WindowStateAnimator winAnimator = win.mWinAnimator;
    282                 if ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
    283                     if (!winAnimator.mAnimating) {
    284                         if (DEBUG_KEYGUARD) Slog.d(TAG,
    285                                 "updateWindowsLocked: creating delay animation");
    286 
    287                         // Create a new animation to delay until keyguard is gone on its own.
    288                         winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
    289                         winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
    290                         winAnimator.mAnimationIsEntrance = false;
    291                         winAnimator.mAnimationStartTime = -1;
    292                         winAnimator.mKeyguardGoingAwayAnimation = true;
    293                         winAnimator.mKeyguardGoingAwayWithWallpaper
    294                                 = keyguardGoingAwayWithWallpaper;
    295                     }
    296                 } else {
    297                     if (DEBUG_KEYGUARD) Slog.d(TAG,
    298                             "updateWindowsLocked: StatusBar is no longer keyguard");
    299                     mKeyguardGoingAway = false;
    300                     winAnimator.clearAnimation();
    301                 }
    302                 break;
    303             }
    304         }
    305 
    306         mForceHiding = KEYGUARD_NOT_SHOWN;
    307 
    308         boolean wallpaperInUnForceHiding = false;
    309         boolean startingInUnForceHiding = false;
    310         ArrayList<WindowStateAnimator> unForceHiding = null;
    311         WindowState wallpaper = null;
    312         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
    313         for (int i = windows.size() - 1; i >= 0; i--) {
    314             WindowState win = windows.get(i);
    315             WindowStateAnimator winAnimator = win.mWinAnimator;
    316             final int flags = win.mAttrs.flags;
    317             boolean canBeForceHidden = mPolicy.canBeForceHidden(win, win.mAttrs);
    318             boolean shouldBeForceHidden = shouldForceHide(win);
    319             if (winAnimator.hasSurface()) {
    320                 final boolean wasAnimating = winAnimator.mWasAnimating;
    321                 final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
    322                 winAnimator.mWasAnimating = nowAnimating;
    323                 orAnimating(nowAnimating);
    324 
    325                 if (DEBUG_WALLPAPER) {
    326                     Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
    327                             ", nowAnimating=" + nowAnimating);
    328                 }
    329 
    330                 if (wasAnimating && !winAnimator.mAnimating
    331                         && wallpaperController.isWallpaperTarget(win)) {
    332                     mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
    333                     setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
    334                             WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
    335                     if (DEBUG_LAYOUT_REPEATS) {
    336                         mWindowPlacerLocked.debugLayoutRepeats(
    337                                 "updateWindowsAndWallpaperLocked 2",
    338                                 getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
    339                     }
    340                 }
    341 
    342                 if (mPolicy.isForceHiding(win.mAttrs)) {
    343                     if (!wasAnimating && nowAnimating) {
    344                         if (DEBUG_KEYGUARD || DEBUG_ANIM ||
    345                                 DEBUG_VISIBILITY) Slog.v(TAG,
    346                                 "Animation started that could impact force hide: " + win);
    347                         mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
    348                         setPendingLayoutChanges(displayId,
    349                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
    350                         if (DEBUG_LAYOUT_REPEATS) {
    351                             mWindowPlacerLocked.debugLayoutRepeats(
    352                                     "updateWindowsAndWallpaperLocked 3",
    353                                     getPendingLayoutChanges(displayId));
    354                         }
    355                         mService.mFocusMayChange = true;
    356                     } else if (mKeyguardGoingAway && !nowAnimating) {
    357                         // Timeout!!
    358                         Slog.e(TAG, "Timeout waiting for animation to startup");
    359                         mPolicy.startKeyguardExitAnimation(0, 0);
    360                         mKeyguardGoingAway = false;
    361                     }
    362                     if (win.isReadyForDisplay()) {
    363                         if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {
    364                             mForceHiding = KEYGUARD_ANIMATING_OUT;
    365                         } else {
    366                             mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
    367                         }
    368                     }
    369                     if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
    370                             "Force hide " + forceHidingToString()
    371                             + " hasSurface=" + win.mHasSurface
    372                             + " policyVis=" + win.mPolicyVisibility
    373                             + " destroying=" + win.mDestroying
    374                             + " attHidden=" + win.mAttachedHidden
    375                             + " vis=" + win.mViewVisibility
    376                             + " hidden=" + win.mRootToken.hidden
    377                             + " anim=" + win.mWinAnimator.mAnimation);
    378                 } else if (canBeForceHidden) {
    379                     if (shouldBeForceHidden) {
    380                         if (!win.hideLw(false, false)) {
    381                             // Was already hidden
    382                             continue;
    383                         }
    384                         if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
    385                                 "Now policy hidden: " + win);
    386                     } else {
    387                         boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null
    388                                 && !mPostKeyguardExitAnimation.hasEnded()
    389                                 && !winAnimator.mKeyguardGoingAwayAnimation
    390                                 && win.hasDrawnLw()
    391                                 && win.mAttachedWindow == null
    392                                 && !win.mIsImWindow
    393                                 && displayId == Display.DEFAULT_DISPLAY;
    394 
    395                         // If the window is already showing and we don't need to apply an existing
    396                         // Keyguard exit animation, skip.
    397                         if (!win.showLw(false, false) && !applyExistingExitAnimation) {
    398                             continue;
    399                         }
    400                         final boolean visibleNow = win.isVisibleNow();
    401                         if (!visibleNow) {
    402                             // Couldn't really show, must showLw() again when win becomes visible.
    403                             win.hideLw(false, false);
    404                             continue;
    405                         }
    406                         if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
    407                                 "Now policy shown: " + win);
    408                         if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
    409                                 && win.mAttachedWindow == null) {
    410                             if (unForceHiding == null) {
    411                                 unForceHiding = new ArrayList<>();
    412                             }
    413                             unForceHiding.add(winAnimator);
    414                             if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
    415                                 wallpaperInUnForceHiding = true;
    416                             }
    417                             if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
    418                                 startingInUnForceHiding = true;
    419                             }
    420                         } else if (applyExistingExitAnimation) {
    421                             // We're already in the middle of an animation. Use the existing
    422                             // animation to bring in this window.
    423                             if (DEBUG_KEYGUARD) Slog.v(TAG,
    424                                     "Applying existing Keyguard exit animation to new window: win="
    425                                             + win);
    426 
    427                             Animation a = mPolicy.createForceHideEnterAnimation(false,
    428                                     keyguardGoingAwayToShade);
    429                             winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime(),
    430                                     STACK_CLIP_BEFORE_ANIM);
    431                             winAnimator.mKeyguardGoingAwayAnimation = true;
    432                             winAnimator.mKeyguardGoingAwayWithWallpaper
    433                                     = keyguardGoingAwayWithWallpaper;
    434                         }
    435                         final WindowState currentFocus = mService.mCurrentFocus;
    436                         if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
    437                             // We are showing on top of the current
    438                             // focus, so re-evaluate focus to make
    439                             // sure it is correct.
    440                             if (DEBUG_FOCUS_LIGHT) Slog.v(TAG,
    441                                     "updateWindowsLocked: setting mFocusMayChange true");
    442                             mService.mFocusMayChange = true;
    443                         }
    444                     }
    445                     if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
    446                         mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
    447                         setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
    448                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
    449                         if (DEBUG_LAYOUT_REPEATS) {
    450                             mWindowPlacerLocked.debugLayoutRepeats(
    451                                     "updateWindowsAndWallpaperLocked 4",
    452                                     getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
    453                         }
    454                     }
    455                 }
    456             }
    457 
    458             // If the window doesn't have a surface, the only thing we care about is the correct
    459             // policy visibility.
    460             else if (canBeForceHidden) {
    461                 if (shouldBeForceHidden) {
    462                     win.hideLw(false, false);
    463                 } else {
    464                     win.showLw(false, false);
    465                 }
    466             }
    467 
    468             final AppWindowToken atoken = win.mAppToken;
    469             if (winAnimator.mDrawState == READY_TO_SHOW) {
    470                 if (atoken == null || atoken.allDrawn) {
    471                     if (winAnimator.performShowLocked()) {
    472                         setPendingLayoutChanges(displayId,
    473                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
    474                         if (DEBUG_LAYOUT_REPEATS) {
    475                             mWindowPlacerLocked.debugLayoutRepeats(
    476                                     "updateWindowsAndWallpaperLocked 5",
    477                                     getPendingLayoutChanges(displayId));
    478                         }
    479                     }
    480                 }
    481             }
    482             final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
    483             if (appAnimator != null && appAnimator.thumbnail != null) {
    484                 if (appAnimator.thumbnailTransactionSeq != mAnimTransactionSequence) {
    485                     appAnimator.thumbnailTransactionSeq = mAnimTransactionSequence;
    486                     appAnimator.thumbnailLayer = 0;
    487                 }
    488                 if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
    489                     appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
    490                 }
    491             }
    492             if (win.mIsWallpaper) {
    493                 wallpaper = win;
    494             }
    495         } // end forall windows
    496 
    497         // If we have windows that are being show due to them no longer
    498         // being force-hidden, apply the appropriate animation to them if animations are not
    499         // disabled.
    500         if (unForceHiding != null) {
    501             if (!keyguardGoingAwayNoAnimation) {
    502                 boolean first = true;
    503                 for (int i=unForceHiding.size()-1; i>=0; i--) {
    504                     final WindowStateAnimator winAnimator = unForceHiding.get(i);
    505                     Animation a = mPolicy.createForceHideEnterAnimation(
    506                             wallpaperInUnForceHiding && !startingInUnForceHiding,
    507                             keyguardGoingAwayToShade);
    508                     if (a != null) {
    509                         if (DEBUG_KEYGUARD) Slog.v(TAG,
    510                                 "Starting keyguard exit animation on window " + winAnimator.mWin);
    511                         winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM);
    512                         winAnimator.mKeyguardGoingAwayAnimation = true;
    513                         winAnimator.mKeyguardGoingAwayWithWallpaper
    514                                 = keyguardGoingAwayWithWallpaper;
    515                         if (first) {
    516                             mPostKeyguardExitAnimation = a;
    517                             mPostKeyguardExitAnimation.setStartTime(mCurrentTime);
    518                             first = false;
    519                         }
    520                     }
    521                 }
    522             } else if (mKeyguardGoingAway) {
    523                 mPolicy.startKeyguardExitAnimation(mCurrentTime, 0 /* duration */);
    524                 mKeyguardGoingAway = false;
    525             }
    526 
    527 
    528             // Wallpaper is going away in un-force-hide motion, animate it as well.
    529             if (!wallpaperInUnForceHiding && wallpaper != null && !keyguardGoingAwayNoAnimation) {
    530                 if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");
    531                 Animation a = mPolicy.createForceHideWallpaperExitAnimation(
    532                         keyguardGoingAwayToShade);
    533                 if (a != null) {
    534                     wallpaper.mWinAnimator.setAnimation(a);
    535                 }
    536             }
    537         }
    538 
    539         if (mPostKeyguardExitAnimation != null) {
    540             // We're in the midst of a keyguard exit animation.
    541             if (mKeyguardGoingAway) {
    542                 mPolicy.startKeyguardExitAnimation(mCurrentTime +
    543                         mPostKeyguardExitAnimation.getStartOffset(),
    544                         mPostKeyguardExitAnimation.getDuration());
    545                 mKeyguardGoingAway = false;
    546             }
    547             // mPostKeyguardExitAnimation might either be ended normally, cancelled, or "orphaned",
    548             // meaning that the window it was running on was removed. We check for hasEnded() for
    549             // ended normally and cancelled case, and check the time for the "orphaned" case.
    550             else if (mPostKeyguardExitAnimation.hasEnded()
    551                     || mCurrentTime - mPostKeyguardExitAnimation.getStartTime()
    552                             > mPostKeyguardExitAnimation.getDuration()) {
    553                 // Done with the animation, reset.
    554                 if (DEBUG_KEYGUARD) Slog.v(TAG, "Done with Keyguard exit animations.");
    555                 mPostKeyguardExitAnimation = null;
    556             }
    557         }
    558     }
    559 
    560     private void updateWallpaperLocked(int displayId) {
    561         mService.getDisplayContentLocked(displayId).resetAnimationBackgroundAnimator();
    562 
    563         final WindowList windows = mService.getWindowListLocked(displayId);
    564         WindowState detachedWallpaper = null;
    565 
    566         for (int i = windows.size() - 1; i >= 0; i--) {
    567             final WindowState win = windows.get(i);
    568             WindowStateAnimator winAnimator = win.mWinAnimator;
    569             if (winAnimator.mSurfaceController == null || !winAnimator.hasSurface()) {
    570                 continue;
    571             }
    572 
    573             final int flags = win.mAttrs.flags;
    574 
    575             // If this window is animating, make a note that we have
    576             // an animating window and take care of a request to run
    577             // a detached wallpaper animation.
    578             if (winAnimator.mAnimating) {
    579                 if (winAnimator.mAnimation != null) {
    580                     if ((flags & FLAG_SHOW_WALLPAPER) != 0
    581                             && winAnimator.mAnimation.getDetachWallpaper()) {
    582                         detachedWallpaper = win;
    583                     }
    584                     final int color = winAnimator.mAnimation.getBackgroundColor();
    585                     if (color != 0) {
    586                         final TaskStack stack = win.getStack();
    587                         if (stack != null) {
    588                             stack.setAnimationBackground(winAnimator, color);
    589                         }
    590                     }
    591                 }
    592                 setAnimating(true);
    593             }
    594 
    595             // If this window's app token is running a detached wallpaper
    596             // animation, make a note so we can ensure the wallpaper is
    597             // displayed behind it.
    598             final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;
    599             if (appAnimator != null && appAnimator.animation != null
    600                     && appAnimator.animating) {
    601                 if ((flags & FLAG_SHOW_WALLPAPER) != 0
    602                         && appAnimator.animation.getDetachWallpaper()) {
    603                     detachedWallpaper = win;
    604                 }
    605 
    606                 final int color = appAnimator.animation.getBackgroundColor();
    607                 if (color != 0) {
    608                     final TaskStack stack = win.getStack();
    609                     if (stack != null) {
    610                         stack.setAnimationBackground(winAnimator, color);
    611                     }
    612                 }
    613             }
    614         } // end forall windows
    615 
    616         if (mWindowDetachedWallpaper != detachedWallpaper) {
    617             if (DEBUG_WALLPAPER) Slog.v(TAG,
    618                     "Detached wallpaper changed from " + mWindowDetachedWallpaper
    619                     + " to " + detachedWallpaper);
    620             mWindowDetachedWallpaper = detachedWallpaper;
    621             mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
    622         }
    623     }
    624 
    625     /** See if any windows have been drawn, so they (and others associated with them) can now be
    626      *  shown. */
    627     private void testTokenMayBeDrawnLocked(int displayId) {
    628         // See if any windows have been drawn, so they (and others
    629         // associated with them) can now be shown.
    630         final ArrayList<Task> tasks = mService.getDisplayContentLocked(displayId).getTasks();
    631         final int numTasks = tasks.size();
    632         for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
    633             final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
    634             final int numTokens = tokens.size();
    635             for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
    636                 final AppWindowToken wtoken = tokens.get(tokenNdx);
    637                 AppWindowAnimator appAnimator = wtoken.mAppAnimator;
    638                 final boolean allDrawn = wtoken.allDrawn;
    639                 if (allDrawn != appAnimator.allDrawn) {
    640                     appAnimator.allDrawn = allDrawn;
    641                     if (allDrawn) {
    642                         // The token has now changed state to having all
    643                         // windows shown...  what to do, what to do?
    644                         if (appAnimator.freezingScreen) {
    645                             appAnimator.showAllWindowsLocked();
    646                             mService.unsetAppFreezingScreenLocked(wtoken, false, true);
    647                             if (DEBUG_ORIENTATION) Slog.i(TAG,
    648                                     "Setting mOrientationChangeComplete=true because wtoken "
    649                                     + wtoken + " numInteresting=" + wtoken.numInterestingWindows
    650                                     + " numDrawn=" + wtoken.numDrawnWindows);
    651                             // This will set mOrientationChangeComplete and cause a pass through
    652                             // layout.
    653                             setAppLayoutChanges(appAnimator,
    654                                     WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
    655                                     "testTokenMayBeDrawnLocked: freezingScreen", displayId);
    656                         } else {
    657                             setAppLayoutChanges(appAnimator,
    658                                     WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
    659                                     "testTokenMayBeDrawnLocked", displayId);
    660 
    661                             // We can now show all of the drawn windows!
    662                             if (!mService.mOpeningApps.contains(wtoken)) {
    663                                 orAnimating(appAnimator.showAllWindowsLocked());
    664                             }
    665                         }
    666                     }
    667                 }
    668             }
    669         }
    670     }
    671 
    672 
    673     /** Locked on mService.mWindowMap. */
    674     private void animateLocked(long frameTimeNs) {
    675         if (!mInitialized) {
    676             return;
    677         }
    678 
    679         mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
    680         mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
    681         boolean wasAnimating = mAnimating;
    682         setAnimating(false);
    683         mAppWindowAnimating = false;
    684         if (DEBUG_WINDOW_TRACE) {
    685             Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
    686         }
    687 
    688         if (SHOW_TRANSACTIONS) Slog.i(
    689                 TAG, ">>> OPEN TRANSACTION animateLocked");
    690         SurfaceControl.openTransaction();
    691         SurfaceControl.setAnimationTransaction();
    692         try {
    693             final int numDisplays = mDisplayContentsAnimators.size();
    694             for (int i = 0; i < numDisplays; i++) {
    695                 final int displayId = mDisplayContentsAnimators.keyAt(i);
    696                 updateAppWindowsLocked(displayId);
    697                 DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
    698 
    699                 final ScreenRotationAnimation screenRotationAnimation =
    700                         displayAnimator.mScreenRotationAnimation;
    701                 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
    702                     if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
    703                         setAnimating(true);
    704                     } else {
    705                         mBulkUpdateParams |= SET_UPDATE_ROTATION;
    706                         screenRotationAnimation.kill();
    707                         displayAnimator.mScreenRotationAnimation = null;
    708 
    709                         //TODO (multidisplay): Accessibility supported only for the default display.
    710                         if (mService.mAccessibilityController != null
    711                                 && displayId == Display.DEFAULT_DISPLAY) {
    712                             // We just finished rotation animation which means we did not
    713                             // anounce the rotation and waited for it to end, announce now.
    714                             mService.mAccessibilityController.onRotationChangedLocked(
    715                                     mService.getDefaultDisplayContentLocked(), mService.mRotation);
    716                         }
    717                     }
    718                 }
    719 
    720                 // Update animations of all applications, including those
    721                 // associated with exiting/removed apps
    722                 updateWindowsLocked(displayId);
    723                 updateWallpaperLocked(displayId);
    724 
    725                 final WindowList windows = mService.getWindowListLocked(displayId);
    726                 final int N = windows.size();
    727                 for (int j = 0; j < N; j++) {
    728                     windows.get(j).mWinAnimator.prepareSurfaceLocked(true);
    729                 }
    730             }
    731 
    732             for (int i = 0; i < numDisplays; i++) {
    733                 final int displayId = mDisplayContentsAnimators.keyAt(i);
    734 
    735                 testTokenMayBeDrawnLocked(displayId);
    736 
    737                 final ScreenRotationAnimation screenRotationAnimation =
    738                         mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
    739                 if (screenRotationAnimation != null) {
    740                     screenRotationAnimation.updateSurfacesInTransaction();
    741                 }
    742 
    743                 orAnimating(mService.getDisplayContentLocked(displayId).animateDimLayers());
    744                 orAnimating(mService.getDisplayContentLocked(displayId).getDockedDividerController()
    745                         .animate(mCurrentTime));
    746                 //TODO (multidisplay): Magnification is supported only for the default display.
    747                 if (mService.mAccessibilityController != null
    748                         && displayId == Display.DEFAULT_DISPLAY) {
    749                     mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
    750                 }
    751             }
    752 
    753             if (mService.mDragState != null) {
    754                 mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
    755             }
    756 
    757             if (mAnimating) {
    758                 mService.scheduleAnimationLocked();
    759             }
    760 
    761             if (mService.mWatermark != null) {
    762                 mService.mWatermark.drawIfNeeded();
    763             }
    764         } catch (RuntimeException e) {
    765             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
    766         } finally {
    767             SurfaceControl.closeTransaction();
    768             if (SHOW_TRANSACTIONS) Slog.i(
    769                     TAG, "<<< CLOSE TRANSACTION animateLocked");
    770         }
    771 
    772         boolean hasPendingLayoutChanges = false;
    773         final int numDisplays = mService.mDisplayContents.size();
    774         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
    775             final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
    776             final int pendingChanges = getPendingLayoutChanges(displayContent.getDisplayId());
    777             if ((pendingChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
    778                 mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
    779             }
    780             if (pendingChanges != 0) {
    781                 hasPendingLayoutChanges = true;
    782             }
    783         }
    784 
    785         boolean doRequest = false;
    786         if (mBulkUpdateParams != 0) {
    787             doRequest = mWindowPlacerLocked.copyAnimToLayoutParamsLocked();
    788         }
    789 
    790         if (hasPendingLayoutChanges || doRequest) {
    791             mWindowPlacerLocked.requestTraversal();
    792         }
    793 
    794         if (mAnimating && !wasAnimating && Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
    795             Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
    796         }
    797 
    798         if (!mAnimating && wasAnimating) {
    799             mWindowPlacerLocked.requestTraversal();
    800             if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
    801                 Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
    802             }
    803         }
    804 
    805         if (mRemoveReplacedWindows) {
    806             removeReplacedWindowsLocked();
    807         }
    808 
    809         mService.stopUsingSavedSurfaceLocked();
    810         mService.destroyPreservedSurfaceLocked();
    811         mService.mWindowPlacerLocked.destroyPendingSurfaces();
    812 
    813         if (DEBUG_WINDOW_TRACE) {
    814             Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
    815                     + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
    816                     + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
    817                     + Integer.toHexString(getPendingLayoutChanges(Display.DEFAULT_DISPLAY)));
    818         }
    819     }
    820 
    821     private void removeReplacedWindowsLocked() {
    822         if (SHOW_TRANSACTIONS) Slog.i(
    823                 TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
    824         SurfaceControl.openTransaction();
    825         try {
    826             for (int i = mService.mDisplayContents.size() - 1; i >= 0; i--) {
    827                 DisplayContent display = mService.mDisplayContents.valueAt(i);
    828                 final WindowList windows = mService.getWindowListLocked(display.getDisplayId());
    829                 for (int j = windows.size() - 1; j >= 0; j--) {
    830                     windows.get(j).maybeRemoveReplacedWindow();
    831                 }
    832             }
    833         } finally {
    834             SurfaceControl.closeTransaction();
    835             if (SHOW_TRANSACTIONS) Slog.i(
    836                     TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
    837         }
    838         mRemoveReplacedWindows = false;
    839     }
    840 
    841     private static String bulkUpdateParamsToString(int bulkUpdateParams) {
    842         StringBuilder builder = new StringBuilder(128);
    843         if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
    844             builder.append(" UPDATE_ROTATION");
    845         }
    846         if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
    847             builder.append(" WALLPAPER_MAY_CHANGE");
    848         }
    849         if ((bulkUpdateParams & WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED) != 0) {
    850             builder.append(" FORCE_HIDING_CHANGED");
    851         }
    852         if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
    853             builder.append(" ORIENTATION_CHANGE_COMPLETE");
    854         }
    855         if ((bulkUpdateParams & WindowSurfacePlacer.SET_TURN_ON_SCREEN) != 0) {
    856             builder.append(" TURN_ON_SCREEN");
    857         }
    858         return builder.toString();
    859     }
    860 
    861     public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
    862         final String subPrefix = "  " + prefix;
    863         final String subSubPrefix = "  " + subPrefix;
    864 
    865         for (int i = 0; i < mDisplayContentsAnimators.size(); i++) {
    866             pw.print(prefix); pw.print("DisplayContentsAnimator #");
    867                     pw.print(mDisplayContentsAnimators.keyAt(i));
    868                     pw.println(":");
    869             DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
    870             final WindowList windows =
    871                     mService.getWindowListLocked(mDisplayContentsAnimators.keyAt(i));
    872             final int N = windows.size();
    873             for (int j = 0; j < N; j++) {
    874                 WindowStateAnimator wanim = windows.get(j).mWinAnimator;
    875                 pw.print(subPrefix); pw.print("Window #"); pw.print(j);
    876                         pw.print(": "); pw.println(wanim);
    877             }
    878             if (displayAnimator.mScreenRotationAnimation != null) {
    879                 pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
    880                 displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
    881             } else if (dumpAll) {
    882                 pw.print(subPrefix); pw.println("no ScreenRotationAnimation ");
    883             }
    884             pw.println();
    885         }
    886 
    887         pw.println();
    888 
    889         if (dumpAll) {
    890             pw.print(prefix); pw.print("mAnimTransactionSequence=");
    891                     pw.print(mAnimTransactionSequence);
    892                     pw.print(" mForceHiding="); pw.println(forceHidingToString());
    893             pw.print(prefix); pw.print("mCurrentTime=");
    894                     pw.println(TimeUtils.formatUptime(mCurrentTime));
    895         }
    896         if (mBulkUpdateParams != 0) {
    897             pw.print(prefix); pw.print("mBulkUpdateParams=0x");
    898                     pw.print(Integer.toHexString(mBulkUpdateParams));
    899                     pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
    900         }
    901         if (mWindowDetachedWallpaper != null) {
    902             pw.print(prefix); pw.print("mWindowDetachedWallpaper=");
    903                 pw.println(mWindowDetachedWallpaper);
    904         }
    905     }
    906 
    907     int getPendingLayoutChanges(final int displayId) {
    908         if (displayId < 0) {
    909             return 0;
    910         }
    911         final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
    912         return (displayContent != null) ? displayContent.pendingLayoutChanges : 0;
    913     }
    914 
    915     void setPendingLayoutChanges(final int displayId, final int changes) {
    916         if (displayId < 0) {
    917             return;
    918         }
    919         final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
    920         if (displayContent != null) {
    921             displayContent.pendingLayoutChanges |= changes;
    922         }
    923     }
    924 
    925     void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String reason,
    926             final int displayId) {
    927         WindowList windows = appAnimator.mAppToken.allAppWindows;
    928         for (int i = windows.size() - 1; i >= 0; i--) {
    929             if (displayId == windows.get(i).getDisplayId()) {
    930                 setPendingLayoutChanges(displayId, changes);
    931                 if (DEBUG_LAYOUT_REPEATS) {
    932                     mWindowPlacerLocked.debugLayoutRepeats(reason,
    933                             getPendingLayoutChanges(displayId));
    934                 }
    935                 break;
    936             }
    937         }
    938     }
    939 
    940     private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
    941         DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
    942         if (displayAnimator == null) {
    943             displayAnimator = new DisplayContentsAnimator();
    944             mDisplayContentsAnimators.put(displayId, displayAnimator);
    945         }
    946         return displayAnimator;
    947     }
    948 
    949     void setScreenRotationAnimationLocked(int displayId, ScreenRotationAnimation animation) {
    950         if (displayId >= 0) {
    951             getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation = animation;
    952         }
    953     }
    954 
    955     ScreenRotationAnimation getScreenRotationAnimationLocked(int displayId) {
    956         if (displayId < 0) {
    957             return null;
    958         }
    959         return getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation;
    960     }
    961 
    962     void requestRemovalOfReplacedWindows(WindowState win) {
    963         mRemoveReplacedWindows = true;
    964     }
    965 
    966     private class DisplayContentsAnimator {
    967         ScreenRotationAnimation mScreenRotationAnimation = null;
    968     }
    969 
    970     boolean isAnimating() {
    971         return mAnimating;
    972     }
    973 
    974     void setAnimating(boolean animating) {
    975         mAnimating = animating;
    976     }
    977 
    978     void orAnimating(boolean animating) {
    979         mAnimating |= animating;
    980     }
    981 }
    982