SurfaceAnimationRunner.java revision a5e105728fb87264b9dc2e66b3703168f8864110
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    @VisibleForTesting
47    Choreographer mChoreographer;
48
49    private final Runnable mApplyTransactionRunnable = this::applyTransaction;
50    private final AnimationHandler mAnimationHandler;
51    private final Transaction mFrameTransaction;
52    private boolean mApplyScheduled;
53
54    @GuardedBy("mLock")
55    @VisibleForTesting
56    final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
57
58    @GuardedBy("mLock")
59    @VisibleForTesting
60    final ArrayMap<SurfaceControl, ValueAnimator> mRunningAnimations = new ArrayMap<>();
61
62    SurfaceAnimationRunner() {
63        this(null /* callbackProvider */, new Transaction());
64    }
65
66    @VisibleForTesting
67    SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
68            Transaction frameTransaction) {
69        SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
70                0 /* timeout */);
71        mFrameTransaction = frameTransaction;
72        mAnimationHandler = new AnimationHandler();
73        mAnimationHandler.setProvider(callbackProvider != null
74                ? callbackProvider
75                : new SfVsyncFrameCallbackProvider(mChoreographer));
76    }
77
78    void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
79            Runnable finishCallback) {
80        synchronized (mLock) {
81            final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
82                    finishCallback);
83            mPendingAnimations.put(animationLeash, runningAnim);
84            mChoreographer.postFrameCallback(this::stepAnimation);
85
86            // Some animations (e.g. move animations) require the initial transform to be applied
87            // immediately.
88            applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
89        }
90    }
91
92    void onAnimationCancelled(SurfaceControl leash) {
93        synchronized (mLock) {
94            if (mPendingAnimations.containsKey(leash)) {
95                mPendingAnimations.remove(leash);
96                return;
97            }
98            final ValueAnimator anim = mRunningAnimations.get(leash);
99            if (anim != null) {
100                mRunningAnimations.remove(leash);
101                SurfaceAnimationThread.getHandler().post(() -> {
102                    anim.cancel();
103                    applyTransaction();
104                });
105            }
106        }
107    }
108
109    private void startPendingAnimationsLocked() {
110        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
111            startAnimationLocked(mPendingAnimations.valueAt(i));
112        }
113        mPendingAnimations.clear();
114    }
115
116    private void startAnimationLocked(RunningAnimation a) {
117        final ValueAnimator result = new SfValueAnimator();
118
119        // Animation length is already expected to be scaled.
120        result.overrideDurationScale(1.0f);
121        result.setDuration(a.mAnimSpec.getDuration());
122        result.addUpdateListener(animation -> {
123            applyTransformation(a, mFrameTransaction, result.getCurrentPlayTime());
124
125            // Transaction will be applied in the commit phase.
126            scheduleApplyTransaction();
127        });
128        result.addListener(new AnimatorListenerAdapter() {
129
130            private boolean mCancelled;
131
132            @Override
133            public void onAnimationStart(Animator animation) {
134                mFrameTransaction.show(a.mLeash);
135            }
136
137            @Override
138            public void onAnimationCancel(Animator animation) {
139                mCancelled = true;
140            }
141
142            @Override
143            public void onAnimationEnd(Animator animation) {
144                synchronized (mLock) {
145                    mRunningAnimations.remove(a.mLeash);
146                }
147                if (!mCancelled) {
148                    // Post on other thread that we can push final state without jank.
149                    AnimationThread.getHandler().post(a.mFinishCallback);
150                }
151            }
152        });
153        result.start();
154        mRunningAnimations.put(a.mLeash, result);
155    }
156
157    private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
158        a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
159    }
160
161    private void stepAnimation(long frameTimeNanos) {
162        synchronized (mLock) {
163            startPendingAnimationsLocked();
164        }
165    }
166
167    private void scheduleApplyTransaction() {
168        if (!mApplyScheduled) {
169            mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
170                    null /* token */);
171            mApplyScheduled = true;
172        }
173    }
174
175    private void applyTransaction() {
176        mFrameTransaction.apply();
177        mApplyScheduled = false;
178    }
179
180    private static final class RunningAnimation {
181        final AnimationSpec mAnimSpec;
182        final SurfaceControl mLeash;
183        final Runnable mFinishCallback;
184
185        RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
186            mAnimSpec = animSpec;
187            mLeash = leash;
188            mFinishCallback = finishCallback;
189        }
190    }
191
192    /**
193     * Value animator that uses sf-vsync signal to tick.
194     */
195    private class SfValueAnimator extends ValueAnimator {
196
197        SfValueAnimator() {
198            setFloatValues(0f, 1f);
199        }
200
201        @Override
202        public AnimationHandler getAnimationHandler() {
203            return mAnimationHandler;
204        }
205    }
206}
207