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