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
17package com.android.server.wm;
18
19import static android.util.TimeUtils.NANOS_PER_MS;
20import static android.view.Choreographer.CALLBACK_TRAVERSAL;
21import static android.view.Choreographer.getSfInstance;
22
23import android.animation.AnimationHandler;
24import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
25import android.animation.Animator;
26import android.animation.AnimatorListenerAdapter;
27import android.animation.ValueAnimator;
28import android.annotation.Nullable;
29import android.util.ArrayMap;
30import android.view.Choreographer;
31import android.view.SurfaceControl;
32import android.view.SurfaceControl.Transaction;
33
34import com.android.internal.annotations.GuardedBy;
35import com.android.internal.annotations.VisibleForTesting;
36import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
37import com.android.server.AnimationThread;
38import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
39
40/**
41 * Class to run animations without holding the window manager lock.
42 */
43class 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
73    SurfaceAnimationRunner() {
74        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
75    }
76
77    @VisibleForTesting
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     */
98    void deferStartingAnimations() {
99        synchronized (mLock) {
100            mAnimationStartDeferred = true;
101        }
102    }
103
104    /**
105     * Continues starting of animations.
106     *
107     * @see #deferStartingAnimations
108     */
109    void continueStartingAnimations() {
110        synchronized (mLock) {
111            mAnimationStartDeferred = false;
112            if (!mPendingAnimations.isEmpty()) {
113                mChoreographer.postFrameCallback(this::startAnimations);
114            }
115        }
116    }
117
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
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")
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")
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
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
230    private void startAnimations(long frameTimeNanos) {
231        synchronized (mLock) {
232            startPendingAnimationsLocked();
233        }
234    }
235
236    private void scheduleApplyTransaction() {
237        if (!mApplyScheduled) {
238            mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
239                    null /* token */);
240            mApplyScheduled = true;
241        }
242    }
243
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
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 {
268        ValueAnimator makeAnimator();
269    }
270
271    /**
272     * Value animator that uses sf-vsync signal to tick.
273     */
274    private class SfValueAnimator extends ValueAnimator {
275
276        SfValueAnimator() {
277            setFloatValues(0f, 1f);
278        }
279
280        @Override
281        public AnimationHandler getAnimationHandler() {
282            return mAnimationHandler;
283        }
284    }
285}
286