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