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 
17 package com.android.server.wm;
18 
19 import static android.util.TimeUtils.NANOS_PER_MS;
20 import static android.view.Choreographer.CALLBACK_TRAVERSAL;
21 import static android.view.Choreographer.getSfInstance;
22 
23 import android.animation.AnimationHandler;
24 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
25 import android.animation.Animator;
26 import android.animation.AnimatorListenerAdapter;
27 import android.animation.ValueAnimator;
28 import android.annotation.Nullable;
29 import android.hardware.power.V1_0.PowerHint;
30 import android.os.PowerManagerInternal;
31 import android.util.ArrayMap;
32 import android.view.Choreographer;
33 import android.view.SurfaceControl;
34 import android.view.SurfaceControl.Transaction;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
39 import com.android.server.AnimationThread;
40 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
41 
42 /**
43  * Class to run animations without holding the window manager lock.
44  */
45 class SurfaceAnimationRunner {
46 
47     private final Object mLock = new Object();
48 
49     /**
50      * Lock for cancelling animations. Must be acquired on it's own, or after acquiring
51      * {@link #mLock}
52      */
53     private final Object mCancelLock = new Object();
54 
55     @VisibleForTesting
56     Choreographer mChoreographer;
57 
58     private final Runnable mApplyTransactionRunnable = this::applyTransaction;
59     private final AnimationHandler mAnimationHandler;
60     private final Transaction mFrameTransaction;
61     private final AnimatorFactory mAnimatorFactory;
62     private final PowerManagerInternal mPowerManagerInternal;
63     private boolean mApplyScheduled;
64 
65     @GuardedBy("mLock")
66     @VisibleForTesting
67     final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
68 
69     @GuardedBy("mLock")
70     @VisibleForTesting
71     final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>();
72 
73     @GuardedBy("mLock")
74     private boolean mAnimationStartDeferred;
75 
SurfaceAnimationRunner(PowerManagerInternal powerManagerInternal)76     SurfaceAnimationRunner(PowerManagerInternal powerManagerInternal) {
77         this(null /* callbackProvider */, null /* animatorFactory */, new Transaction(),
78                 powerManagerInternal);
79     }
80 
81     @VisibleForTesting
SurfaceAnimationRunner(@ullable AnimationFrameCallbackProvider callbackProvider, AnimatorFactory animatorFactory, Transaction frameTransaction, PowerManagerInternal powerManagerInternal)82     SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
83             AnimatorFactory animatorFactory, Transaction frameTransaction,
84             PowerManagerInternal powerManagerInternal) {
85         SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
86                 0 /* timeout */);
87         mFrameTransaction = frameTransaction;
88         mAnimationHandler = new AnimationHandler();
89         mAnimationHandler.setProvider(callbackProvider != null
90                 ? callbackProvider
91                 : new SfVsyncFrameCallbackProvider(mChoreographer));
92         mAnimatorFactory = animatorFactory != null
93                 ? animatorFactory
94                 : SfValueAnimator::new;
95         mPowerManagerInternal = powerManagerInternal;
96     }
97 
98     /**
99      * Defers starting of animations until {@link #continueStartingAnimations} is called. This
100      * method is NOT nestable.
101      *
102      * @see #continueStartingAnimations
103      */
deferStartingAnimations()104     void deferStartingAnimations() {
105         synchronized (mLock) {
106             mAnimationStartDeferred = true;
107         }
108     }
109 
110     /**
111      * Continues starting of animations.
112      *
113      * @see #deferStartingAnimations
114      */
continueStartingAnimations()115     void continueStartingAnimations() {
116         synchronized (mLock) {
117             mAnimationStartDeferred = false;
118             if (!mPendingAnimations.isEmpty()) {
119                 mChoreographer.postFrameCallback(this::startAnimations);
120             }
121         }
122     }
123 
startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, Runnable finishCallback)124     void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
125             Runnable finishCallback) {
126         synchronized (mLock) {
127             final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
128                     finishCallback);
129             mPendingAnimations.put(animationLeash, runningAnim);
130             if (!mAnimationStartDeferred) {
131                 mChoreographer.postFrameCallback(this::startAnimations);
132             }
133 
134             // Some animations (e.g. move animations) require the initial transform to be applied
135             // immediately.
136             applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
137         }
138     }
139 
onAnimationCancelled(SurfaceControl leash)140     void onAnimationCancelled(SurfaceControl leash) {
141         synchronized (mLock) {
142             if (mPendingAnimations.containsKey(leash)) {
143                 mPendingAnimations.remove(leash);
144                 return;
145             }
146             final RunningAnimation anim = mRunningAnimations.get(leash);
147             if (anim != null) {
148                 mRunningAnimations.remove(leash);
149                 synchronized (mCancelLock) {
150                     anim.mCancelled = true;
151                 }
152                 SurfaceAnimationThread.getHandler().post(() -> {
153                     anim.mAnim.cancel();
154                     applyTransaction();
155                 });
156             }
157         }
158     }
159 
160     @GuardedBy("mLock")
startPendingAnimationsLocked()161     private void startPendingAnimationsLocked() {
162         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
163             startAnimationLocked(mPendingAnimations.valueAt(i));
164         }
165         mPendingAnimations.clear();
166     }
167 
168     @GuardedBy("mLock")
startAnimationLocked(RunningAnimation a)169     private void startAnimationLocked(RunningAnimation a) {
170         final ValueAnimator anim = mAnimatorFactory.makeAnimator();
171 
172         // Animation length is already expected to be scaled.
173         anim.overrideDurationScale(1.0f);
174         anim.setDuration(a.mAnimSpec.getDuration());
175         anim.addUpdateListener(animation -> {
176             synchronized (mCancelLock) {
177                 if (!a.mCancelled) {
178                     final long duration = anim.getDuration();
179                     long currentPlayTime = anim.getCurrentPlayTime();
180                     if (currentPlayTime > duration) {
181                         currentPlayTime = duration;
182                     }
183                     applyTransformation(a, mFrameTransaction, currentPlayTime);
184                 }
185             }
186 
187             // Transaction will be applied in the commit phase.
188             scheduleApplyTransaction();
189         });
190 
191         anim.addListener(new AnimatorListenerAdapter() {
192             @Override
193             public void onAnimationStart(Animator animation) {
194                 synchronized (mCancelLock) {
195                     if (!a.mCancelled) {
196                         // TODO: change this back to use show instead of alpha when b/138459974 is
197                         // fixed.
198                         mFrameTransaction.setAlpha(a.mLeash, 1);
199                     }
200                 }
201             }
202 
203             @Override
204             public void onAnimationEnd(Animator animation) {
205                 synchronized (mLock) {
206                     mRunningAnimations.remove(a.mLeash);
207                     synchronized (mCancelLock) {
208                         if (!a.mCancelled) {
209 
210                             // Post on other thread that we can push final state without jank.
211                             AnimationThread.getHandler().post(a.mFinishCallback);
212                         }
213                     }
214                 }
215             }
216         });
217         a.mAnim = anim;
218         mRunningAnimations.put(a.mLeash, a);
219 
220         anim.start();
221         if (a.mAnimSpec.canSkipFirstFrame()) {
222             // If we can skip the first frame, we start one frame later.
223             anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
224         }
225 
226         // Immediately start the animation by manually applying an animation frame. Otherwise, the
227         // start time would only be set in the next frame, leading to a delay.
228         anim.doAnimationFrame(mChoreographer.getFrameTime());
229     }
230 
applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime)231     private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
232         if (a.mAnimSpec.needsEarlyWakeup()) {
233             t.setEarlyWakeup();
234         }
235         a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
236     }
237 
startAnimations(long frameTimeNanos)238     private void startAnimations(long frameTimeNanos) {
239         synchronized (mLock) {
240             startPendingAnimationsLocked();
241         }
242         mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
243     }
244 
scheduleApplyTransaction()245     private void scheduleApplyTransaction() {
246         if (!mApplyScheduled) {
247             mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
248                     null /* token */);
249             mApplyScheduled = true;
250         }
251     }
252 
applyTransaction()253     private void applyTransaction() {
254         mFrameTransaction.setAnimationTransaction();
255         mFrameTransaction.apply();
256         mApplyScheduled = false;
257     }
258 
259     private static final class RunningAnimation {
260         final AnimationSpec mAnimSpec;
261         final SurfaceControl mLeash;
262         final Runnable mFinishCallback;
263         ValueAnimator mAnim;
264 
265         @GuardedBy("mCancelLock")
266         private boolean mCancelled;
267 
RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback)268         RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
269             mAnimSpec = animSpec;
270             mLeash = leash;
271             mFinishCallback = finishCallback;
272         }
273     }
274 
275     @VisibleForTesting
276     interface AnimatorFactory {
makeAnimator()277         ValueAnimator makeAnimator();
278     }
279 
280     /**
281      * Value animator that uses sf-vsync signal to tick.
282      */
283     private class SfValueAnimator extends ValueAnimator {
284 
SfValueAnimator()285         SfValueAnimator() {
286             setFloatValues(0f, 1f);
287         }
288 
289         @Override
getAnimationHandler()290         public AnimationHandler getAnimationHandler() {
291             return mAnimationHandler;
292         }
293     }
294 }
295