SurfaceAnimationRunner.java revision 2d74fafdaf9a5adb74bb336a0052206da629feaa
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    SurfaceAnimationRunner() {
71        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
72    }
73
74    @VisibleForTesting
75    SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
76            AnimatorFactory animatorFactory, Transaction frameTransaction) {
77        SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
78                0 /* timeout */);
79        mFrameTransaction = frameTransaction;
80        mAnimationHandler = new AnimationHandler();
81        mAnimationHandler.setProvider(callbackProvider != null
82                ? callbackProvider
83                : new SfVsyncFrameCallbackProvider(mChoreographer));
84        mAnimatorFactory = animatorFactory != null
85                ? animatorFactory
86                : SfValueAnimator::new;
87    }
88
89    void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
90            Runnable finishCallback) {
91        synchronized (mLock) {
92            final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
93                    finishCallback);
94            mPendingAnimations.put(animationLeash, runningAnim);
95            mChoreographer.postFrameCallback(this::stepAnimation);
96
97            // Some animations (e.g. move animations) require the initial transform to be applied
98            // immediately.
99            applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
100        }
101    }
102
103    void onAnimationCancelled(SurfaceControl leash) {
104        synchronized (mLock) {
105            if (mPendingAnimations.containsKey(leash)) {
106                mPendingAnimations.remove(leash);
107                return;
108            }
109            final RunningAnimation anim = mRunningAnimations.get(leash);
110            if (anim != null) {
111                mRunningAnimations.remove(leash);
112                synchronized (mCancelLock) {
113                    anim.mCancelled = true;
114                }
115                SurfaceAnimationThread.getHandler().post(() -> {
116                    anim.mAnim.cancel();
117                    applyTransaction();
118                });
119            }
120        }
121    }
122
123    private void startPendingAnimationsLocked() {
124        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
125            startAnimationLocked(mPendingAnimations.valueAt(i));
126        }
127        mPendingAnimations.clear();
128    }
129
130    private void startAnimationLocked(RunningAnimation a) {
131        final ValueAnimator anim = mAnimatorFactory.makeAnimator();
132
133        // Animation length is already expected to be scaled.
134        anim.overrideDurationScale(1.0f);
135        anim.setDuration(a.mAnimSpec.getDuration());
136        anim.addUpdateListener(animation -> {
137            synchronized (mCancelLock) {
138                if (!a.mCancelled) {
139                    applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime());
140                }
141            }
142
143            // Transaction will be applied in the commit phase.
144            scheduleApplyTransaction();
145        });
146
147        anim.addListener(new AnimatorListenerAdapter() {
148            @Override
149            public void onAnimationStart(Animator animation) {
150                synchronized (mCancelLock) {
151                    if (!a.mCancelled) {
152                        mFrameTransaction.show(a.mLeash);
153                    }
154                }
155            }
156
157            @Override
158            public void onAnimationEnd(Animator animation) {
159                synchronized (mLock) {
160                    mRunningAnimations.remove(a.mLeash);
161                    synchronized (mCancelLock) {
162                        if (!a.mCancelled) {
163
164                            // Post on other thread that we can push final state without jank.
165                            AnimationThread.getHandler().post(a.mFinishCallback);
166                        }
167                    }
168                }
169            }
170        });
171        anim.start();
172        if (a.mAnimSpec.canSkipFirstFrame()) {
173            // If we can skip the first frame, we start one frame later.
174            anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
175        }
176
177        // Immediately start the animation by manually applying an animation frame. Otherwise, the
178        // start time would only be set in the next frame, leading to a delay.
179        anim.doAnimationFrame(mChoreographer.getFrameTime());
180        a.mAnim = anim;
181        mRunningAnimations.put(a.mLeash, a);
182    }
183
184    private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
185        a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
186    }
187
188    private void stepAnimation(long frameTimeNanos) {
189        synchronized (mLock) {
190            startPendingAnimationsLocked();
191        }
192    }
193
194    private void scheduleApplyTransaction() {
195        if (!mApplyScheduled) {
196            mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
197                    null /* token */);
198            mApplyScheduled = true;
199        }
200    }
201
202    private void applyTransaction() {
203        mFrameTransaction.setAnimationTransaction();
204        mFrameTransaction.apply();
205        mApplyScheduled = false;
206    }
207
208    private static final class RunningAnimation {
209        final AnimationSpec mAnimSpec;
210        final SurfaceControl mLeash;
211        final Runnable mFinishCallback;
212        ValueAnimator mAnim;
213
214        @GuardedBy("mCancelLock")
215        private boolean mCancelled;
216
217        RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
218            mAnimSpec = animSpec;
219            mLeash = leash;
220            mFinishCallback = finishCallback;
221        }
222    }
223
224    @VisibleForTesting
225    interface AnimatorFactory {
226        ValueAnimator makeAnimator();
227    }
228
229    /**
230     * Value animator that uses sf-vsync signal to tick.
231     */
232    private class SfValueAnimator extends ValueAnimator {
233
234        SfValueAnimator() {
235            setFloatValues(0f, 1f);
236        }
237
238        @Override
239        public AnimationHandler getAnimationHandler() {
240            return mAnimationHandler;
241        }
242    }
243}
244