1 /* 2 * Copyright (C) 2010 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.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 import static android.util.RotationUtils.deltaRotation; 21 import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE; 22 23 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; 24 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; 25 import static com.android.server.wm.AnimationSpecProto.ROTATE; 26 import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS; 27 import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA; 28 import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA; 29 import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING; 30 import static com.android.server.wm.ScreenRotationAnimationProto.STARTED; 31 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION; 32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 33 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 34 import static com.android.server.wm.utils.CoordinateTransforms.computeRotationMatrix; 35 import static com.android.window.flags.Flags.deleteCaptureDisplay; 36 37 import android.animation.ArgbEvaluator; 38 import android.content.Context; 39 import android.graphics.Color; 40 import android.graphics.Matrix; 41 import android.graphics.Point; 42 import android.graphics.Rect; 43 import android.hardware.HardwareBuffer; 44 import android.os.IBinder; 45 import android.os.Trace; 46 import android.util.Slog; 47 import android.util.proto.ProtoOutputStream; 48 import android.view.DisplayAddress; 49 import android.view.DisplayInfo; 50 import android.view.Surface; 51 import android.view.Surface.OutOfResourcesException; 52 import android.view.SurfaceControl; 53 import android.view.animation.Animation; 54 import android.view.animation.AnimationUtils; 55 import android.view.animation.Transformation; 56 import android.window.ScreenCapture; 57 58 import com.android.internal.R; 59 import com.android.internal.policy.TransitionAnimation; 60 import com.android.internal.protolog.common.ProtoLog; 61 import com.android.server.display.DisplayControl; 62 import com.android.server.wm.SurfaceAnimator.AnimationType; 63 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 64 65 import java.io.PrintWriter; 66 67 /** 68 * This class handles the rotation animation when the device is rotated. 69 * 70 * <p> 71 * The screen rotation animation is composed of 4 different part: 72 * <ul> 73 * <li> The screenshot: <p> 74 * A screenshot of the whole screen prior the change of orientation is taken to hide the 75 * element resizing below. The screenshot is then animated to rotate and cross-fade to 76 * the new orientation with the content in the new orientation. 77 * 78 * <li> The windows on the display: <p>y 79 * Once the device is rotated, the screen and its content are in the new orientation. The 80 * animation first rotate the new content into the old orientation to then be able to 81 * animate to the new orientation 82 * 83 * <li> The Background color frame: <p> 84 * To have the animation seem more seamless, we add a color transitioning background behind the 85 * exiting and entering layouts. We compute the brightness of the start and end 86 * layouts and transition from the two brightness values as grayscale underneath the animation 87 * 88 * <li> The entering Blackframe: <p> 89 * The enter Blackframe is similar to the exit Blackframe but is only used when a custom 90 * rotation animation is used and matches the new content size instead of the screenshot. 91 * </ul> 92 * 93 * Each part has its own Surface which are then animated by {@link SurfaceAnimator}s. 94 */ 95 class ScreenRotationAnimation { 96 private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM; 97 98 private final Context mContext; 99 private final DisplayContent mDisplayContent; 100 private final float[] mTmpFloats = new float[9]; 101 private final Transformation mRotateExitTransformation = new Transformation(); 102 private final Transformation mRotateEnterTransformation = new Transformation(); 103 // Complete transformations being applied. 104 private final Matrix mSnapshotInitialMatrix = new Matrix(); 105 private final WindowManagerService mService; 106 /** Only used for custom animations and not screen rotation. */ 107 private SurfaceControl mEnterBlackFrameLayer; 108 /** This layer contains the actual screenshot that is to be faded out. */ 109 private SurfaceControl mScreenshotLayer; 110 private SurfaceControl[] mRoundedCornerOverlay; 111 /** 112 * Only used for screen rotation and not custom animations. Layered behind all other layers 113 * to avoid showing any "empty" spots 114 */ 115 private SurfaceControl mBackColorSurface; 116 private BlackFrame mEnteringBlackFrame; 117 118 private final int mOriginalRotation; 119 private final int mOriginalWidth; 120 private final int mOriginalHeight; 121 private int mCurRotation; 122 123 // The current active animation to move from the old to the new rotated 124 // state. Which animation is run here will depend on the old and new 125 // rotations. 126 private Animation mRotateExitAnimation; 127 private Animation mRotateEnterAnimation; 128 private Animation mRotateAlphaAnimation; 129 private boolean mStarted; 130 private boolean mAnimRunning; 131 private boolean mFinishAnimReady; 132 private long mFinishAnimStartTime; 133 private SurfaceRotationAnimationController mSurfaceRotationAnimationController; 134 /** Intensity of light/whiteness of the layout before rotation occurs. */ 135 private float mStartLuma; 136 /** Intensity of light/whiteness of the layout after rotation occurs. */ 137 private float mEndLuma; 138 ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation)139 ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) { 140 mService = displayContent.mWmService; 141 mContext = mService.mContext; 142 mDisplayContent = displayContent; 143 final Rect currentBounds = displayContent.getBounds(); 144 final int width = currentBounds.width(); 145 final int height = currentBounds.height(); 146 147 // Screenshot does NOT include rotation! 148 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); 149 final int realOriginalRotation = displayInfo.rotation; 150 151 mOriginalRotation = originalRotation; 152 // If the delta is not zero, the rotation of display may not change, but we still want to 153 // apply rotation animation because there should be a top app shown as rotated. So the 154 // specified original rotation customizes the direction of animation to have better look 155 // when restoring the rotated app to the same rotation as current display. 156 final int delta = deltaRotation(originalRotation, realOriginalRotation); 157 final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270; 158 mOriginalWidth = flipped ? height : width; 159 mOriginalHeight = flipped ? width : height; 160 final int logicalWidth = displayInfo.logicalWidth; 161 final int logicalHeight = displayInfo.logicalHeight; 162 final boolean isSizeChanged = 163 logicalWidth > mOriginalWidth == logicalHeight > mOriginalHeight 164 && (logicalWidth != mOriginalWidth || logicalHeight != mOriginalHeight); 165 mSurfaceRotationAnimationController = new SurfaceRotationAnimationController(); 166 167 // Check whether the current screen contains any secure content. 168 boolean isSecure = displayContent.hasSecureWindowOnScreen(); 169 final int displayId = displayContent.getDisplayId(); 170 final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); 171 172 try { 173 final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer; 174 if (isSizeChanged && !deleteCaptureDisplay()) { 175 final DisplayAddress address = displayInfo.address; 176 if (!(address instanceof DisplayAddress.Physical)) { 177 Slog.e(TAG, "Display does not have a physical address: " + displayId); 178 return; 179 } 180 final DisplayAddress.Physical physicalAddress = 181 (DisplayAddress.Physical) address; 182 final IBinder displayToken = DisplayControl.getPhysicalDisplayToken( 183 physicalAddress.getPhysicalDisplayId()); 184 if (displayToken == null) { 185 Slog.e(TAG, "Display token is null."); 186 return; 187 } 188 // Temporarily not skip screenshot for the rounded corner overlays and screenshot 189 // the whole display to include the rounded corner overlays. 190 setSkipScreenshotForRoundedCornerOverlays(false, t); 191 mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays(); 192 final ScreenCapture.DisplayCaptureArgs captureArgs = 193 new ScreenCapture.DisplayCaptureArgs.Builder(displayToken) 194 .setSourceCrop(new Rect(0, 0, width, height)) 195 .setAllowProtected(true) 196 .setCaptureSecureLayers(true) 197 .setHintForSeamlessTransition(true) 198 .build(); 199 screenshotBuffer = ScreenCapture.captureDisplay(captureArgs); 200 } else if (isSizeChanged) { 201 // Temporarily not skip screenshot for the rounded corner overlays and screenshot 202 // the whole display to include the rounded corner overlays. 203 setSkipScreenshotForRoundedCornerOverlays(false, t); 204 ScreenCapture.LayerCaptureArgs captureArgs = 205 new ScreenCapture.LayerCaptureArgs.Builder( 206 displayContent.getSurfaceControl()) 207 .setCaptureSecureLayers(true) 208 .setAllowProtected(true) 209 .setSourceCrop(new Rect(0, 0, width, height)) 210 .setHintForSeamlessTransition(true) 211 .build(); 212 screenshotBuffer = ScreenCapture.captureLayers(captureArgs); 213 } else { 214 ScreenCapture.LayerCaptureArgs captureArgs = 215 new ScreenCapture.LayerCaptureArgs.Builder( 216 displayContent.getSurfaceControl()) 217 .setCaptureSecureLayers(true) 218 .setAllowProtected(true) 219 .setSourceCrop(new Rect(0, 0, width, height)) 220 .setHintForSeamlessTransition(true) 221 .build(); 222 screenshotBuffer = ScreenCapture.captureLayers(captureArgs); 223 } 224 225 if (screenshotBuffer == null) { 226 Slog.w(TAG, "Unable to take screenshot of display " + displayId); 227 return; 228 } 229 230 // If the screenshot contains secure layers, we have to make sure the 231 // screenshot surface we display it in also has FLAG_SECURE so that 232 // the user can not screenshot secure layers via the screenshot surface. 233 if (screenshotBuffer.containsSecureLayers()) { 234 isSecure = true; 235 } 236 237 mBackColorSurface = displayContent.makeChildSurface(null) 238 .setName("BackColorSurface") 239 .setColorLayer() 240 .setCallsite("ScreenRotationAnimation") 241 .build(); 242 243 String name = "RotationLayer"; 244 mScreenshotLayer = displayContent.makeOverlay() 245 .setName(name) 246 .setOpaque(true) 247 .setSecure(isSecure) 248 .setCallsite("ScreenRotationAnimation") 249 .setBLASTLayer() 250 .build(); 251 // This is the way to tell the input system to exclude this surface from occlusion 252 // detection since we don't have a window for it. We do this because this window is 253 // generated by the system as well as its content. 254 InputMonitor.setTrustedOverlayInputInfo(mScreenshotLayer, t, displayId, name); 255 256 mEnterBlackFrameLayer = displayContent.makeOverlay() 257 .setName("EnterBlackFrameLayer") 258 .setContainerLayer() 259 .setCallsite("ScreenRotationAnimation") 260 .build(); 261 262 HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); 263 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, 264 "ScreenRotationAnimation#getMedianBorderLuma"); 265 mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer, 266 screenshotBuffer.getColorSpace()); 267 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 268 269 t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE); 270 t.reparent(mBackColorSurface, displayContent.getSurfaceControl()); 271 // If hdr layers are on-screen, e.g. picture-in-picture mode, the screenshot of 272 // rotation animation is an sdr image containing tone-mapping hdr content, then 273 // disable dimming effect to get avoid of hdr content being dimmed during animation. 274 t.setDimmingEnabled(mScreenshotLayer, !screenshotBuffer.containsHdrLayers()); 275 t.setLayer(mBackColorSurface, -1); 276 t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma}); 277 t.setAlpha(mBackColorSurface, 1); 278 t.setBuffer(mScreenshotLayer, hardwareBuffer); 279 t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace()); 280 t.show(mScreenshotLayer); 281 t.show(mBackColorSurface); 282 hardwareBuffer.close(); 283 284 if (mRoundedCornerOverlay != null) { 285 for (SurfaceControl sc : mRoundedCornerOverlay) { 286 if (sc.isValid()) { 287 t.hide(sc); 288 } 289 } 290 } 291 292 } catch (OutOfResourcesException e) { 293 Slog.w(TAG, "Unable to allocate freeze surface", e); 294 } 295 296 // If display size is changed with the same orientation, map the bounds of screenshot to 297 // the new logical display size. Currently pending transaction and RWC#mDisplayTransaction 298 // are merged to global transaction, so it can be synced with display change when calling 299 // DisplayManagerInternal#performTraversal(transaction). 300 if (mScreenshotLayer != null && isSizeChanged) { 301 displayContent.getPendingTransaction().setGeometry(mScreenshotLayer, 302 new Rect(0, 0, mOriginalWidth, mOriginalHeight), 303 new Rect(0, 0, logicalWidth, logicalHeight), Surface.ROTATION_0); 304 } 305 306 ProtoLog.i(WM_SHOW_SURFACE_ALLOC, 307 " FREEZE %s: CREATE", mScreenshotLayer); 308 if (originalRotation == realOriginalRotation) { 309 setRotation(t, realOriginalRotation); 310 } else { 311 // If the given original rotation is different from real original display rotation, 312 // this is playing non-zero degree rotation animation without display rotation change, 313 // so the snapshot doesn't need to be transformed. 314 mCurRotation = realOriginalRotation; 315 mSnapshotInitialMatrix.reset(); 316 setRotationTransform(t, mSnapshotInitialMatrix); 317 } 318 t.apply(); 319 } 320 setSkipScreenshotForRoundedCornerOverlays( boolean skipScreenshot, SurfaceControl.Transaction t)321 void setSkipScreenshotForRoundedCornerOverlays( 322 boolean skipScreenshot, SurfaceControl.Transaction t) { 323 mDisplayContent.forAllWindows(w -> { 324 if (!w.mToken.mRoundedCornerOverlay || !w.isVisible() || !w.mWinAnimator.hasSurface()) { 325 return; 326 } 327 t.setSkipScreenshot(w.mWinAnimator.mSurfaceController.mSurfaceControl, skipScreenshot); 328 }, false); 329 if (!skipScreenshot) { 330 // Use sync apply to apply the change immediately, so that the next 331 // SC.captureDisplay can capture the screen decor layers. 332 t.apply(true /* sync */); 333 } 334 } 335 dumpDebug(ProtoOutputStream proto, long fieldId)336 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 337 final long token = proto.start(fieldId); 338 proto.write(STARTED, mStarted); 339 proto.write(ANIMATION_RUNNING, mAnimRunning); 340 proto.end(token); 341 } 342 hasScreenshot()343 boolean hasScreenshot() { 344 return mScreenshotLayer != null; 345 } 346 setRotationTransform(SurfaceControl.Transaction t, Matrix matrix)347 private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) { 348 if (mScreenshotLayer == null) { 349 return; 350 } 351 matrix.getValues(mTmpFloats); 352 float x = mTmpFloats[Matrix.MTRANS_X]; 353 float y = mTmpFloats[Matrix.MTRANS_Y]; 354 t.setPosition(mScreenshotLayer, x, y); 355 t.setMatrix(mScreenshotLayer, 356 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y], 357 mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]); 358 359 t.setAlpha(mScreenshotLayer, (float) 1.0); 360 t.show(mScreenshotLayer); 361 } 362 printTo(String prefix, PrintWriter pw)363 public void printTo(String prefix, PrintWriter pw) { 364 pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer); 365 pw.print(prefix); 366 pw.print("mEnteringBlackFrame="); 367 pw.println(mEnteringBlackFrame); 368 if (mEnteringBlackFrame != null) { 369 mEnteringBlackFrame.printTo(prefix + " ", pw); 370 } 371 pw.print(prefix); pw.print("mCurRotation="); pw.print(mCurRotation); 372 pw.print(" mOriginalRotation="); pw.println(mOriginalRotation); 373 pw.print(prefix); pw.print("mOriginalWidth="); pw.print(mOriginalWidth); 374 pw.print(" mOriginalHeight="); pw.println(mOriginalHeight); 375 pw.print(prefix); pw.print("mStarted="); pw.print(mStarted); 376 pw.print(" mAnimRunning="); pw.print(mAnimRunning); 377 pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady); 378 pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime); 379 pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation); 380 pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println(); 381 pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation); 382 pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println(); 383 pw.print(prefix); pw.print("mSnapshotInitialMatrix="); 384 mSnapshotInitialMatrix.dump(pw); pw.println(); 385 } 386 setRotation(SurfaceControl.Transaction t, int rotation)387 public void setRotation(SurfaceControl.Transaction t, int rotation) { 388 mCurRotation = rotation; 389 390 // Compute the transformation matrix that must be applied 391 // to the snapshot to make it stay in the same original position 392 // with the current screen rotation. 393 int delta = deltaRotation(rotation, mOriginalRotation); 394 computeRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mSnapshotInitialMatrix); 395 setRotationTransform(t, mSnapshotInitialMatrix); 396 } 397 398 /** 399 * Returns true if animating. 400 */ startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)401 private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, 402 float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) { 403 if (mScreenshotLayer == null) { 404 // Can't do animation. 405 return false; 406 } 407 if (mStarted) { 408 return true; 409 } 410 411 mStarted = true; 412 413 // Figure out how the screen has moved from the original rotation. 414 int delta = deltaRotation(mCurRotation, mOriginalRotation); 415 416 final boolean customAnim; 417 if (exitAnim != 0 && enterAnim != 0) { 418 customAnim = true; 419 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim); 420 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim); 421 mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext, 422 R.anim.screen_rotate_alpha); 423 } else { 424 customAnim = false; 425 switch (delta) { /* Counter-Clockwise Rotations */ 426 case Surface.ROTATION_0: 427 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 428 R.anim.screen_rotate_0_exit); 429 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 430 R.anim.rotation_animation_enter); 431 break; 432 case Surface.ROTATION_90: 433 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 434 R.anim.screen_rotate_plus_90_exit); 435 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 436 R.anim.screen_rotate_plus_90_enter); 437 break; 438 case Surface.ROTATION_180: 439 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 440 R.anim.screen_rotate_180_exit); 441 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 442 R.anim.screen_rotate_180_enter); 443 break; 444 case Surface.ROTATION_270: 445 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 446 R.anim.screen_rotate_minus_90_exit); 447 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 448 R.anim.screen_rotate_minus_90_enter); 449 break; 450 } 451 } 452 453 ProtoLog.d(WM_DEBUG_ORIENTATION, "Start rotation animation. customAnim=%s, " 454 + "mCurRotation=%s, mOriginalRotation=%s", 455 customAnim, Surface.rotationToString(mCurRotation), 456 Surface.rotationToString(mOriginalRotation)); 457 458 mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); 459 mRotateExitAnimation.restrictDuration(maxAnimationDuration); 460 mRotateExitAnimation.scaleCurrentDuration(animationScale); 461 mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); 462 mRotateEnterAnimation.restrictDuration(maxAnimationDuration); 463 mRotateEnterAnimation.scaleCurrentDuration(animationScale); 464 465 mAnimRunning = false; 466 mFinishAnimReady = false; 467 mFinishAnimStartTime = -1; 468 469 if (customAnim) { 470 mRotateAlphaAnimation.restrictDuration(maxAnimationDuration); 471 mRotateAlphaAnimation.scaleCurrentDuration(animationScale); 472 } 473 474 if (customAnim && mEnteringBlackFrame == null) { 475 try { 476 Rect outer = new Rect(-finalWidth, -finalHeight, 477 finalWidth * 2, finalHeight * 2); 478 Rect inner = new Rect(0, 0, finalWidth, finalHeight); 479 mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner, 480 SCREEN_FREEZE_LAYER_BASE, mDisplayContent, false, mEnterBlackFrameLayer); 481 } catch (OutOfResourcesException e) { 482 Slog.w(TAG, "Unable to allocate black surface", e); 483 } 484 } 485 486 if (customAnim) { 487 mSurfaceRotationAnimationController.startCustomAnimation(); 488 } else { 489 mSurfaceRotationAnimationController.startScreenRotationAnimation(); 490 } 491 492 return true; 493 } 494 495 /** 496 * Returns true if animating. 497 */ dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)498 public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, 499 float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) { 500 if (mScreenshotLayer == null) { 501 // Can't do animation. 502 return false; 503 } 504 if (!mStarted) { 505 mEndLuma = TransitionAnimation.getBorderLuma(mDisplayContent.getWindowingLayer(), 506 finalWidth, finalHeight); 507 startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight, 508 exitAnim, enterAnim); 509 } 510 if (!mStarted) { 511 return false; 512 } 513 mFinishAnimReady = true; 514 return true; 515 } 516 kill()517 public void kill() { 518 if (mSurfaceRotationAnimationController != null) { 519 mSurfaceRotationAnimationController.cancel(); 520 mSurfaceRotationAnimationController = null; 521 } 522 523 if (mScreenshotLayer != null) { 524 ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mScreenshotLayer); 525 SurfaceControl.Transaction t = mService.mTransactionFactory.get(); 526 if (mScreenshotLayer.isValid()) { 527 t.remove(mScreenshotLayer); 528 } 529 mScreenshotLayer = null; 530 531 if (mEnterBlackFrameLayer != null) { 532 if (mEnterBlackFrameLayer.isValid()) { 533 t.remove(mEnterBlackFrameLayer); 534 } 535 mEnterBlackFrameLayer = null; 536 } 537 if (mBackColorSurface != null) { 538 if (mBackColorSurface.isValid()) { 539 t.remove(mBackColorSurface); 540 } 541 mBackColorSurface = null; 542 } 543 544 if (mRoundedCornerOverlay != null) { 545 if (mDisplayContent.getRotationAnimation() == null 546 || mDisplayContent.getRotationAnimation() == this) { 547 setSkipScreenshotForRoundedCornerOverlays(true, t); 548 for (SurfaceControl sc : mRoundedCornerOverlay) { 549 if (sc.isValid()) { 550 t.show(sc); 551 } 552 } 553 } 554 mRoundedCornerOverlay = null; 555 } 556 t.apply(); 557 } 558 559 if (mEnteringBlackFrame != null) { 560 mEnteringBlackFrame.kill(); 561 mEnteringBlackFrame = null; 562 } 563 if (mRotateExitAnimation != null) { 564 mRotateExitAnimation.cancel(); 565 mRotateExitAnimation = null; 566 } 567 if (mRotateEnterAnimation != null) { 568 mRotateEnterAnimation.cancel(); 569 mRotateEnterAnimation = null; 570 } 571 if (mRotateAlphaAnimation != null) { 572 mRotateAlphaAnimation.cancel(); 573 mRotateAlphaAnimation = null; 574 } 575 } 576 isAnimating()577 public boolean isAnimating() { 578 return mSurfaceRotationAnimationController != null 579 && mSurfaceRotationAnimationController.isAnimating(); 580 } 581 isRotating()582 public boolean isRotating() { 583 return mCurRotation != mOriginalRotation; 584 } 585 586 /** 587 * Utility class that runs a {@link ScreenRotationAnimation} on the {@link 588 * SurfaceAnimationRunner}. 589 * <p> 590 * The rotation animation supports both screen rotation and custom animations 591 * 592 * For custom animations: 593 * <ul> 594 * <li> 595 * The screenshot layer which has an added animation of it's alpha channel 596 * ("screen_rotate_alpha") and that will be applied along with the custom animation. 597 * </li> 598 * <li> A device layer that is animated with the provided custom animation </li> 599 * </ul> 600 * 601 * For screen rotation: 602 * <ul> 603 * <li> A rotation layer that is both rotated and faded out during a single animation </li> 604 * <li> A device layer that is both rotated and faded in during a single animation </li> 605 * <li> A background color layer that transitions colors behind the first two layers </li> 606 * </ul> 607 * 608 * {@link ScreenRotationAnimation#startAnimation( 609 * SurfaceControl.Transaction, long, float, int, int, int, int)}. 610 * </ul> 611 * 612 * <p> 613 * Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of 614 * this three {@link SurfaceControl}s which then delegates the animation to the 615 * {@link ScreenRotationAnimation}. 616 */ 617 class SurfaceRotationAnimationController { 618 private SurfaceAnimator mDisplayAnimator; 619 private SurfaceAnimator mScreenshotRotationAnimator; 620 private SurfaceAnimator mRotateScreenAnimator; 621 private SurfaceAnimator mEnterBlackFrameAnimator; 622 startCustomAnimation()623 void startCustomAnimation() { 624 try { 625 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 626 mRotateScreenAnimator = startScreenshotAlphaAnimation(); 627 mDisplayAnimator = startDisplayRotation(); 628 if (mEnteringBlackFrame != null) { 629 mEnterBlackFrameAnimator = startEnterBlackFrameAnimation(); 630 } 631 } finally { 632 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 633 } 634 } 635 636 /** 637 * Start the rotation animation of the display and the screenshot on the 638 * {@link SurfaceAnimationRunner}. 639 */ startScreenRotationAnimation()640 void startScreenRotationAnimation() { 641 try { 642 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 643 mDisplayAnimator = startDisplayRotation(); 644 mScreenshotRotationAnimator = startScreenshotRotationAnimation(); 645 startColorAnimation(); 646 } finally { 647 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 648 } 649 } 650 initializeBuilder()651 private SimpleSurfaceAnimatable.Builder initializeBuilder() { 652 return new SimpleSurfaceAnimatable.Builder() 653 .setSyncTransactionSupplier(mDisplayContent::getSyncTransaction) 654 .setPendingTransactionSupplier(mDisplayContent::getPendingTransaction) 655 .setCommitTransactionRunnable(mDisplayContent::commitPendingTransaction) 656 .setAnimationLeashSupplier(mDisplayContent::makeOverlay); 657 } 658 startDisplayRotation()659 private SurfaceAnimator startDisplayRotation() { 660 SurfaceAnimator animator = startAnimation(initializeBuilder() 661 .setAnimationLeashParent(mDisplayContent.getSurfaceControl()) 662 .setSurfaceControl(mDisplayContent.getWindowingLayer()) 663 .setParentSurfaceControl(mDisplayContent.getSurfaceControl()) 664 .setWidth(mDisplayContent.getSurfaceWidth()) 665 .setHeight(mDisplayContent.getSurfaceHeight()) 666 .build(), 667 createWindowAnimationSpec(mRotateEnterAnimation), 668 this::onAnimationEnd); 669 670 // Crop the animation leash to avoid extended wallpaper from showing over 671 // mBackColorSurface 672 Rect displayBounds = mDisplayContent.getBounds(); 673 mDisplayContent.getPendingTransaction() 674 .setWindowCrop(animator.mLeash, displayBounds.width(), displayBounds.height()); 675 return animator; 676 } 677 startScreenshotAlphaAnimation()678 private SurfaceAnimator startScreenshotAlphaAnimation() { 679 return startAnimation(initializeBuilder() 680 .setSurfaceControl(mScreenshotLayer) 681 .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) 682 .setWidth(mDisplayContent.getSurfaceWidth()) 683 .setHeight(mDisplayContent.getSurfaceHeight()) 684 .build(), 685 createWindowAnimationSpec(mRotateAlphaAnimation), 686 this::onAnimationEnd); 687 } 688 startEnterBlackFrameAnimation()689 private SurfaceAnimator startEnterBlackFrameAnimation() { 690 return startAnimation(initializeBuilder() 691 .setSurfaceControl(mEnterBlackFrameLayer) 692 .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) 693 .build(), 694 createWindowAnimationSpec(mRotateEnterAnimation), 695 this::onAnimationEnd); 696 } 697 startScreenshotRotationAnimation()698 private SurfaceAnimator startScreenshotRotationAnimation() { 699 return startAnimation(initializeBuilder() 700 .setSurfaceControl(mScreenshotLayer) 701 .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) 702 .build(), 703 createWindowAnimationSpec(mRotateExitAnimation), 704 this::onAnimationEnd); 705 } 706 707 708 /** 709 * Applies the color change from {@link #mStartLuma} to {@link #mEndLuma} as a 710 * grayscale color 711 */ startColorAnimation()712 private void startColorAnimation() { 713 int colorTransitionMs = mContext.getResources().getInteger( 714 R.integer.config_screen_rotation_color_transition); 715 final SurfaceAnimationRunner runner = mService.mSurfaceAnimationRunner; 716 final float[] rgbTmpFloat = new float[3]; 717 final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma); 718 final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma); 719 final long duration = colorTransitionMs * (long) mService.getCurrentAnimatorScale(); 720 final ArgbEvaluator va = ArgbEvaluator.getInstance(); 721 runner.startAnimation( 722 new LocalAnimationAdapter.AnimationSpec() { 723 @Override 724 public long getDuration() { 725 return duration; 726 } 727 728 @Override 729 public void apply(SurfaceControl.Transaction t, SurfaceControl leash, 730 long currentPlayTime) { 731 final float fraction = getFraction(currentPlayTime); 732 final int color = (Integer) va.evaluate(fraction, startColor, endColor); 733 Color middleColor = Color.valueOf(color); 734 rgbTmpFloat[0] = middleColor.red(); 735 rgbTmpFloat[1] = middleColor.green(); 736 rgbTmpFloat[2] = middleColor.blue(); 737 if (leash.isValid()) { 738 t.setColor(leash, rgbTmpFloat); 739 } 740 } 741 742 @Override 743 public void dump(PrintWriter pw, String prefix) { 744 pw.println(prefix + "startLuma=" + mStartLuma 745 + " endLuma=" + mEndLuma 746 + " durationMs=" + colorTransitionMs); 747 } 748 749 @Override 750 public void dumpDebugInner(ProtoOutputStream proto) { 751 final long token = proto.start(ROTATE); 752 proto.write(START_LUMA, mStartLuma); 753 proto.write(END_LUMA, mEndLuma); 754 proto.write(DURATION_MS, colorTransitionMs); 755 proto.end(token); 756 } 757 }, 758 mBackColorSurface, mDisplayContent.getPendingTransaction(), null); 759 } 760 createWindowAnimationSpec(Animation mAnimation)761 private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) { 762 return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */, 763 false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */); 764 } 765 766 /** 767 * Start an animation defined by animationSpec on a new {@link SurfaceAnimator}. 768 * 769 * @param animatable The animatable used for the animation. 770 * @param animationSpec The spec of the animation. 771 * @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator} 772 * and called when the animation finishes. 773 * @return The newly created {@link SurfaceAnimator} that as been started. 774 */ startAnimation( SurfaceAnimator.Animatable animatable, LocalAnimationAdapter.AnimationSpec animationSpec, OnAnimationFinishedCallback animationFinishedCallback)775 private SurfaceAnimator startAnimation( 776 SurfaceAnimator.Animatable animatable, 777 LocalAnimationAdapter.AnimationSpec animationSpec, 778 OnAnimationFinishedCallback animationFinishedCallback) { 779 SurfaceAnimator animator = new SurfaceAnimator( 780 animatable, animationFinishedCallback, mService); 781 782 LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter( 783 animationSpec, mService.mSurfaceAnimationRunner); 784 animator.startAnimation(mDisplayContent.getPendingTransaction(), 785 localAnimationAdapter, false, ANIMATION_TYPE_SCREEN_ROTATION); 786 return animator; 787 } 788 onAnimationEnd(@nimationType int type, AnimationAdapter anim)789 private void onAnimationEnd(@AnimationType int type, AnimationAdapter anim) { 790 synchronized (mService.mGlobalLock) { 791 if (isAnimating()) { 792 ProtoLog.v(WM_DEBUG_ORIENTATION, 793 "ScreenRotation still animating: type: %d\n" 794 + "mDisplayAnimator: %s\n" 795 + "mEnterBlackFrameAnimator: %s\n" 796 + "mRotateScreenAnimator: %s\n" 797 + "mScreenshotRotationAnimator: %s", 798 type, 799 mDisplayAnimator != null 800 ? mDisplayAnimator.isAnimating() : null, 801 mEnterBlackFrameAnimator != null 802 ? mEnterBlackFrameAnimator.isAnimating() : null, 803 mRotateScreenAnimator != null 804 ? mRotateScreenAnimator.isAnimating() : null, 805 mScreenshotRotationAnimator != null 806 ? mScreenshotRotationAnimator.isAnimating() : null 807 ); 808 return; 809 } 810 ProtoLog.d(WM_DEBUG_ORIENTATION, "ScreenRotationAnimation onAnimationEnd"); 811 mEnterBlackFrameAnimator = null; 812 mScreenshotRotationAnimator = null; 813 mRotateScreenAnimator = null; 814 mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION; 815 if (mDisplayContent.getRotationAnimation() == ScreenRotationAnimation.this) { 816 // It also invokes kill(). 817 mDisplayContent.setRotationAnimation(null); 818 if (mDisplayContent.mDisplayRotationCompatPolicy != null) { 819 mDisplayContent.mDisplayRotationCompatPolicy 820 .onScreenRotationAnimationFinished(); 821 } 822 } else { 823 kill(); 824 } 825 mService.updateRotation(false, false); 826 } 827 } 828 cancel()829 public void cancel() { 830 if (mEnterBlackFrameAnimator != null) { 831 mEnterBlackFrameAnimator.cancelAnimation(); 832 } 833 if (mScreenshotRotationAnimator != null) { 834 mScreenshotRotationAnimator.cancelAnimation(); 835 } 836 837 if (mRotateScreenAnimator != null) { 838 mRotateScreenAnimator.cancelAnimation(); 839 } 840 841 if (mDisplayAnimator != null) { 842 mDisplayAnimator.cancelAnimation(); 843 } 844 845 if (mBackColorSurface != null) { 846 mService.mSurfaceAnimationRunner.onAnimationCancelled(mBackColorSurface); 847 } 848 } 849 isAnimating()850 public boolean isAnimating() { 851 return mDisplayAnimator != null && mDisplayAnimator.isAnimating() 852 || mEnterBlackFrameAnimator != null && mEnterBlackFrameAnimator.isAnimating() 853 || mRotateScreenAnimator != null && mRotateScreenAnimator.isAnimating() 854 || mScreenshotRotationAnimator != null 855 && mScreenshotRotationAnimator.isAnimating(); 856 } 857 } 858 } 859