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