blob: 86afd4eda9aa13a548f337616c9fbe49f98da9ae [file] [log] [blame]
Jorim Jaggi21c39a72017-10-20 15:47:51 +02001/*
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;
Jorim Jaggi32fd84a2017-11-20 19:59:42 +010028import android.os.SystemClock;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020029import android.util.ArrayMap;
30import android.view.Choreographer;
31import android.view.SurfaceControl;
32import android.view.SurfaceControl.Transaction;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020033
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
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010047 /**
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
Jorim Jaggi21c39a72017-10-20 15:47:51 +020053 @VisibleForTesting
54 Choreographer mChoreographer;
55
56 private final Runnable mApplyTransactionRunnable = this::applyTransaction;
57 private final AnimationHandler mAnimationHandler;
58 private final Transaction mFrameTransaction;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010059 private final AnimatorFactory mAnimatorFactory;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020060 private boolean mApplyScheduled;
61
62 @GuardedBy("mLock")
63 @VisibleForTesting
64 final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
65
66 @GuardedBy("mLock")
67 @VisibleForTesting
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010068 final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>();
Jorim Jaggi21c39a72017-10-20 15:47:51 +020069
70 SurfaceAnimationRunner() {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010071 this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
Jorim Jaggi21c39a72017-10-20 15:47:51 +020072 }
73
74 @VisibleForTesting
75 SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010076 AnimatorFactory animatorFactory, Transaction frameTransaction) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +020077 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));
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010084 mAnimatorFactory = animatorFactory != null
85 ? animatorFactory
86 : SfValueAnimator::new;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020087 }
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);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200107 return;
108 }
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100109 final RunningAnimation anim = mRunningAnimations.get(leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200110 if (anim != null) {
111 mRunningAnimations.remove(leash);
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100112 synchronized (mCancelLock) {
113 anim.mCancelled = true;
114 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200115 SurfaceAnimationThread.getHandler().post(() -> {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100116 anim.mAnim.cancel();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200117 applyTransaction();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200118 });
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) {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100131 final ValueAnimator anim = mAnimatorFactory.makeAnimator();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200132
133 // Animation length is already expected to be scaled.
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100134 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 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200142
143 // Transaction will be applied in the commit phase.
144 scheduleApplyTransaction();
145 });
Jorim Jaggi2e3c31d2017-11-20 19:49:00 +0100146
147 if (a.mAnimSpec.canSkipFirstFrame()) {
148 anim.setCurrentPlayTime(Choreographer.getFrameDelay());
149 }
150
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100151 anim.addListener(new AnimatorListenerAdapter() {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200152 @Override
153 public void onAnimationStart(Animator animation) {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100154 synchronized (mCancelLock) {
155 if (!a.mCancelled) {
156 mFrameTransaction.show(a.mLeash);
157 }
158 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200159 }
160
161 @Override
162 public void onAnimationEnd(Animator animation) {
163 synchronized (mLock) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100164 mRunningAnimations.remove(a.mLeash);
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100165 synchronized (mCancelLock) {
166 if (!a.mCancelled) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100167
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100168 // Post on other thread that we can push final state without jank.
169 AnimationThread.getHandler().post(a.mFinishCallback);
170 }
171 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200172 }
173 }
174 });
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100175 anim.start();
176 a.mAnim = anim;
177 mRunningAnimations.put(a.mLeash, a);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200178 }
179
180 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100181 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200182 }
183
184 private void stepAnimation(long frameTimeNanos) {
185 synchronized (mLock) {
186 startPendingAnimationsLocked();
187 }
188 }
189
190 private void scheduleApplyTransaction() {
191 if (!mApplyScheduled) {
192 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
193 null /* token */);
194 mApplyScheduled = true;
195 }
196 }
197
198 private void applyTransaction() {
Jorim Jaggi32fd84a2017-11-20 19:59:42 +0100199 mFrameTransaction.setAnimationTransaction();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200200 mFrameTransaction.apply();
201 mApplyScheduled = false;
202 }
203
204 private static final class RunningAnimation {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100205 final AnimationSpec mAnimSpec;
206 final SurfaceControl mLeash;
207 final Runnable mFinishCallback;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100208 ValueAnimator mAnim;
209
210 @GuardedBy("mCancelLock")
211 private boolean mCancelled;
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200212
213 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100214 mAnimSpec = animSpec;
215 mLeash = leash;
216 mFinishCallback = finishCallback;
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200217 }
218 }
219
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100220 @VisibleForTesting
221 interface AnimatorFactory {
222 ValueAnimator makeAnimator();
223 }
224
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200225 /**
226 * Value animator that uses sf-vsync signal to tick.
227 */
228 private class SfValueAnimator extends ValueAnimator {
229
230 SfValueAnimator() {
231 setFloatValues(0f, 1f);
232 }
233
234 @Override
235 public AnimationHandler getAnimationHandler() {
236 return mAnimationHandler;
237 }
238 }
239}