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 }