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