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.Display.INVALID_DISPLAY; 20 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; 21 import static com.android.server.wm.AppTransition.TRANSIT_UNSET; 22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; 23 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; 24 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 26 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 27 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET; 28 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; 29 30 import android.graphics.Matrix; 31 import android.util.Slog; 32 import android.util.TimeUtils; 33 import android.view.Choreographer; 34 import android.view.Display; 35 import android.view.SurfaceControl; 36 import android.view.animation.Animation; 37 import android.view.animation.Transformation; 38 39 import java.io.PrintWriter; 40 import java.util.ArrayList; 41 42 public class AppWindowAnimator { 43 static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowAnimator" : TAG_WM; 44 45 private static final int PROLONG_ANIMATION_DISABLED = 0; 46 static final int PROLONG_ANIMATION_AT_END = 1; 47 static final int PROLONG_ANIMATION_AT_START = 2; 48 49 final AppWindowToken mAppToken; 50 final WindowManagerService mService; 51 final WindowAnimator mAnimator; 52 53 boolean animating; 54 boolean wasAnimating; 55 Animation animation; 56 boolean hasTransformation; 57 final Transformation transformation = new Transformation(); 58 59 // Have we been asked to have this token keep the screen frozen? 60 // Protect with mAnimator. 61 boolean freezingScreen; 62 63 /** 64 * How long we last kept the screen frozen. 65 */ 66 int lastFreezeDuration; 67 68 // Offset to the window of all layers in the token, for use by 69 // AppWindowToken animations. 70 int animLayerAdjustment; 71 72 // Propagated from AppWindowToken.allDrawn, to determine when 73 // the state changes. 74 boolean allDrawn; 75 76 // Special surface for thumbnail animation. If deferThumbnailDestruction is enabled, then we 77 // will make sure that the thumbnail is destroyed after the other surface is completed. This 78 // requires that the duration of the two animations are the same. 79 SurfaceControl thumbnail; 80 int thumbnailTransactionSeq; 81 // TODO(b/62029108): combine both members into a private one. Create a member function to set 82 // the thumbnail layer to +1 to the highest layer position and replace all setter instances 83 // with this function. Remove all unnecessary calls to both variables in other classes. 84 int thumbnailLayer; 85 int thumbnailForceAboveLayer; 86 Animation thumbnailAnimation; 87 final Transformation thumbnailTransformation = new Transformation(); 88 // This flag indicates that the destruction of the thumbnail surface is synchronized with 89 // another animation, so defer the destruction of this thumbnail surface for a single frame 90 // after the secondary animation completes. 91 boolean deferThumbnailDestruction; 92 // This flag is set if the animator has deferThumbnailDestruction set and has reached the final 93 // frame of animation. It will extend the animation by one frame and then clean up afterwards. 94 boolean deferFinalFrameCleanup; 95 // If true when the animation hits the last frame, it will keep running on that last frame. 96 // This is used to synchronize animation with Recents and we wait for Recents to tell us to 97 // finish or for a new animation be set as fail-safe mechanism. 98 private int mProlongAnimation; 99 // Whether the prolong animation can be removed when animation is set. The purpose of this is 100 // that if recents doesn't tell us to remove the prolonged animation, we will get rid of it 101 // when new animation is set. 102 private boolean mClearProlongedAnimation; 103 private int mTransit; 104 private int mTransitFlags; 105 106 /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */ 107 ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<>(); 108 109 /** True if the current animation was transferred from another AppWindowAnimator. 110 * See {@link #transferCurrentAnimation}*/ 111 boolean usingTransferredAnimation = false; 112 113 private boolean mSkipFirstFrame = false; 114 private int mStackClip = STACK_CLIP_BEFORE_ANIM; 115 116 static final Animation sDummyAnimation = new DummyAnimation(); 117 AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service)118 public AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service) { 119 mAppToken = atoken; 120 mService = service; 121 mAnimator = mService.mAnimator; 122 } 123 setAnimation(Animation anim, int width, int height, int parentWidth, int parentHeight, boolean skipFirstFrame, int stackClip, int transit, int transitFlags)124 public void setAnimation(Animation anim, int width, int height, int parentWidth, 125 int parentHeight, boolean skipFirstFrame, int stackClip, int transit, 126 int transitFlags) { 127 if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken 128 + ": " + anim + " wxh=" + width + "x" + height 129 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay()); 130 animation = anim; 131 animating = false; 132 if (!anim.isInitialized()) { 133 anim.initialize(width, height, parentWidth, parentHeight); 134 } 135 anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); 136 anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); 137 int zorder = anim.getZAdjustment(); 138 int adj = 0; 139 if (zorder == Animation.ZORDER_TOP) { 140 adj = TYPE_LAYER_OFFSET; 141 } else if (zorder == Animation.ZORDER_BOTTOM) { 142 adj = -TYPE_LAYER_OFFSET; 143 } 144 145 if (animLayerAdjustment != adj) { 146 animLayerAdjustment = adj; 147 updateLayers(); 148 } 149 // Start out animation gone if window is gone, or visible if window is visible. 150 transformation.clear(); 151 transformation.setAlpha(mAppToken.isVisible() ? 1 : 0); 152 hasTransformation = true; 153 mStackClip = stackClip; 154 155 mSkipFirstFrame = skipFirstFrame; 156 mTransit = transit; 157 mTransitFlags = transitFlags; 158 159 if (!mAppToken.fillsParent()) { 160 anim.setBackgroundColor(0); 161 } 162 if (mClearProlongedAnimation) { 163 mProlongAnimation = PROLONG_ANIMATION_DISABLED; 164 } else { 165 mClearProlongedAnimation = true; 166 } 167 } 168 setDummyAnimation()169 public void setDummyAnimation() { 170 if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken 171 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay()); 172 animation = sDummyAnimation; 173 hasTransformation = true; 174 transformation.clear(); 175 transformation.setAlpha(mAppToken.isVisible() ? 1 : 0); 176 } 177 setNullAnimation()178 void setNullAnimation() { 179 animation = null; 180 usingTransferredAnimation = false; 181 } 182 clearAnimation()183 public void clearAnimation() { 184 if (animation != null) { 185 animating = true; 186 } 187 clearThumbnail(); 188 setNullAnimation(); 189 if (mAppToken.deferClearAllDrawn) { 190 mAppToken.clearAllDrawn(); 191 } 192 mStackClip = STACK_CLIP_BEFORE_ANIM; 193 mTransit = TRANSIT_UNSET; 194 mTransitFlags = 0; 195 } 196 isAnimating()197 public boolean isAnimating() { 198 return animation != null || mAppToken.inPendingTransaction; 199 } 200 201 /** 202 * @return whether an animation is about to start, i.e. the animation is set already but we 203 * haven't processed the first frame yet. 204 */ isAnimationStarting()205 boolean isAnimationStarting() { 206 return animation != null && !animating; 207 } 208 getTransit()209 public int getTransit() { 210 return mTransit; 211 } 212 getTransitFlags()213 int getTransitFlags() { 214 return mTransitFlags; 215 } 216 clearThumbnail()217 public void clearThumbnail() { 218 if (thumbnail != null) { 219 thumbnail.hide(); 220 mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail); 221 thumbnail = null; 222 } 223 deferThumbnailDestruction = false; 224 } 225 getStackClip()226 int getStackClip() { 227 return mStackClip; 228 } 229 transferCurrentAnimation( AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator)230 void transferCurrentAnimation( 231 AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) { 232 233 if (animation != null) { 234 toAppAnimator.animation = animation; 235 toAppAnimator.animating = animating; 236 toAppAnimator.animLayerAdjustment = animLayerAdjustment; 237 setNullAnimation(); 238 animLayerAdjustment = 0; 239 toAppAnimator.updateLayers(); 240 updateLayers(); 241 toAppAnimator.usingTransferredAnimation = true; 242 toAppAnimator.mTransit = mTransit; 243 } 244 if (transferWinAnimator != null) { 245 mAllAppWinAnimators.remove(transferWinAnimator); 246 toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator); 247 toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation; 248 if (toAppAnimator.hasTransformation) { 249 toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation); 250 } else { 251 toAppAnimator.transformation.clear(); 252 } 253 transferWinAnimator.mAppAnimator = toAppAnimator; 254 } 255 } 256 updateLayers()257 private void updateLayers() { 258 mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */); 259 thumbnailLayer = mAppToken.getHighestAnimLayer(); 260 } 261 stepThumbnailAnimation(long currentTime)262 private void stepThumbnailAnimation(long currentTime) { 263 thumbnailTransformation.clear(); 264 final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime); 265 thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation); 266 267 ScreenRotationAnimation screenRotationAnimation = 268 mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); 269 final boolean screenAnimation = screenRotationAnimation != null 270 && screenRotationAnimation.isAnimating(); 271 if (screenAnimation) { 272 thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation()); 273 } 274 // cache often used attributes locally 275 final float tmpFloats[] = mService.mTmpFloats; 276 thumbnailTransformation.getMatrix().getValues(tmpFloats); 277 if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, 278 "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X] 279 + ", " + tmpFloats[Matrix.MTRANS_Y]); 280 thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]); 281 if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, 282 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha() 283 + " layer=" + thumbnailLayer 284 + " matrix=[" + tmpFloats[Matrix.MSCALE_X] 285 + "," + tmpFloats[Matrix.MSKEW_Y] 286 + "][" + tmpFloats[Matrix.MSKEW_X] 287 + "," + tmpFloats[Matrix.MSCALE_Y] + "]"); 288 thumbnail.setAlpha(thumbnailTransformation.getAlpha()); 289 if (thumbnailForceAboveLayer > 0) { 290 thumbnail.setLayer(thumbnailForceAboveLayer + 1); 291 } else { 292 // The thumbnail is layered below the window immediately above this 293 // token's anim layer. 294 thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER 295 - WindowManagerService.LAYER_OFFSET_THUMBNAIL); 296 } 297 thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], 298 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); 299 thumbnail.setWindowCrop(thumbnailTransformation.getClipRect()); 300 } 301 302 /** 303 * Sometimes we need to synchronize the first frame of animation with some external event, e.g. 304 * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton 305 * and keep producing the first frame of the animation. 306 */ getAnimationFrameTime(Animation animation, long currentTime)307 private long getAnimationFrameTime(Animation animation, long currentTime) { 308 if (mProlongAnimation == PROLONG_ANIMATION_AT_START) { 309 animation.setStartTime(currentTime); 310 return currentTime + 1; 311 } 312 return currentTime; 313 } 314 stepAnimation(long currentTime)315 private boolean stepAnimation(long currentTime) { 316 if (animation == null) { 317 return false; 318 } 319 transformation.clear(); 320 final long animationFrameTime = getAnimationFrameTime(animation, currentTime); 321 boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation); 322 if (!hasMoreFrames) { 323 if (deferThumbnailDestruction && !deferFinalFrameCleanup) { 324 // We are deferring the thumbnail destruction, so extend the animation for one more 325 // (dummy) frame before we clean up 326 deferFinalFrameCleanup = true; 327 hasMoreFrames = true; 328 } else { 329 if (false && DEBUG_ANIM) Slog.v(TAG, 330 "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames + 331 ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation); 332 deferFinalFrameCleanup = false; 333 if (mProlongAnimation == PROLONG_ANIMATION_AT_END) { 334 hasMoreFrames = true; 335 } else { 336 setNullAnimation(); 337 clearThumbnail(); 338 if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ " 339 + currentTime); 340 } 341 } 342 } 343 hasTransformation = hasMoreFrames; 344 return hasMoreFrames; 345 } 346 getStartTimeCorrection()347 private long getStartTimeCorrection() { 348 if (mSkipFirstFrame) { 349 350 // If the transition is an animation in which the first frame doesn't change the screen 351 // contents at all, we can just skip it and start at the second frame. So we shift the 352 // start time of the animation forward by minus the frame duration. 353 return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS; 354 } else { 355 return 0; 356 } 357 } 358 359 // This must be called while inside a transaction. stepAnimationLocked(long currentTime)360 boolean stepAnimationLocked(long currentTime) { 361 if (mAppToken.okToAnimate()) { 362 // We will run animations as long as the display isn't frozen. 363 364 if (animation == sDummyAnimation) { 365 // This guy is going to animate, but not yet. For now count 366 // it as not animating for purposes of scheduling transactions; 367 // when it is really time to animate, this will be set to 368 // a real animation and the next call will execute normally. 369 return false; 370 } 371 372 if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed) 373 && animation != null) { 374 if (!animating) { 375 if (DEBUG_ANIM) Slog.v(TAG, 376 "Starting animation in " + mAppToken + 377 " @ " + currentTime + " scale=" 378 + mService.getTransitionAnimationScaleLocked() 379 + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating); 380 long correction = getStartTimeCorrection(); 381 animation.setStartTime(currentTime + correction); 382 animating = true; 383 if (thumbnail != null) { 384 thumbnail.show(); 385 thumbnailAnimation.setStartTime(currentTime + correction); 386 } 387 mSkipFirstFrame = false; 388 } 389 if (stepAnimation(currentTime)) { 390 // animation isn't over, step any thumbnail and that's 391 // it for now. 392 if (thumbnail != null) { 393 stepThumbnailAnimation(currentTime); 394 } 395 return true; 396 } 397 } 398 } else if (animation != null) { 399 // If the display is frozen, and there is a pending animation, 400 // clear it and make sure we run the cleanup code. 401 animating = true; 402 animation = null; 403 } 404 405 hasTransformation = false; 406 407 if (!animating && animation == null) { 408 return false; 409 } 410 411 mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "AppWindowToken"); 412 413 clearAnimation(); 414 animating = false; 415 if (animLayerAdjustment != 0) { 416 animLayerAdjustment = 0; 417 updateLayers(); 418 } 419 if (mService.mInputMethodTarget != null 420 && mService.mInputMethodTarget.mAppToken == mAppToken) { 421 mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */); 422 } 423 424 if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken 425 + ": reportedVisible=" + mAppToken.reportedVisible 426 + " okToDisplay=" + mAppToken.okToDisplay() 427 + " okToAnimate=" + mAppToken.okToAnimate() 428 + " startingDisplayed=" + mAppToken.startingDisplayed); 429 430 transformation.clear(); 431 432 final int numAllAppWinAnimators = mAllAppWinAnimators.size(); 433 for (int i = 0; i < numAllAppWinAnimators; i++) { 434 mAllAppWinAnimators.get(i).mWin.onExitAnimationDone(); 435 } 436 mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token); 437 return false; 438 } 439 440 // This must be called while inside a transaction. showAllWindowsLocked()441 boolean showAllWindowsLocked() { 442 boolean isAnimating = false; 443 final int NW = mAllAppWinAnimators.size(); 444 for (int i=0; i<NW; i++) { 445 WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i); 446 if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator); 447 winAnimator.mWin.performShowLocked(); 448 isAnimating |= winAnimator.isAnimationSet(); 449 } 450 return isAnimating; 451 } 452 dump(PrintWriter pw, String prefix)453 void dump(PrintWriter pw, String prefix) { 454 pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); 455 pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator); 456 pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen); 457 pw.print(" allDrawn="); pw.print(allDrawn); 458 pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment); 459 if (lastFreezeDuration != 0) { 460 pw.print(prefix); pw.print("lastFreezeDuration="); 461 TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println(); 462 } 463 if (animating || animation != null) { 464 pw.print(prefix); pw.print("animating="); pw.println(animating); 465 pw.print(prefix); pw.print("animation="); pw.println(animation); 466 pw.print(prefix); pw.print("mTransit="); pw.println(mTransit); 467 pw.print(prefix); pw.print("mTransitFlags="); pw.println(mTransitFlags); 468 } 469 if (hasTransformation) { 470 pw.print(prefix); pw.print("XForm: "); 471 transformation.printShortString(pw); 472 pw.println(); 473 } 474 if (thumbnail != null) { 475 pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail); 476 pw.print(" layer="); pw.println(thumbnailLayer); 477 pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation); 478 pw.print(prefix); pw.print("thumbnailTransformation="); 479 pw.println(thumbnailTransformation.toShortString()); 480 } 481 for (int i=0; i<mAllAppWinAnimators.size(); i++) { 482 WindowStateAnimator wanim = mAllAppWinAnimators.get(i); 483 pw.print(prefix); pw.print("App Win Anim #"); pw.print(i); 484 pw.print(": "); pw.println(wanim); 485 } 486 } 487 startProlongAnimation(int prolongType)488 void startProlongAnimation(int prolongType) { 489 mProlongAnimation = prolongType; 490 mClearProlongedAnimation = false; 491 } 492 endProlongedAnimation()493 void endProlongedAnimation() { 494 mProlongAnimation = PROLONG_ANIMATION_DISABLED; 495 } 496 497 // This is an animation that does nothing: it just immediately finishes 498 // itself every time it is called. It is used as a stub animation in cases 499 // where we want to synchronize multiple things that may be animating. 500 static final class DummyAnimation extends Animation { 501 @Override getTransformation(long currentTime, Transformation outTransformation)502 public boolean getTransformation(long currentTime, Transformation outTransformation) { 503 return false; 504 } 505 } 506 507 } 508