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