SurfaceAnimationRunner.java revision 32fd84ace1315854b2443ba9c1445837af4bc5eb
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.view.Choreographer.CALLBACK_TRAVERSAL; 20import static android.view.Choreographer.getSfInstance; 21 22import android.animation.AnimationHandler; 23import android.animation.AnimationHandler.AnimationFrameCallbackProvider; 24import android.animation.Animator; 25import android.animation.AnimatorListenerAdapter; 26import android.animation.ValueAnimator; 27import android.annotation.Nullable; 28import android.os.SystemClock; 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 if (a.mAnimSpec.canSkipFirstFrame()) { 148 anim.setCurrentPlayTime(Choreographer.getFrameDelay()); 149 } 150 151 anim.addListener(new AnimatorListenerAdapter() { 152 @Override 153 public void onAnimationStart(Animator animation) { 154 synchronized (mCancelLock) { 155 if (!a.mCancelled) { 156 mFrameTransaction.show(a.mLeash); 157 } 158 } 159 } 160 161 @Override 162 public void onAnimationEnd(Animator animation) { 163 synchronized (mLock) { 164 mRunningAnimations.remove(a.mLeash); 165 synchronized (mCancelLock) { 166 if (!a.mCancelled) { 167 168 // Post on other thread that we can push final state without jank. 169 AnimationThread.getHandler().post(a.mFinishCallback); 170 } 171 } 172 } 173 } 174 }); 175 anim.start(); 176 a.mAnim = anim; 177 mRunningAnimations.put(a.mLeash, a); 178 } 179 180 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { 181 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); 182 } 183 184 private void stepAnimation(long frameTimeNanos) { 185 synchronized (mLock) { 186 startPendingAnimationsLocked(); 187 } 188 } 189 190 private void scheduleApplyTransaction() { 191 if (!mApplyScheduled) { 192 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, 193 null /* token */); 194 mApplyScheduled = true; 195 } 196 } 197 198 private void applyTransaction() { 199 mFrameTransaction.setAnimationTransaction(); 200 mFrameTransaction.apply(); 201 mApplyScheduled = false; 202 } 203 204 private static final class RunningAnimation { 205 final AnimationSpec mAnimSpec; 206 final SurfaceControl mLeash; 207 final Runnable mFinishCallback; 208 ValueAnimator mAnim; 209 210 @GuardedBy("mCancelLock") 211 private boolean mCancelled; 212 213 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) { 214 mAnimSpec = animSpec; 215 mLeash = leash; 216 mFinishCallback = finishCallback; 217 } 218 } 219 220 @VisibleForTesting 221 interface AnimatorFactory { 222 ValueAnimator makeAnimator(); 223 } 224 225 /** 226 * Value animator that uses sf-vsync signal to tick. 227 */ 228 private class SfValueAnimator extends ValueAnimator { 229 230 SfValueAnimator() { 231 setFloatValues(0f, 1f); 232 } 233 234 @Override 235 public AnimationHandler getAnimationHandler() { 236 return mAnimationHandler; 237 } 238 } 239} 240