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