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