blob: dc62cc89c14dc6aec4cbced086499f877ea3c448 [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
Jorim Jaggi2d74faf2017-12-18 19:21:27 +010019import static android.util.TimeUtils.NANOS_PER_MS;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020020import 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;
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
Jorim Jaggid6d97162018-01-05 18:28:36 +010070 @GuardedBy("mLock")
71 private boolean mAnimationStartDeferred;
72
Jorim Jaggi21c39a72017-10-20 15:47:51 +020073 SurfaceAnimationRunner() {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010074 this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
Jorim Jaggi21c39a72017-10-20 15:47:51 +020075 }
76
77 @VisibleForTesting
78 SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010079 AnimatorFactory animatorFactory, Transaction frameTransaction) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +020080 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));
Jorim Jaggi6c7e5612017-12-12 02:17:10 +010087 mAnimatorFactory = animatorFactory != null
88 ? animatorFactory
89 : SfValueAnimator::new;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020090 }
91
Jorim Jaggid6d97162018-01-05 18:28:36 +010092 /**
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
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200118 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);
Jorim Jaggid6d97162018-01-05 18:28:36 +0100124 if (!mAnimationStartDeferred) {
125 mChoreographer.postFrameCallback(this::startAnimations);
126 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200127
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);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200138 return;
139 }
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100140 final RunningAnimation anim = mRunningAnimations.get(leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200141 if (anim != null) {
142 mRunningAnimations.remove(leash);
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100143 synchronized (mCancelLock) {
144 anim.mCancelled = true;
145 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200146 SurfaceAnimationThread.getHandler().post(() -> {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100147 anim.mAnim.cancel();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200148 applyTransaction();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200149 });
150 }
151 }
152 }
153
154 private void startPendingAnimationsLocked() {
155 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
156 startAnimationLocked(mPendingAnimations.valueAt(i));
157 }
158 mPendingAnimations.clear();
159 }
160
161 private void startAnimationLocked(RunningAnimation a) {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100162 final ValueAnimator anim = mAnimatorFactory.makeAnimator();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200163
164 // Animation length is already expected to be scaled.
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100165 anim.overrideDurationScale(1.0f);
166 anim.setDuration(a.mAnimSpec.getDuration());
167 anim.addUpdateListener(animation -> {
168 synchronized (mCancelLock) {
169 if (!a.mCancelled) {
170 applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime());
171 }
172 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200173
174 // Transaction will be applied in the commit phase.
175 scheduleApplyTransaction();
176 });
Jorim Jaggi2e3c31d2017-11-20 19:49:00 +0100177
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100178 anim.addListener(new AnimatorListenerAdapter() {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200179 @Override
180 public void onAnimationStart(Animator animation) {
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100181 synchronized (mCancelLock) {
182 if (!a.mCancelled) {
183 mFrameTransaction.show(a.mLeash);
184 }
185 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200186 }
187
188 @Override
189 public void onAnimationEnd(Animator animation) {
190 synchronized (mLock) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100191 mRunningAnimations.remove(a.mLeash);
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100192 synchronized (mCancelLock) {
193 if (!a.mCancelled) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100194
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100195 // Post on other thread that we can push final state without jank.
196 AnimationThread.getHandler().post(a.mFinishCallback);
197 }
198 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200199 }
200 }
201 });
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100202 anim.start();
Jorim Jaggi2d74faf2017-12-18 19:21:27 +0100203 if (a.mAnimSpec.canSkipFirstFrame()) {
204 // If we can skip the first frame, we start one frame later.
205 anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
206 }
207
208 // Immediately start the animation by manually applying an animation frame. Otherwise, the
209 // start time would only be set in the next frame, leading to a delay.
210 anim.doAnimationFrame(mChoreographer.getFrameTime());
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100211 a.mAnim = anim;
212 mRunningAnimations.put(a.mLeash, a);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200213 }
214
215 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100216 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200217 }
218
Jorim Jaggid6d97162018-01-05 18:28:36 +0100219 private void startAnimations(long frameTimeNanos) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200220 synchronized (mLock) {
221 startPendingAnimationsLocked();
222 }
223 }
224
225 private void scheduleApplyTransaction() {
226 if (!mApplyScheduled) {
227 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
228 null /* token */);
229 mApplyScheduled = true;
230 }
231 }
232
233 private void applyTransaction() {
Jorim Jaggi32fd84a2017-11-20 19:59:42 +0100234 mFrameTransaction.setAnimationTransaction();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200235 mFrameTransaction.apply();
236 mApplyScheduled = false;
237 }
238
239 private static final class RunningAnimation {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100240 final AnimationSpec mAnimSpec;
241 final SurfaceControl mLeash;
242 final Runnable mFinishCallback;
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100243 ValueAnimator mAnim;
244
245 @GuardedBy("mCancelLock")
246 private boolean mCancelled;
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200247
248 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100249 mAnimSpec = animSpec;
250 mLeash = leash;
251 mFinishCallback = finishCallback;
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200252 }
253 }
254
Jorim Jaggi6c7e5612017-12-12 02:17:10 +0100255 @VisibleForTesting
256 interface AnimatorFactory {
257 ValueAnimator makeAnimator();
258 }
259
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200260 /**
261 * Value animator that uses sf-vsync signal to tick.
262 */
263 private class SfValueAnimator extends ValueAnimator {
264
265 SfValueAnimator() {
266 setFloatValues(0f, 1f);
267 }
268
269 @Override
270 public AnimationHandler getAnimationHandler() {
271 return mAnimationHandler;
272 }
273 }
274}