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