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