SurfaceAnimationRunner.java revision 2d74fafdaf9a5adb74bb336a0052206da629feaa
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 SurfaceAnimationRunner() { 71 this(null /* callbackProvider */, null /* animatorFactory */, new Transaction()); 72 } 73 74 @VisibleForTesting 75 SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider, 76 AnimatorFactory animatorFactory, Transaction frameTransaction) { 77 SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(), 78 0 /* timeout */); 79 mFrameTransaction = frameTransaction; 80 mAnimationHandler = new AnimationHandler(); 81 mAnimationHandler.setProvider(callbackProvider != null 82 ? callbackProvider 83 : new SfVsyncFrameCallbackProvider(mChoreographer)); 84 mAnimatorFactory = animatorFactory != null 85 ? animatorFactory 86 : SfValueAnimator::new; 87 } 88 89 void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, 90 Runnable finishCallback) { 91 synchronized (mLock) { 92 final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, 93 finishCallback); 94 mPendingAnimations.put(animationLeash, runningAnim); 95 mChoreographer.postFrameCallback(this::stepAnimation); 96 97 // Some animations (e.g. move animations) require the initial transform to be applied 98 // immediately. 99 applyTransformation(runningAnim, t, 0 /* currentPlayTime */); 100 } 101 } 102 103 void onAnimationCancelled(SurfaceControl leash) { 104 synchronized (mLock) { 105 if (mPendingAnimations.containsKey(leash)) { 106 mPendingAnimations.remove(leash); 107 return; 108 } 109 final RunningAnimation anim = mRunningAnimations.get(leash); 110 if (anim != null) { 111 mRunningAnimations.remove(leash); 112 synchronized (mCancelLock) { 113 anim.mCancelled = true; 114 } 115 SurfaceAnimationThread.getHandler().post(() -> { 116 anim.mAnim.cancel(); 117 applyTransaction(); 118 }); 119 } 120 } 121 } 122 123 private void startPendingAnimationsLocked() { 124 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 125 startAnimationLocked(mPendingAnimations.valueAt(i)); 126 } 127 mPendingAnimations.clear(); 128 } 129 130 private void startAnimationLocked(RunningAnimation a) { 131 final ValueAnimator anim = mAnimatorFactory.makeAnimator(); 132 133 // Animation length is already expected to be scaled. 134 anim.overrideDurationScale(1.0f); 135 anim.setDuration(a.mAnimSpec.getDuration()); 136 anim.addUpdateListener(animation -> { 137 synchronized (mCancelLock) { 138 if (!a.mCancelled) { 139 applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime()); 140 } 141 } 142 143 // Transaction will be applied in the commit phase. 144 scheduleApplyTransaction(); 145 }); 146 147 anim.addListener(new AnimatorListenerAdapter() { 148 @Override 149 public void onAnimationStart(Animator animation) { 150 synchronized (mCancelLock) { 151 if (!a.mCancelled) { 152 mFrameTransaction.show(a.mLeash); 153 } 154 } 155 } 156 157 @Override 158 public void onAnimationEnd(Animator animation) { 159 synchronized (mLock) { 160 mRunningAnimations.remove(a.mLeash); 161 synchronized (mCancelLock) { 162 if (!a.mCancelled) { 163 164 // Post on other thread that we can push final state without jank. 165 AnimationThread.getHandler().post(a.mFinishCallback); 166 } 167 } 168 } 169 } 170 }); 171 anim.start(); 172 if (a.mAnimSpec.canSkipFirstFrame()) { 173 // If we can skip the first frame, we start one frame later. 174 anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS); 175 } 176 177 // Immediately start the animation by manually applying an animation frame. Otherwise, the 178 // start time would only be set in the next frame, leading to a delay. 179 anim.doAnimationFrame(mChoreographer.getFrameTime()); 180 a.mAnim = anim; 181 mRunningAnimations.put(a.mLeash, a); 182 } 183 184 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { 185 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); 186 } 187 188 private void stepAnimation(long frameTimeNanos) { 189 synchronized (mLock) { 190 startPendingAnimationsLocked(); 191 } 192 } 193 194 private void scheduleApplyTransaction() { 195 if (!mApplyScheduled) { 196 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, 197 null /* token */); 198 mApplyScheduled = true; 199 } 200 } 201 202 private void applyTransaction() { 203 mFrameTransaction.setAnimationTransaction(); 204 mFrameTransaction.apply(); 205 mApplyScheduled = false; 206 } 207 208 private static final class RunningAnimation { 209 final AnimationSpec mAnimSpec; 210 final SurfaceControl mLeash; 211 final Runnable mFinishCallback; 212 ValueAnimator mAnim; 213 214 @GuardedBy("mCancelLock") 215 private boolean mCancelled; 216 217 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) { 218 mAnimSpec = animSpec; 219 mLeash = leash; 220 mFinishCallback = finishCallback; 221 } 222 } 223 224 @VisibleForTesting 225 interface AnimatorFactory { 226 ValueAnimator makeAnimator(); 227 } 228 229 /** 230 * Value animator that uses sf-vsync signal to tick. 231 */ 232 private class SfValueAnimator extends ValueAnimator { 233 234 SfValueAnimator() { 235 setFloatValues(0f, 1f); 236 } 237 238 @Override 239 public AnimationHandler getAnimationHandler() { 240 return mAnimationHandler; 241 } 242 } 243} 244