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 
21 import static com.android.server.wm.AnimationSpecProto.ROTATE;
22 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
23 import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
24 import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS;
25 import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA;
26 import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA;
27 import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
28 import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
29 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
30 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
31 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
32 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
33 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
34 
35 import android.animation.ArgbEvaluator;
36 import android.content.Context;
37 import android.graphics.Color;
38 import android.graphics.Matrix;
39 import android.graphics.Point;
40 import android.graphics.Rect;
41 import android.os.Trace;
42 import android.util.Slog;
43 import android.util.proto.ProtoOutputStream;
44 import android.view.DisplayInfo;
45 import android.view.Surface;
46 import android.view.Surface.OutOfResourcesException;
47 import android.view.SurfaceControl;
48 import android.view.animation.Animation;
49 import android.view.animation.AnimationUtils;
50 import android.view.animation.Transformation;
51 
52 import com.android.internal.R;
53 import com.android.server.protolog.common.ProtoLog;
54 import com.android.server.wm.SurfaceAnimator.AnimationType;
55 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
56 import com.android.server.wm.utils.RotationAnimationUtils;
57 
58 import java.io.PrintWriter;
59 
60 /**
61  * This class handles the rotation animation when the device is rotated.
62  *
63  * <p>
64  * The screen rotation animation is composed of 4 different part:
65  * <ul>
66  * <li> The screenshot: <p>
67  *     A screenshot of the whole screen prior the change of orientation is taken to hide the
68  *     element resizing below. The screenshot is then animated to rotate and cross-fade to
69  *     the new orientation with the content in the new orientation.
70  *
71  * <li> The windows on the display: <p>y
72  *      Once the device is rotated, the screen and its content are in the new orientation. The
73  *      animation first rotate the new content into the old orientation to then be able to
74  *      animate to the new orientation
75  *
76  * <li> The Background color frame: <p>
77  *      To have the animation seem more seamless, we add a color transitioning background behind the
78  *      exiting and entering layouts. We compute the brightness of the start and end
79  *      layouts and transition from the two brightness values as grayscale underneath the animation
80  *
81  * <li> The entering Blackframe: <p>
82  *     The enter Blackframe is similar to the exit Blackframe but is only used when a custom
83  *     rotation animation is used and matches the new content size instead of the screenshot.
84  * </ul>
85  *
86  * Each part has its own Surface which are then animated by {@link SurfaceAnimator}s.
87  */
88 class ScreenRotationAnimation {
89     private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
90 
91     /*
92      * Layers for screen rotation animation. We put these layers above
93      * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
94      */
95     private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
96     private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
97 
98     private final Context mContext;
99     private final DisplayContent mDisplayContent;
100     private final float[] mTmpFloats = new float[9];
101     private final Transformation mRotateExitTransformation = new Transformation();
102     private final Transformation mRotateEnterTransformation = new Transformation();
103     // Complete transformations being applied.
104     private final Transformation mEnterTransformation = new Transformation();
105     private final Matrix mSnapshotInitialMatrix = new Matrix();
106     private final WindowManagerService mService;
107     /** Only used for custom animations and not screen rotation. */
108     private SurfaceControl mEnterBlackFrameLayer;
109     /** This layer contains the actual screenshot that is to be faded out. */
110     private SurfaceControl mScreenshotLayer;
111     /**
112      * Only used for screen rotation and not custom animations. Layered behind all other layers
113      * to avoid showing any "empty" spots
114      */
115     private SurfaceControl mBackColorSurface;
116     private BlackFrame mEnteringBlackFrame;
117     private int mWidth, mHeight;
118 
119     private final int mOriginalRotation;
120     private final int mOriginalWidth;
121     private final int mOriginalHeight;
122     private int mCurRotation;
123 
124     private Rect mOriginalDisplayRect = new Rect();
125     private Rect mCurrentDisplayRect = new Rect();
126     // The current active animation to move from the old to the new rotated
127     // state.  Which animation is run here will depend on the old and new
128     // rotations.
129     private Animation mRotateExitAnimation;
130     private Animation mRotateEnterAnimation;
131     private Animation mRotateAlphaAnimation;
132     private boolean mStarted;
133     private boolean mAnimRunning;
134     private boolean mFinishAnimReady;
135     private long mFinishAnimStartTime;
136     private boolean mForceDefaultOrientation;
137     private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
138     /** Intensity of light/whiteness of the layout before rotation occurs. */
139     private float mStartLuma;
140     /** Intensity of light/whiteness of the layout after rotation occurs. */
141     private float mEndLuma;
142 
ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation)143     ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {
144         mService = displayContent.mWmService;
145         mContext = mService.mContext;
146         mDisplayContent = displayContent;
147         displayContent.getBounds(mOriginalDisplayRect);
148 
149         // Screenshot does NOT include rotation!
150         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
151         final int realOriginalRotation = displayInfo.rotation;
152         final int originalWidth;
153         final int originalHeight;
154         if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
155             // Emulated orientation.
156             mForceDefaultOrientation = true;
157             originalWidth = displayContent.mBaseDisplayWidth;
158             originalHeight = displayContent.mBaseDisplayHeight;
159         } else {
160             // Normal situation
161             originalWidth = displayInfo.logicalWidth;
162             originalHeight = displayInfo.logicalHeight;
163         }
164         if (realOriginalRotation == Surface.ROTATION_90
165                 || realOriginalRotation == Surface.ROTATION_270) {
166             mWidth = originalHeight;
167             mHeight = originalWidth;
168         } else {
169             mWidth = originalWidth;
170             mHeight = originalHeight;
171         }
172 
173         mOriginalRotation = originalRotation;
174         // If the delta is not zero, the rotation of display may not change, but we still want to
175         // apply rotation animation because there should be a top app shown as rotated. So the
176         // specified original rotation customizes the direction of animation to have better look
177         // when restoring the rotated app to the same rotation as current display.
178         final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation);
179         final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
180         mOriginalWidth = flipped ? originalHeight : originalWidth;
181         mOriginalHeight = flipped ? originalWidth : originalHeight;
182         mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
183 
184         // Check whether the current screen contains any secure content.
185         final boolean isSecure = displayContent.hasSecureWindowOnScreen();
186         final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
187         try {
188             mBackColorSurface = displayContent.makeChildSurface(null)
189                     .setName("BackColorSurface")
190                     .setColorLayer()
191                     .setCallsite("ScreenRotationAnimation")
192                     .build();
193 
194             mScreenshotLayer = displayContent.makeOverlay()
195                     .setName("RotationLayer")
196                     .setBufferSize(mWidth, mHeight)
197                     .setSecure(isSecure)
198                     .setCallsite("ScreenRotationAnimation")
199                     .build();
200 
201             mEnterBlackFrameLayer = displayContent.makeOverlay()
202                     .setName("EnterBlackFrameLayer")
203                     .setContainerLayer()
204                     .setCallsite("ScreenRotationAnimation")
205                     .build();
206 
207             // In case display bounds change, screenshot buffer and surface may mismatch so set a
208             // scaling mode.
209             SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
210             t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
211             t2.apply(true /* sync */);
212 
213             // Capture a screenshot into the surface we just created.
214             final int displayId = displayContent.getDisplayId();
215             final Surface surface = mService.mSurfaceFactory.get();
216             surface.copyFrom(mScreenshotLayer);
217             SurfaceControl.ScreenshotGraphicBuffer gb =
218                     mService.mDisplayManagerInternal.systemScreenshot(displayId);
219             if (gb != null) {
220                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
221                         "ScreenRotationAnimation#getMedianBorderLuma");
222                 mStartLuma = RotationAnimationUtils.getMedianBorderLuma(gb.getGraphicBuffer(),
223                         gb.getColorSpace());
224                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
225                 try {
226                     surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
227                             gb.getColorSpace());
228                 } catch (RuntimeException e) {
229                     Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
230                 }
231                 // If the screenshot contains secure layers, we have to make sure the
232                 // screenshot surface we display it in also has FLAG_SECURE so that
233                 // the user can not screenshot secure layers via the screenshot surface.
234                 if (gb.containsSecureLayers()) {
235                     t.setSecure(mScreenshotLayer, true);
236                 }
237                 t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
238                 t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
239                 t.setLayer(mBackColorSurface, -1);
240                 t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
241                 t.setAlpha(mBackColorSurface, 1);
242                 t.show(mScreenshotLayer);
243                 t.show(mBackColorSurface);
244             } else {
245                 Slog.w(TAG, "Unable to take screenshot of display " + displayId);
246             }
247             surface.destroy();
248         } catch (OutOfResourcesException e) {
249             Slog.w(TAG, "Unable to allocate freeze surface", e);
250         }
251 
252         ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
253                     "  FREEZE %s: CREATE", mScreenshotLayer);
254         setRotation(t, realOriginalRotation);
255         t.apply();
256     }
257 
dumpDebug(ProtoOutputStream proto, long fieldId)258     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
259         final long token = proto.start(fieldId);
260         proto.write(STARTED, mStarted);
261         proto.write(ANIMATION_RUNNING, mAnimRunning);
262         proto.end(token);
263     }
264 
hasScreenshot()265     boolean hasScreenshot() {
266         return mScreenshotLayer != null;
267     }
268 
setRotationTransform(SurfaceControl.Transaction t, Matrix matrix)269     private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
270         if (mScreenshotLayer == null) {
271             return;
272         }
273         matrix.getValues(mTmpFloats);
274         float x = mTmpFloats[Matrix.MTRANS_X];
275         float y = mTmpFloats[Matrix.MTRANS_Y];
276         if (mForceDefaultOrientation) {
277             mDisplayContent.getBounds(mCurrentDisplayRect);
278             x -= mCurrentDisplayRect.left;
279             y -= mCurrentDisplayRect.top;
280         }
281         t.setPosition(mScreenshotLayer, x, y);
282         t.setMatrix(mScreenshotLayer,
283                 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
284                 mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
285 
286         t.setAlpha(mScreenshotLayer, (float) 1.0);
287         t.show(mScreenshotLayer);
288     }
289 
printTo(String prefix, PrintWriter pw)290     public void printTo(String prefix, PrintWriter pw) {
291         pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer);
292         pw.print(" mWidth="); pw.print(mWidth);
293         pw.print(" mHeight="); pw.println(mHeight);
294         pw.print(prefix);
295         pw.print("mEnteringBlackFrame=");
296         pw.println(mEnteringBlackFrame);
297         if (mEnteringBlackFrame != null) {
298             mEnteringBlackFrame.printTo(prefix + "  ", pw);
299         }
300         pw.print(prefix); pw.print("mCurRotation="); pw.print(mCurRotation);
301         pw.print(" mOriginalRotation="); pw.println(mOriginalRotation);
302         pw.print(prefix); pw.print("mOriginalWidth="); pw.print(mOriginalWidth);
303         pw.print(" mOriginalHeight="); pw.println(mOriginalHeight);
304         pw.print(prefix); pw.print("mStarted="); pw.print(mStarted);
305         pw.print(" mAnimRunning="); pw.print(mAnimRunning);
306         pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady);
307         pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime);
308         pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation);
309         pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
310         pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
311         pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
312         pw.print(prefix); pw.print("mEnterTransformation=");
313         mEnterTransformation.printShortString(pw); pw.println();
314         pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
315         mSnapshotInitialMatrix.printShortString(pw);pw.println();
316         pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
317         if (mForceDefaultOrientation) {
318             pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
319             pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString());
320         }
321     }
322 
setRotation(SurfaceControl.Transaction t, int rotation)323     public void setRotation(SurfaceControl.Transaction t, int rotation) {
324         mCurRotation = rotation;
325 
326         // Compute the transformation matrix that must be applied
327         // to the snapshot to make it stay in the same original position
328         // with the current screen rotation.
329         int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
330         RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
331 
332         setRotationTransform(t, mSnapshotInitialMatrix);
333     }
334 
335     /**
336      * Returns true if animating.
337      */
startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)338     private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
339             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
340         if (mScreenshotLayer == null) {
341             // Can't do animation.
342             return false;
343         }
344         if (mStarted) {
345             return true;
346         }
347 
348         mStarted = true;
349 
350         // Figure out how the screen has moved from the original rotation.
351         int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
352 
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.screen_rotate_0_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_ENTER, 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 
getEnterTransformation()511     public Transformation getEnterTransformation() {
512         return mEnterTransformation;
513     }
514 
515     /**
516      * Utility class that runs a {@link ScreenRotationAnimation} on the {@link
517      * SurfaceAnimationRunner}.
518      * <p>
519      * The rotation animation supports both screen rotation and custom animations
520      *
521      * For custom animations:
522      * <ul>
523      *   <li>
524      *     The screenshot layer which has an added animation of it's alpha channel
525      *     ("screen_rotate_alpha") and that will be applied along with the custom animation.
526      *   </li>
527      *   <li> A device layer that is animated with the provided custom animation </li>
528      * </ul>
529      *
530      * For screen rotation:
531      * <ul>
532      *   <li> A rotation layer that is both rotated and faded out during a single animation </li>
533      *   <li> A device layer that is both rotated and faded in during a single animation </li>
534      *   <li> A background color layer that transitions colors behind the first two layers </li>
535      * </ul>
536      *
537      * {@link ScreenRotationAnimation#startAnimation(
538      *     SurfaceControl.Transaction, long, float, int, int, int, int)}.
539      * </ul>
540      *
541      * <p>
542      * Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of
543      * this three {@link SurfaceControl}s which then delegates the animation to the
544      * {@link ScreenRotationAnimation}.
545      */
546     class SurfaceRotationAnimationController {
547         private SurfaceAnimator mDisplayAnimator;
548         private SurfaceAnimator mScreenshotRotationAnimator;
549         private SurfaceAnimator mRotateScreenAnimator;
550         private SurfaceAnimator mEnterBlackFrameAnimator;
551 
startCustomAnimation()552         void startCustomAnimation() {
553             try {
554                 mService.mSurfaceAnimationRunner.deferStartingAnimations();
555                 mRotateScreenAnimator = startScreenshotAlphaAnimation();
556                 mDisplayAnimator = startDisplayRotation();
557                 if (mEnteringBlackFrame != null) {
558                     mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
559                 }
560             } finally {
561                 mService.mSurfaceAnimationRunner.continueStartingAnimations();
562             }
563         }
564 
565         /**
566          * Start the rotation animation of the display and the screenshot on the
567          * {@link SurfaceAnimationRunner}.
568          */
startScreenRotationAnimation()569         void startScreenRotationAnimation() {
570             try {
571                 mService.mSurfaceAnimationRunner.deferStartingAnimations();
572                 mDisplayAnimator = startDisplayRotation();
573                 mScreenshotRotationAnimator = startScreenshotRotationAnimation();
574                 startColorAnimation();
575             } finally {
576                 mService.mSurfaceAnimationRunner.continueStartingAnimations();
577             }
578         }
579 
initializeBuilder()580         private SimpleSurfaceAnimatable.Builder initializeBuilder() {
581             return new SimpleSurfaceAnimatable.Builder()
582                     .setPendingTransactionSupplier(mDisplayContent::getPendingTransaction)
583                     .setCommitTransactionRunnable(mDisplayContent::commitPendingTransaction)
584                     .setAnimationLeashSupplier(mDisplayContent::makeOverlay);
585         }
586 
startDisplayRotation()587         private SurfaceAnimator startDisplayRotation() {
588             return startAnimation(initializeBuilder()
589                             .setAnimationLeashParent(mDisplayContent.getSurfaceControl())
590                             .setSurfaceControl(mDisplayContent.getWindowingLayer())
591                             .setParentSurfaceControl(mDisplayContent.getSurfaceControl())
592                             .setWidth(mDisplayContent.getSurfaceWidth())
593                             .setHeight(mDisplayContent.getSurfaceHeight())
594                             .build(),
595                     createWindowAnimationSpec(mRotateEnterAnimation),
596                     this::onAnimationEnd);
597         }
598 
startScreenshotAlphaAnimation()599         private SurfaceAnimator startScreenshotAlphaAnimation() {
600             return startAnimation(initializeBuilder()
601                             .setSurfaceControl(mScreenshotLayer)
602                             .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
603                             .setWidth(mDisplayContent.getSurfaceWidth())
604                             .setHeight(mDisplayContent.getSurfaceHeight())
605                             .build(),
606                     createWindowAnimationSpec(mRotateAlphaAnimation),
607                     this::onAnimationEnd);
608         }
609 
startEnterBlackFrameAnimation()610         private SurfaceAnimator startEnterBlackFrameAnimation() {
611             return startAnimation(initializeBuilder()
612                             .setSurfaceControl(mEnterBlackFrameLayer)
613                             .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
614                             .build(),
615                     createWindowAnimationSpec(mRotateEnterAnimation),
616                     this::onAnimationEnd);
617         }
618 
startScreenshotRotationAnimation()619         private SurfaceAnimator startScreenshotRotationAnimation() {
620             return startAnimation(initializeBuilder()
621                             .setSurfaceControl(mScreenshotLayer)
622                             .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
623                             .build(),
624                     createWindowAnimationSpec(mRotateExitAnimation),
625                     this::onAnimationEnd);
626         }
627 
628 
629         /**
630          * Applies the color change from {@link #mStartLuma} to {@link #mEndLuma} as a
631          * grayscale color
632          */
startColorAnimation()633         private void startColorAnimation() {
634             int colorTransitionMs = mContext.getResources().getInteger(
635                     R.integer.config_screen_rotation_color_transition);
636             final SurfaceAnimationRunner runner = mService.mSurfaceAnimationRunner;
637             final float[] rgbTmpFloat = new float[3];
638             final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
639             final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
640             final long duration = colorTransitionMs * (long) mService.getCurrentAnimatorScale();
641             final ArgbEvaluator va = ArgbEvaluator.getInstance();
642             runner.startAnimation(
643                 new LocalAnimationAdapter.AnimationSpec() {
644                     @Override
645                     public long getDuration() {
646                         return duration;
647                     }
648 
649                     @Override
650                     public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
651                         long currentPlayTime) {
652                         final float fraction = getFraction(currentPlayTime);
653                         final int color = (Integer) va.evaluate(fraction, startColor, endColor);
654                         Color middleColor = Color.valueOf(color);
655                         rgbTmpFloat[0] = middleColor.red();
656                         rgbTmpFloat[1] = middleColor.green();
657                         rgbTmpFloat[2] = middleColor.blue();
658                         if (leash.isValid()) {
659                             t.setColor(leash, rgbTmpFloat);
660                         }
661                     }
662 
663                     @Override
664                     public void dump(PrintWriter pw, String prefix) {
665                         pw.println(prefix + "startLuma=" + mStartLuma
666                                 + " endLuma=" + mEndLuma
667                                 + " durationMs=" + colorTransitionMs);
668                     }
669 
670                     @Override
671                     public void dumpDebugInner(ProtoOutputStream proto) {
672                         final long token = proto.start(ROTATE);
673                         proto.write(START_LUMA, mStartLuma);
674                         proto.write(END_LUMA, mEndLuma);
675                         proto.write(DURATION_MS, colorTransitionMs);
676                         proto.end(token);
677                     }
678                 },
679                 mBackColorSurface, mDisplayContent.getPendingTransaction(), null);
680         }
681 
createWindowAnimationSpec(Animation mAnimation)682         private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) {
683             return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */,
684                     false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */);
685         }
686 
687         /**
688          * Start an animation defined by animationSpec on a new {@link SurfaceAnimator}.
689          *
690          * @param animatable The animatable used for the animation.
691          * @param animationSpec The spec of the animation.
692          * @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator}
693          *                                    and called when the animation finishes.
694          * @return The newly created {@link SurfaceAnimator} that as been started.
695          */
startAnimation( SurfaceAnimator.Animatable animatable, LocalAnimationAdapter.AnimationSpec animationSpec, OnAnimationFinishedCallback animationFinishedCallback)696         private SurfaceAnimator startAnimation(
697                 SurfaceAnimator.Animatable animatable,
698                 LocalAnimationAdapter.AnimationSpec animationSpec,
699                 OnAnimationFinishedCallback animationFinishedCallback) {
700             SurfaceAnimator animator = new SurfaceAnimator(
701                     animatable, animationFinishedCallback, mService);
702 
703             LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
704                     animationSpec, mService.mSurfaceAnimationRunner);
705             animator.startAnimation(mDisplayContent.getPendingTransaction(),
706                     localAnimationAdapter, false, ANIMATION_TYPE_SCREEN_ROTATION);
707             return animator;
708         }
709 
onAnimationEnd(@nimationType int type, AnimationAdapter anim)710         private void onAnimationEnd(@AnimationType int type, AnimationAdapter anim) {
711             synchronized (mService.mGlobalLock) {
712                 if (isAnimating()) {
713                     ProtoLog.v(WM_DEBUG_ORIENTATION,
714                             "ScreenRotation still animating: type: %d\n"
715                                     + "mDisplayAnimator: %s\n"
716                                     + "mEnterBlackFrameAnimator: %s\n"
717                                     + "mRotateScreenAnimator: %s\n"
718                                     + "mScreenshotRotationAnimator: %s",
719                             type,
720                             mDisplayAnimator != null
721                                     ? mDisplayAnimator.isAnimating() : null,
722                             mEnterBlackFrameAnimator != null
723                                     ? mEnterBlackFrameAnimator.isAnimating() : null,
724                             mRotateScreenAnimator != null
725                                     ? mRotateScreenAnimator.isAnimating() : null,
726                             mScreenshotRotationAnimator != null
727                                     ? mScreenshotRotationAnimator.isAnimating() : null
728                     );
729                     return;
730                 }
731                 ProtoLog.d(WM_DEBUG_ORIENTATION, "ScreenRotationAnimation onAnimationEnd");
732                 mEnterBlackFrameAnimator = null;
733                 mScreenshotRotationAnimator = null;
734                 mRotateScreenAnimator = null;
735                 mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION;
736                 kill();
737                 mService.updateRotation(false, false);
738             }
739         }
740 
cancel()741         public void cancel() {
742             if (mEnterBlackFrameAnimator != null) {
743                 mEnterBlackFrameAnimator.cancelAnimation();
744             }
745             if (mScreenshotRotationAnimator != null) {
746                 mScreenshotRotationAnimator.cancelAnimation();
747             }
748 
749             if (mRotateScreenAnimator != null) {
750                 mRotateScreenAnimator.cancelAnimation();
751             }
752 
753             if (mDisplayAnimator != null) {
754                 mDisplayAnimator.cancelAnimation();
755             }
756 
757             if (mBackColorSurface != null) {
758                 mService.mSurfaceAnimationRunner.onAnimationCancelled(mBackColorSurface);
759             }
760         }
761 
isAnimating()762         public boolean isAnimating() {
763             return mDisplayAnimator != null && mDisplayAnimator.isAnimating()
764                     || mEnterBlackFrameAnimator != null && mEnterBlackFrameAnimator.isAnimating()
765                     || mRotateScreenAnimator != null && mRotateScreenAnimator.isAnimating()
766                     || mScreenshotRotationAnimator != null
767                     && mScreenshotRotationAnimator.isAnimating();
768         }
769     }
770 }
771