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