• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                         mFrameTransaction.show(a.mLeash);
197                     }
198                 }
199             }
200 
201             @Override
202             public void onAnimationEnd(Animator animation) {
203                 synchronized (mLock) {
204                     mRunningAnimations.remove(a.mLeash);
205                     synchronized (mCancelLock) {
206                         if (!a.mCancelled) {
207 
208                             // Post on other thread that we can push final state without jank.
209                             AnimationThread.getHandler().post(a.mFinishCallback);
210                         }
211                     }
212                 }
213             }
214         });
215         a.mAnim = anim;
216         mRunningAnimations.put(a.mLeash, a);
217 
218         anim.start();
219         if (a.mAnimSpec.canSkipFirstFrame()) {
220             // If we can skip the first frame, we start one frame later.
221             anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);
222         }
223 
224         // Immediately start the animation by manually applying an animation frame. Otherwise, the
225         // start time would only be set in the next frame, leading to a delay.
226         anim.doAnimationFrame(mChoreographer.getFrameTime());
227     }
228 
applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime)229     private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
230         if (a.mAnimSpec.needsEarlyWakeup()) {
231             t.setEarlyWakeup();
232         }
233         a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
234     }
235 
startAnimations(long frameTimeNanos)236     private void startAnimations(long frameTimeNanos) {
237         synchronized (mLock) {
238             startPendingAnimationsLocked();
239         }
240         mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
241     }
242 
scheduleApplyTransaction()243     private void scheduleApplyTransaction() {
244         if (!mApplyScheduled) {
245             mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
246                     null /* token */);
247             mApplyScheduled = true;
248         }
249     }
250 
applyTransaction()251     private void applyTransaction() {
252         mFrameTransaction.setAnimationTransaction();
253         mFrameTransaction.apply();
254         mApplyScheduled = false;
255     }
256 
257     private static final class RunningAnimation {
258         final AnimationSpec mAnimSpec;
259         final SurfaceControl mLeash;
260         final Runnable mFinishCallback;
261         ValueAnimator mAnim;
262 
263         @GuardedBy("mCancelLock")
264         private boolean mCancelled;
265 
RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback)266         RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
267             mAnimSpec = animSpec;
268             mLeash = leash;
269             mFinishCallback = finishCallback;
270         }
271     }
272 
273     @VisibleForTesting
274     interface AnimatorFactory {
makeAnimator()275         ValueAnimator makeAnimator();
276     }
277 
278     /**
279      * Value animator that uses sf-vsync signal to tick.
280      */
281     private class SfValueAnimator extends ValueAnimator {
282 
SfValueAnimator()283         SfValueAnimator() {
284             setFloatValues(0f, 1f);
285         }
286 
287         @Override
getAnimationHandler()288         public AnimationHandler getAnimationHandler() {
289             return mAnimationHandler;
290         }
291     }
292 }
293