SurfaceAnimationRunner.java revision a36dc621ef0b86a0bd46f1d4f5a719a466ba1800
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 17package com.android.server.wm; 18 19import static android.util.TimeUtils.NANOS_PER_MS; 20import 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; 33 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 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 73 SurfaceAnimationRunner() { 74 this(null /* callbackProvider */, null /* animatorFactory */, new Transaction()); 75 } 76 77 @VisibleForTesting 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 */ 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 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 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") 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") 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 applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime()); 173 } 174 } 175 176 // Transaction will be applied in the commit phase. 177 scheduleApplyTransaction(); 178 }); 179 180 anim.addListener(new AnimatorListenerAdapter() { 181 @Override 182 public void onAnimationStart(Animator animation) { 183 synchronized (mCancelLock) { 184 if (!a.mCancelled) { 185 mFrameTransaction.show(a.mLeash); 186 } 187 } 188 } 189 190 @Override 191 public void onAnimationEnd(Animator animation) { 192 synchronized (mLock) { 193 mRunningAnimations.remove(a.mLeash); 194 synchronized (mCancelLock) { 195 if (!a.mCancelled) { 196 197 // Post on other thread that we can push final state without jank. 198 AnimationThread.getHandler().post(a.mFinishCallback); 199 } 200 } 201 } 202 } 203 }); 204 anim.start(); 205 if (a.mAnimSpec.canSkipFirstFrame()) { 206 // If we can skip the first frame, we start one frame later. 207 anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS); 208 } 209 210 // Immediately start the animation by manually applying an animation frame. Otherwise, the 211 // start time would only be set in the next frame, leading to a delay. 212 anim.doAnimationFrame(mChoreographer.getFrameTime()); 213 a.mAnim = anim; 214 mRunningAnimations.put(a.mLeash, a); 215 } 216 217 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { 218 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); 219 } 220 221 private void startAnimations(long frameTimeNanos) { 222 synchronized (mLock) { 223 startPendingAnimationsLocked(); 224 } 225 } 226 227 private void scheduleApplyTransaction() { 228 if (!mApplyScheduled) { 229 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, 230 null /* token */); 231 mApplyScheduled = true; 232 } 233 } 234 235 private void applyTransaction() { 236 mFrameTransaction.setAnimationTransaction(); 237 mFrameTransaction.apply(); 238 mApplyScheduled = false; 239 } 240 241 private static final class RunningAnimation { 242 final AnimationSpec mAnimSpec; 243 final SurfaceControl mLeash; 244 final Runnable mFinishCallback; 245 ValueAnimator mAnim; 246 247 @GuardedBy("mCancelLock") 248 private boolean mCancelled; 249 250 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) { 251 mAnimSpec = animSpec; 252 mLeash = leash; 253 mFinishCallback = finishCallback; 254 } 255 } 256 257 @VisibleForTesting 258 interface AnimatorFactory { 259 ValueAnimator makeAnimator(); 260 } 261 262 /** 263 * Value animator that uses sf-vsync signal to tick. 264 */ 265 private class SfValueAnimator extends ValueAnimator { 266 267 SfValueAnimator() { 268 setFloatValues(0f, 1f); 269 } 270 271 @Override 272 public AnimationHandler getAnimationHandler() { 273 return mAnimationHandler; 274 } 275 } 276} 277