SurfaceAnimationRunner.java revision a36dc621ef0b86a0bd46f1d4f5a719a466ba1800
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                    applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime());
173                }
174            }
175
176            // Transaction will be applied in the commit phase.
177            scheduleApplyTransaction();
178        });
179
180        anim.addListener(new AnimatorListenerAdapter() {
181            @Override
182            public void onAnimationStart(Animator animation) {
183                synchronized (mCancelLock) {
184                    if (!a.mCancelled) {
185                        mFrameTransaction.show(a.mLeash);
186                    }
187                }
188            }
189
190            @Override
191            public void onAnimationEnd(Animator animation) {
192                synchronized (mLock) {
193                    mRunningAnimations.remove(a.mLeash);
194                    synchronized (mCancelLock) {
195                        if (!a.mCancelled) {
196
197                            // Post on other thread that we can push final state without jank.
198                            AnimationThread.getHandler().post(a.mFinishCallback);
199                        }
200                    }
201                }
202            }
203        });
204        anim.start();
205        if (a.mAnimSpec.canSkipFirstFrame()) {
206            // If we can skip the first frame, we start one frame later.
207            anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
208        }
209
210        // Immediately start the animation by manually applying an animation frame. Otherwise, the
211        // start time would only be set in the next frame, leading to a delay.
212        anim.doAnimationFrame(mChoreographer.getFrameTime());
213        a.mAnim = anim;
214        mRunningAnimations.put(a.mLeash, a);
215    }
216
217    private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
218        a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
219    }
220
221    private void startAnimations(long frameTimeNanos) {
222        synchronized (mLock) {
223            startPendingAnimationsLocked();
224        }
225    }
226
227    private void scheduleApplyTransaction() {
228        if (!mApplyScheduled) {
229            mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
230                    null /* token */);
231            mApplyScheduled = true;
232        }
233    }
234
235    private void applyTransaction() {
236        mFrameTransaction.setAnimationTransaction();
237        mFrameTransaction.apply();
238        mApplyScheduled = false;
239    }
240
241    private static final class RunningAnimation {
242        final AnimationSpec mAnimSpec;
243        final SurfaceControl mLeash;
244        final Runnable mFinishCallback;
245        ValueAnimator mAnim;
246
247        @GuardedBy("mCancelLock")
248        private boolean mCancelled;
249
250        RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
251            mAnimSpec = animSpec;
252            mLeash = leash;
253            mFinishCallback = finishCallback;
254        }
255    }
256
257    @VisibleForTesting
258    interface AnimatorFactory {
259        ValueAnimator makeAnimator();
260    }
261
262    /**
263     * Value animator that uses sf-vsync signal to tick.
264     */
265    private class SfValueAnimator extends ValueAnimator {
266
267        SfValueAnimator() {
268            setFloatValues(0f, 1f);
269        }
270
271        @Override
272        public AnimationHandler getAnimationHandler() {
273            return mAnimationHandler;
274        }
275    }
276}
277