1 /*
2  * Copyright (C) 2017 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.TimeUtils.NANOS_PER_MS;
21 import static android.view.Choreographer.CALLBACK_TRAVERSAL;
22 import static android.view.Choreographer.getSfInstance;
23 
24 import android.animation.AnimationHandler;
25 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
26 import android.animation.Animator;
27 import android.animation.AnimatorListenerAdapter;
28 import android.animation.ValueAnimator;
29 import android.annotation.Nullable;
30 import android.graphics.BitmapShader;
31 import android.graphics.Canvas;
32 import android.graphics.Insets;
33 import android.graphics.Paint;
34 import android.graphics.PixelFormat;
35 import android.graphics.Rect;
36 import android.hardware.power.Boost;
37 import android.os.Handler;
38 import android.os.PowerManagerInternal;
39 import android.os.Trace;
40 import android.util.ArrayMap;
41 import android.util.Log;
42 import android.view.Choreographer;
43 import android.view.Surface;
44 import android.view.SurfaceControl;
45 import android.view.SurfaceControl.Transaction;
46 import android.view.animation.Animation;
47 import android.view.animation.Transformation;
48 import android.window.ScreenCapture;
49 
50 import com.android.internal.annotations.GuardedBy;
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
53 import com.android.server.AnimationThread;
54 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
55 
56 import java.util.ArrayList;
57 import java.util.concurrent.ExecutorService;
58 import java.util.concurrent.Executors;
59 import java.util.function.Supplier;
60 
61 /**
62  * Class to run animations without holding the window manager lock.
63  */
64 class SurfaceAnimationRunner {
65 
66     private final Object mLock = new Object();
67 
68     /**
69      * Lock for cancelling animations. Must be acquired on it's own, or after acquiring
70      * {@link #mLock}
71      */
72     private final Object mCancelLock = new Object();
73 
74     /**
75      * Lock for synchronizing {@link #mEdgeExtensions} to prevent race conditions when managing
76      * created edge extension surfaces.
77      */
78     private final Object mEdgeExtensionLock = new Object();
79 
80     @VisibleForTesting
81     Choreographer mChoreographer;
82 
83     private final Handler mAnimationThreadHandler = AnimationThread.getHandler();
84     private final Handler mSurfaceAnimationHandler = SurfaceAnimationThread.getHandler();
85     private final Runnable mApplyTransactionRunnable = this::applyTransaction;
86     private final AnimationHandler mAnimationHandler;
87     private final Transaction mFrameTransaction;
88     private final AnimatorFactory mAnimatorFactory;
89     private final PowerManagerInternal mPowerManagerInternal;
90     private boolean mApplyScheduled;
91 
92     // Executor to perform the edge extension.
93     // With two threads because in practice we will want to extend two surfaces in one animation,
94     // in which case we want to be able to parallelize those two extensions to cut down latency in
95     // starting the animation.
96     private final ExecutorService mEdgeExtensionExecutor = Executors.newFixedThreadPool(2);
97 
98     @GuardedBy("mLock")
99     @VisibleForTesting
100     final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
101 
102     @GuardedBy("mLock")
103     @VisibleForTesting
104     final ArrayMap<SurfaceControl, RunningAnimation> mPreProcessingAnimations = new ArrayMap<>();
105 
106     @GuardedBy("mLock")
107     @VisibleForTesting
108     final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>();
109 
110     @GuardedBy("mLock")
111     private boolean mAnimationStartDeferred;
112 
113     // Mapping animation leashes to a list of edge extension surfaces associated with them
114     @GuardedBy("mEdgeExtensionLock")
115     private final ArrayMap<SurfaceControl, ArrayList<SurfaceControl>> mEdgeExtensions =
116             new ArrayMap<>();
117 
118     /**
119      * There should only ever be one instance of this class. Usual spot for it is with
120      * {@link WindowManagerService}
121      */
SurfaceAnimationRunner(Supplier<Transaction> transactionFactory, PowerManagerInternal powerManagerInternal)122     SurfaceAnimationRunner(Supplier<Transaction> transactionFactory,
123             PowerManagerInternal powerManagerInternal) {
124         this(null /* callbackProvider */, null /* animatorFactory */,
125                 transactionFactory.get(), powerManagerInternal);
126     }
127 
128     @VisibleForTesting
SurfaceAnimationRunner(@ullable AnimationFrameCallbackProvider callbackProvider, AnimatorFactory animatorFactory, Transaction frameTransaction, PowerManagerInternal powerManagerInternal)129     SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
130             AnimatorFactory animatorFactory, Transaction frameTransaction,
131             PowerManagerInternal powerManagerInternal) {
132         mSurfaceAnimationHandler.runWithScissors(() -> mChoreographer = getSfInstance(),
133                 0 /* timeout */);
134         mFrameTransaction = frameTransaction;
135         mAnimationHandler = new AnimationHandler();
136         mAnimationHandler.setProvider(callbackProvider != null
137                 ? callbackProvider
138                 : new SfVsyncFrameCallbackProvider(mChoreographer));
139         mAnimatorFactory = animatorFactory != null
140                 ? animatorFactory
141                 : SfValueAnimator::new;
142         mPowerManagerInternal = powerManagerInternal;
143     }
144 
145     /**
146      * Defers starting of animations until {@link #continueStartingAnimations} is called. This
147      * method is NOT nestable.
148      *
149      * @see #continueStartingAnimations
150      */
deferStartingAnimations()151     void deferStartingAnimations() {
152         synchronized (mLock) {
153             mAnimationStartDeferred = true;
154         }
155     }
156 
157     /**
158      * Continues starting of animations.
159      *
160      * @see #deferStartingAnimations
161      */
continueStartingAnimations()162     void continueStartingAnimations() {
163         synchronized (mLock) {
164             mAnimationStartDeferred = false;
165             if (!mPendingAnimations.isEmpty() && mPreProcessingAnimations.isEmpty()) {
166                 mChoreographer.postFrameCallback(this::startAnimations);
167             }
168         }
169     }
170 
startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, Runnable finishCallback)171     void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
172             Runnable finishCallback) {
173         synchronized (mLock) {
174             final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
175                     finishCallback);
176             boolean requiresEdgeExtension = requiresEdgeExtension(a);
177 
178             if (requiresEdgeExtension) {
179                 final ArrayList<SurfaceControl> extensionSurfaces = new ArrayList<>();
180                 synchronized (mEdgeExtensionLock) {
181                     mEdgeExtensions.put(animationLeash, extensionSurfaces);
182                 }
183 
184                 mPreProcessingAnimations.put(animationLeash, runningAnim);
185 
186                 // We must wait for t to be committed since otherwise the leash doesn't have the
187                 // windows we want to screenshot and extend as children.
188                 t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> {
189                     final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();
190 
191                     final Transaction edgeExtensionCreationTransaction = new Transaction();
192                     edgeExtendWindow(animationLeash,
193                             animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
194                             edgeExtensionCreationTransaction);
195 
196                     synchronized (mLock) {
197                         // only run if animation is not yet canceled by this point
198                         if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {
199                             // In the case the animation is cancelled, edge extensions are removed
200                             // onAnimationLeashLost which is called before onAnimationCancelled.
201                             // So we need to check if the edge extensions have already been removed
202                             // or not, and if so we don't want to apply the transaction.
203                             synchronized (mEdgeExtensionLock) {
204                                 if (!mEdgeExtensions.isEmpty()) {
205                                     edgeExtensionCreationTransaction.apply();
206                                 }
207                             }
208 
209                             mPreProcessingAnimations.remove(animationLeash);
210                             mPendingAnimations.put(animationLeash, runningAnim);
211                             if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
212                                 mChoreographer.postFrameCallback(this::startAnimations);
213                             }
214                         }
215                     }
216                 });
217             }
218 
219             if (!requiresEdgeExtension) {
220                 mPendingAnimations.put(animationLeash, runningAnim);
221                 if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
222                     mChoreographer.postFrameCallback(this::startAnimations);
223                 }
224             }
225 
226             // Some animations (e.g. move animations) require the initial transform to be
227             // applied immediately.
228             applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
229         }
230     }
231 
requiresEdgeExtension(AnimationSpec a)232     private boolean requiresEdgeExtension(AnimationSpec a) {
233         return a.asWindowAnimationSpec() != null && a.asWindowAnimationSpec().hasExtension();
234     }
235 
onAnimationCancelled(SurfaceControl leash)236     void onAnimationCancelled(SurfaceControl leash) {
237         synchronized (mLock) {
238             if (mPendingAnimations.containsKey(leash)) {
239                 mPendingAnimations.remove(leash);
240                 return;
241             }
242             if (mPreProcessingAnimations.containsKey(leash)) {
243                 mPreProcessingAnimations.remove(leash);
244                 return;
245             }
246             final RunningAnimation anim = mRunningAnimations.get(leash);
247             if (anim != null) {
248                 mRunningAnimations.remove(leash);
249                 synchronized (mCancelLock) {
250                     anim.mCancelled = true;
251                 }
252                 mSurfaceAnimationHandler.post(() -> {
253                     anim.mAnim.cancel();
254                     applyTransaction();
255                 });
256             }
257         }
258     }
259 
260     @GuardedBy("mLock")
startPendingAnimationsLocked()261     private void startPendingAnimationsLocked() {
262         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
263             startAnimationLocked(mPendingAnimations.valueAt(i));
264         }
265         mPendingAnimations.clear();
266     }
267 
268     @GuardedBy("mLock")
startAnimationLocked(RunningAnimation a)269     private void startAnimationLocked(RunningAnimation a) {
270         final ValueAnimator anim = mAnimatorFactory.makeAnimator();
271 
272         // Animation length is already expected to be scaled.
273         anim.overrideDurationScale(1.0f);
274         anim.setDuration(a.mAnimSpec.getDuration());
275         anim.addUpdateListener(animation -> {
276             synchronized (mCancelLock) {
277                 if (!a.mCancelled) {
278                     final long duration = anim.getDuration();
279                     long currentPlayTime = anim.getCurrentPlayTime();
280                     if (currentPlayTime > duration) {
281                         currentPlayTime = duration;
282                     }
283                     applyTransformation(a, mFrameTransaction, currentPlayTime);
284                 }
285             }
286 
287             // Transaction will be applied in the commit phase.
288             scheduleApplyTransaction();
289         });
290 
291         anim.addListener(new AnimatorListenerAdapter() {
292             @Override
293             public void onAnimationStart(Animator animation) {
294                 synchronized (mCancelLock) {
295                     if (!a.mCancelled) {
296                         // TODO: change this back to use show instead of alpha when b/138459974 is
297                         // fixed.
298                         mFrameTransaction.setAlpha(a.mLeash, 1);
299                     }
300                 }
301             }
302 
303             @Override
304             public void onAnimationEnd(Animator animation) {
305                 synchronized (mLock) {
306                     mRunningAnimations.remove(a.mLeash);
307                     synchronized (mCancelLock) {
308                         if (!a.mCancelled) {
309 
310                             // Post on other thread that we can push final state without jank.
311                             mAnimationThreadHandler.post(a.mFinishCallback);
312                         }
313                     }
314                 }
315             }
316         });
317         a.mAnim = anim;
318         mRunningAnimations.put(a.mLeash, a);
319 
320         anim.start();
321         if (a.mAnimSpec.canSkipFirstFrame()) {
322             // If we can skip the first frame, we start one frame later.
323             anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
324         }
325 
326         // Immediately start the animation by manually applying an animation frame. Otherwise, the
327         // start time would only be set in the next frame, leading to a delay.
328         anim.doAnimationFrame(mChoreographer.getFrameTime());
329     }
330 
applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime)331     private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
332         a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
333     }
334 
startAnimations(long frameTimeNanos)335     private void startAnimations(long frameTimeNanos) {
336         synchronized (mLock) {
337             if (!mPreProcessingAnimations.isEmpty()) {
338                 // We only want to start running animations once all mPreProcessingAnimations have
339                 // been processed to ensure preprocessed animations start in sync.
340                 // NOTE: This means we might delay running animations that require preprocessing if
341                 // new animations that also require preprocessing are requested before the previous
342                 // ones have finished (see b/227449117).
343                 return;
344             }
345             startPendingAnimationsLocked();
346         }
347         mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
348     }
349 
scheduleApplyTransaction()350     private void scheduleApplyTransaction() {
351         if (!mApplyScheduled) {
352             mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
353                     null /* token */);
354             mApplyScheduled = true;
355         }
356     }
357 
applyTransaction()358     private void applyTransaction() {
359         mFrameTransaction.setAnimationTransaction();
360         mFrameTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
361         mFrameTransaction.apply();
362         mApplyScheduled = false;
363     }
364 
edgeExtendWindow(SurfaceControl leash, Rect bounds, Animation a, Transaction transaction)365     private void edgeExtendWindow(SurfaceControl leash, Rect bounds, Animation a,
366             Transaction transaction) {
367         final Transformation transformationAtStart = new Transformation();
368         a.getTransformationAt(0, transformationAtStart);
369         final Transformation transformationAtEnd = new Transformation();
370         a.getTransformationAt(1, transformationAtEnd);
371 
372         // We want to create an extension surface that is the maximal size and the animation will
373         // take care of cropping any part that overflows.
374         final Insets maxExtensionInsets = Insets.min(
375                 transformationAtStart.getInsets(), transformationAtEnd.getInsets());
376 
377         final int targetSurfaceHeight = bounds.height();
378         final int targetSurfaceWidth = bounds.width();
379 
380         if (maxExtensionInsets.left < 0) {
381             final Rect edgeBounds = new Rect(bounds.left, bounds.top, bounds.left + 1,
382                     bounds.bottom);
383             final Rect extensionRect = new Rect(0, 0,
384                     -maxExtensionInsets.left, targetSurfaceHeight);
385             final int xPos = bounds.left + maxExtensionInsets.left;
386             final int yPos = bounds.top;
387             createExtensionSurface(leash, edgeBounds,
388                     extensionRect, xPos, yPos, "Left Edge Extension", transaction);
389         }
390 
391         if (maxExtensionInsets.top < 0) {
392             final Rect edgeBounds = new Rect(bounds.left, bounds.top, targetSurfaceWidth,
393                     bounds.top + 1);
394             final Rect extensionRect = new Rect(0, 0,
395                     targetSurfaceWidth, -maxExtensionInsets.top);
396             final int xPos = bounds.left;
397             final int yPos = bounds.top + maxExtensionInsets.top;
398             createExtensionSurface(leash, edgeBounds,
399                     extensionRect, xPos, yPos, "Top Edge Extension", transaction);
400         }
401 
402         if (maxExtensionInsets.right < 0) {
403             final Rect edgeBounds = new Rect(bounds.right - 1, bounds.top, bounds.right,
404                     bounds.bottom);
405             final Rect extensionRect = new Rect(0, 0,
406                     -maxExtensionInsets.right, targetSurfaceHeight);
407             final int xPos = bounds.right;
408             final int yPos = bounds.top;
409             createExtensionSurface(leash, edgeBounds,
410                     extensionRect, xPos, yPos, "Right Edge Extension", transaction);
411         }
412 
413         if (maxExtensionInsets.bottom < 0) {
414             final Rect edgeBounds = new Rect(bounds.left, bounds.bottom - 1,
415                     bounds.right, bounds.bottom);
416             final Rect extensionRect = new Rect(0, 0,
417                     targetSurfaceWidth, -maxExtensionInsets.bottom);
418             final int xPos = bounds.left;
419             final int yPos = bounds.bottom;
420             createExtensionSurface(leash, edgeBounds,
421                     extensionRect, xPos, yPos, "Bottom Edge Extension", transaction);
422         }
423     }
424 
createExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction)425     private void createExtensionSurface(SurfaceControl leash, Rect edgeBounds,
426             Rect extensionRect, int xPos, int yPos, String layerName,
427             Transaction startTransaction) {
428         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createExtensionSurface");
429         doCreateExtensionSurface(leash, edgeBounds, extensionRect, xPos, yPos, layerName,
430                 startTransaction);
431         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
432     }
433 
doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction)434     private void doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds,
435             Rect extensionRect, int xPos, int yPos, String layerName,
436             Transaction startTransaction) {
437         ScreenCapture.LayerCaptureArgs captureArgs =
438                 new ScreenCapture.LayerCaptureArgs.Builder(leash /* surfaceToExtend */)
439                         .setSourceCrop(edgeBounds)
440                         .setFrameScale(1)
441                         .setPixelFormat(PixelFormat.RGBA_8888)
442                         .setChildrenOnly(true)
443                         .setAllowProtected(true)
444                         .setCaptureSecureLayers(true)
445                         .build();
446         final ScreenCapture.ScreenshotHardwareBuffer edgeBuffer =
447                 ScreenCapture.captureLayers(captureArgs);
448 
449         if (edgeBuffer == null) {
450             // The leash we are trying to screenshot may have been removed by this point, which is
451             // likely the reason for ending up with a null edgeBuffer, in which case we just want to
452             // return and do nothing.
453             Log.e("SurfaceAnimationRunner", "Failed to create edge extension - "
454                     + "edge buffer is null");
455             return;
456         }
457 
458         final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
459                 .setName(layerName)
460                 .setHidden(true)
461                 .setCallsite("DefaultTransitionHandler#startAnimation")
462                 .setOpaque(true)
463                 .setBufferSize(extensionRect.width(), extensionRect.height())
464                 .build();
465 
466         BitmapShader shader = new BitmapShader(edgeBuffer.asBitmap(),
467                 android.graphics.Shader.TileMode.CLAMP,
468                 android.graphics.Shader.TileMode.CLAMP);
469         final Paint paint = new Paint();
470         paint.setShader(shader);
471 
472         final Surface surface = new Surface(edgeExtensionLayer);
473         Canvas c = surface.lockHardwareCanvas();
474         c.drawRect(extensionRect, paint);
475         surface.unlockCanvasAndPost(c);
476         surface.release();
477 
478         synchronized (mEdgeExtensionLock) {
479             if (!mEdgeExtensions.containsKey(leash)) {
480                 // The animation leash has already been removed, so we don't want to attach the
481                 // edgeExtension layer and should immediately remove it instead.
482                 startTransaction.remove(edgeExtensionLayer);
483                 return;
484             }
485 
486             startTransaction.reparent(edgeExtensionLayer, leash);
487             startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
488             startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
489             startTransaction.setVisibility(edgeExtensionLayer, true);
490 
491             mEdgeExtensions.get(leash).add(edgeExtensionLayer);
492         }
493     }
494 
getScaleXForExtensionSurface(Rect edgeBounds, Rect extensionRect)495     private float getScaleXForExtensionSurface(Rect edgeBounds, Rect extensionRect) {
496         if (edgeBounds.width() == extensionRect.width()) {
497             // Top or bottom edge extension, no need to scale the X axis of the extension surface.
498             return 1;
499         }
500         if (edgeBounds.width() == 1) {
501             // Left or right edge extension, scale the surface to be the extensionRect's width.
502             return extensionRect.width();
503         }
504 
505         throw new RuntimeException("Unexpected edgeBounds and extensionRect widths");
506     }
507 
getScaleYForExtensionSurface(Rect edgeBounds, Rect extensionRect)508     private float getScaleYForExtensionSurface(Rect edgeBounds, Rect extensionRect) {
509         if (edgeBounds.height() == extensionRect.height()) {
510             // Left or right edge extension, no need to scale the Y axis of the extension surface.
511             return 1;
512         }
513         if (edgeBounds.height() == 1) {
514             // Top or bottom edge extension, scale the surface to be the extensionRect's height.
515             return extensionRect.height();
516         }
517 
518         throw new RuntimeException("Unexpected edgeBounds and extensionRect heights");
519     }
520 
521     private static final class RunningAnimation {
522         final AnimationSpec mAnimSpec;
523         final SurfaceControl mLeash;
524         final Runnable mFinishCallback;
525         ValueAnimator mAnim;
526 
527         @GuardedBy("mCancelLock")
528         private boolean mCancelled;
529 
RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback)530         RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
531             mAnimSpec = animSpec;
532             mLeash = leash;
533             mFinishCallback = finishCallback;
534         }
535     }
536 
onAnimationLeashLost(SurfaceControl animationLeash, Transaction t)537     protected void onAnimationLeashLost(SurfaceControl animationLeash,
538             Transaction t) {
539         synchronized (mEdgeExtensionLock) {
540             if (!mEdgeExtensions.containsKey(animationLeash)) {
541                 return;
542             }
543 
544             final ArrayList<SurfaceControl> edgeExtensions = mEdgeExtensions.get(animationLeash);
545             for (int i = 0; i < edgeExtensions.size(); i++) {
546                 final SurfaceControl extension = edgeExtensions.get(i);
547                 t.remove(extension);
548             }
549             mEdgeExtensions.remove(animationLeash);
550         }
551     }
552 
553     @VisibleForTesting
554     interface AnimatorFactory {
makeAnimator()555         ValueAnimator makeAnimator();
556     }
557 
558     /**
559      * Value animator that uses sf-vsync signal to tick.
560      */
561     private class SfValueAnimator extends ValueAnimator {
562 
SfValueAnimator()563         SfValueAnimator() {
564             setFloatValues(0f, 1f);
565         }
566 
567         @Override
getAnimationHandler()568         public AnimationHandler getAnimationHandler() {
569             return mAnimationHandler;
570         }
571     }
572 }