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