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