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.util.TimeUtils.NANOS_PER_MS;
20 import static android.view.Choreographer.CALLBACK_TRAVERSAL;
21 import static android.view.Choreographer.getSfInstance;
22 
23 import android.animation.AnimationHandler;
24 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
25 import android.animation.Animator;
26 import android.animation.AnimatorListenerAdapter;
27 import android.animation.ValueAnimator;
28 import android.annotation.Nullable;
29 import android.util.ArrayMap;
30 import android.view.Choreographer;
31 import android.view.SurfaceControl;
32 import android.view.SurfaceControl.Transaction;
33 
34 import com.android.internal.annotations.GuardedBy;
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
37 import com.android.server.AnimationThread;
38 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
39 
40 /**
41  * Class to run animations without holding the window manager lock.
42  */
43 class SurfaceAnimationRunner {
44 
45     private final Object mLock = new Object();
46 
47     /**
48      * Lock for cancelling animations. Must be acquired on it's own, or after acquiring
49      * {@link #mLock}
50      */
51     private final Object mCancelLock = new Object();
52 
53     @VisibleForTesting
54     Choreographer mChoreographer;
55 
56     private final Runnable mApplyTransactionRunnable = this::applyTransaction;
57     private final AnimationHandler mAnimationHandler;
58     private final Transaction mFrameTransaction;
59     private final AnimatorFactory mAnimatorFactory;
60     private boolean mApplyScheduled;
61 
62     @GuardedBy("mLock")
63     @VisibleForTesting
64     final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
65 
66     @GuardedBy("mLock")
67     @VisibleForTesting
68     final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>();
69 
70     @GuardedBy("mLock")
71     private boolean mAnimationStartDeferred;
72 
SurfaceAnimationRunner()73     SurfaceAnimationRunner() {
74         this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
75     }
76 
77     @VisibleForTesting
SurfaceAnimationRunner(@ullable AnimationFrameCallbackProvider callbackProvider, AnimatorFactory animatorFactory, Transaction frameTransaction)78     SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
79             AnimatorFactory animatorFactory, Transaction frameTransaction) {
80         SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
81                 0 /* timeout */);
82         mFrameTransaction = frameTransaction;
83         mAnimationHandler = new AnimationHandler();
84         mAnimationHandler.setProvider(callbackProvider != null
85                 ? callbackProvider
86                 : new SfVsyncFrameCallbackProvider(mChoreographer));
87         mAnimatorFactory = animatorFactory != null
88                 ? animatorFactory
89                 : SfValueAnimator::new;
90     }
91 
92     /**
93      * Defers starting of animations until {@link #continueStartingAnimations} is called. This
94      * method is NOT nestable.
95      *
96      * @see #continueStartingAnimations
97      */
deferStartingAnimations()98     void deferStartingAnimations() {
99         synchronized (mLock) {
100             mAnimationStartDeferred = true;
101         }
102     }
103 
104     /**
105      * Continues starting of animations.
106      *
107      * @see #deferStartingAnimations
108      */
continueStartingAnimations()109     void continueStartingAnimations() {
110         synchronized (mLock) {
111             mAnimationStartDeferred = false;
112             if (!mPendingAnimations.isEmpty()) {
113                 mChoreographer.postFrameCallback(this::startAnimations);
114             }
115         }
116     }
117 
startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, Runnable finishCallback)118     void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
119             Runnable finishCallback) {
120         synchronized (mLock) {
121             final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
122                     finishCallback);
123             mPendingAnimations.put(animationLeash, runningAnim);
124             if (!mAnimationStartDeferred) {
125                 mChoreographer.postFrameCallback(this::startAnimations);
126             }
127 
128             // Some animations (e.g. move animations) require the initial transform to be applied
129             // immediately.
130             applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
131         }
132     }
133 
onAnimationCancelled(SurfaceControl leash)134     void onAnimationCancelled(SurfaceControl leash) {
135         synchronized (mLock) {
136             if (mPendingAnimations.containsKey(leash)) {
137                 mPendingAnimations.remove(leash);
138                 return;
139             }
140             final RunningAnimation anim = mRunningAnimations.get(leash);
141             if (anim != null) {
142                 mRunningAnimations.remove(leash);
143                 synchronized (mCancelLock) {
144                     anim.mCancelled = true;
145                 }
146                 SurfaceAnimationThread.getHandler().post(() -> {
147                     anim.mAnim.cancel();
148                     applyTransaction();
149                 });
150             }
151         }
152     }
153 
154     @GuardedBy("mLock")
startPendingAnimationsLocked()155     private void startPendingAnimationsLocked() {
156         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
157             startAnimationLocked(mPendingAnimations.valueAt(i));
158         }
159         mPendingAnimations.clear();
160     }
161 
162     @GuardedBy("mLock")
startAnimationLocked(RunningAnimation a)163     private void startAnimationLocked(RunningAnimation a) {
164         final ValueAnimator anim = mAnimatorFactory.makeAnimator();
165 
166         // Animation length is already expected to be scaled.
167         anim.overrideDurationScale(1.0f);
168         anim.setDuration(a.mAnimSpec.getDuration());
169         anim.addUpdateListener(animation -> {
170             synchronized (mCancelLock) {
171                 if (!a.mCancelled) {
172                     final long duration = anim.getDuration();
173                     long currentPlayTime = anim.getCurrentPlayTime();
174                     if (currentPlayTime > duration) {
175                         currentPlayTime = duration;
176                     }
177                     applyTransformation(a, mFrameTransaction, currentPlayTime);
178                 }
179             }
180 
181             // Transaction will be applied in the commit phase.
182             scheduleApplyTransaction();
183         });
184 
185         anim.addListener(new AnimatorListenerAdapter() {
186             @Override
187             public void onAnimationStart(Animator animation) {
188                 synchronized (mCancelLock) {
189                     if (!a.mCancelled) {
190                         mFrameTransaction.show(a.mLeash);
191                     }
192                 }
193             }
194 
195             @Override
196             public void onAnimationEnd(Animator animation) {
197                 synchronized (mLock) {
198                     mRunningAnimations.remove(a.mLeash);
199                     synchronized (mCancelLock) {
200                         if (!a.mCancelled) {
201 
202                             // Post on other thread that we can push final state without jank.
203                             AnimationThread.getHandler().post(a.mFinishCallback);
204                         }
205                     }
206                 }
207             }
208         });
209         a.mAnim = anim;
210         mRunningAnimations.put(a.mLeash, a);
211 
212         anim.start();
213         if (a.mAnimSpec.canSkipFirstFrame()) {
214             // If we can skip the first frame, we start one frame later.
215             anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
216         }
217 
218         // Immediately start the animation by manually applying an animation frame. Otherwise, the
219         // start time would only be set in the next frame, leading to a delay.
220         anim.doAnimationFrame(mChoreographer.getFrameTime());
221     }
222 
applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime)223     private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
224         if (a.mAnimSpec.needsEarlyWakeup()) {
225             t.setEarlyWakeup();
226         }
227         a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
228     }
229 
startAnimations(long frameTimeNanos)230     private void startAnimations(long frameTimeNanos) {
231         synchronized (mLock) {
232             startPendingAnimationsLocked();
233         }
234     }
235 
scheduleApplyTransaction()236     private void scheduleApplyTransaction() {
237         if (!mApplyScheduled) {
238             mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
239                     null /* token */);
240             mApplyScheduled = true;
241         }
242     }
243 
applyTransaction()244     private void applyTransaction() {
245         mFrameTransaction.setAnimationTransaction();
246         mFrameTransaction.apply();
247         mApplyScheduled = false;
248     }
249 
250     private static final class RunningAnimation {
251         final AnimationSpec mAnimSpec;
252         final SurfaceControl mLeash;
253         final Runnable mFinishCallback;
254         ValueAnimator mAnim;
255 
256         @GuardedBy("mCancelLock")
257         private boolean mCancelled;
258 
RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback)259         RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
260             mAnimSpec = animSpec;
261             mLeash = leash;
262             mFinishCallback = finishCallback;
263         }
264     }
265 
266     @VisibleForTesting
267     interface AnimatorFactory {
makeAnimator()268         ValueAnimator makeAnimator();
269     }
270 
271     /**
272      * Value animator that uses sf-vsync signal to tick.
273      */
274     private class SfValueAnimator extends ValueAnimator {
275 
SfValueAnimator()276         SfValueAnimator() {
277             setFloatValues(0f, 1f);
278         }
279 
280         @Override
getAnimationHandler()281         public AnimationHandler getAnimationHandler() {
282             return mAnimationHandler;
283         }
284     }
285 }
286