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