SurfaceAnimationRunner.java revision 980c9de9766668f7d8899067fe34833560197108
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.util.ArrayMap; 29import android.view.Choreographer; 30import android.view.SurfaceControl; 31import android.view.SurfaceControl.Transaction; 32 33import com.android.internal.annotations.GuardedBy; 34import com.android.internal.annotations.VisibleForTesting; 35import com.android.internal.graphics.SfVsyncFrameCallbackProvider; 36import com.android.server.AnimationThread; 37import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; 38 39/** 40 * Class to run animations without holding the window manager lock. 41 */ 42class SurfaceAnimationRunner { 43 44 private final Object mLock = new Object(); 45 46 /** 47 * Lock for cancelling animations. Must be acquired on it's own, or after acquiring 48 * {@link #mLock} 49 */ 50 private final Object mCancelLock = new Object(); 51 52 @VisibleForTesting 53 Choreographer mChoreographer; 54 55 private final Runnable mApplyTransactionRunnable = this::applyTransaction; 56 private final AnimationHandler mAnimationHandler; 57 private final Transaction mFrameTransaction; 58 private final AnimatorFactory mAnimatorFactory; 59 private boolean mApplyScheduled; 60 61 @GuardedBy("mLock") 62 @VisibleForTesting 63 final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>(); 64 65 @GuardedBy("mLock") 66 @VisibleForTesting 67 final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>(); 68 69 SurfaceAnimationRunner() { 70 this(null /* callbackProvider */, null /* animatorFactory */, new Transaction()); 71 } 72 73 @VisibleForTesting 74 SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider, 75 AnimatorFactory animatorFactory, Transaction frameTransaction) { 76 SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(), 77 0 /* timeout */); 78 mFrameTransaction = frameTransaction; 79 mAnimationHandler = new AnimationHandler(); 80 mAnimationHandler.setProvider(callbackProvider != null 81 ? callbackProvider 82 : new SfVsyncFrameCallbackProvider(mChoreographer)); 83 mAnimatorFactory = animatorFactory != null 84 ? animatorFactory 85 : SfValueAnimator::new; 86 } 87 88 void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, 89 Runnable finishCallback) { 90 synchronized (mLock) { 91 final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, 92 finishCallback); 93 mPendingAnimations.put(animationLeash, runningAnim); 94 mChoreographer.postFrameCallback(this::stepAnimation); 95 96 // Some animations (e.g. move animations) require the initial transform to be applied 97 // immediately. 98 applyTransformation(runningAnim, t, 0 /* currentPlayTime */); 99 } 100 } 101 102 void onAnimationCancelled(SurfaceControl leash) { 103 synchronized (mLock) { 104 if (mPendingAnimations.containsKey(leash)) { 105 mPendingAnimations.remove(leash); 106 return; 107 } 108 final RunningAnimation anim = mRunningAnimations.get(leash); 109 if (anim != null) { 110 mRunningAnimations.remove(leash); 111 synchronized (mCancelLock) { 112 anim.mCancelled = true; 113 } 114 SurfaceAnimationThread.getHandler().post(() -> { 115 anim.mAnim.cancel(); 116 applyTransaction(); 117 }); 118 } 119 } 120 } 121 122 private void startPendingAnimationsLocked() { 123 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 124 startAnimationLocked(mPendingAnimations.valueAt(i)); 125 } 126 mPendingAnimations.clear(); 127 } 128 129 private void startAnimationLocked(RunningAnimation a) { 130 final ValueAnimator anim = mAnimatorFactory.makeAnimator(); 131 132 // Animation length is already expected to be scaled. 133 anim.overrideDurationScale(1.0f); 134 anim.setDuration(a.mAnimSpec.getDuration()); 135 anim.addUpdateListener(animation -> { 136 synchronized (mCancelLock) { 137 if (!a.mCancelled) { 138 applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime()); 139 } 140 } 141 142 // Transaction will be applied in the commit phase. 143 scheduleApplyTransaction(); 144 }); 145 anim.addListener(new AnimatorListenerAdapter() { 146 @Override 147 public void onAnimationStart(Animator animation) { 148 synchronized (mCancelLock) { 149 if (!a.mCancelled) { 150 mFrameTransaction.show(a.mLeash); 151 } 152 } 153 } 154 155 @Override 156 public void onAnimationEnd(Animator animation) { 157 synchronized (mLock) { 158 mRunningAnimations.remove(a.mLeash); 159 synchronized (mCancelLock) { 160 if (!a.mCancelled) { 161 162 // Post on other thread that we can push final state without jank. 163 AnimationThread.getHandler().post(a.mFinishCallback); 164 } 165 } 166 } 167 } 168 }); 169 anim.start(); 170 a.mAnim = anim; 171 mRunningAnimations.put(a.mLeash, a); 172 } 173 174 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { 175 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); 176 } 177 178 private void stepAnimation(long frameTimeNanos) { 179 synchronized (mLock) { 180 startPendingAnimationsLocked(); 181 } 182 } 183 184 private void scheduleApplyTransaction() { 185 if (!mApplyScheduled) { 186 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, 187 null /* token */); 188 mApplyScheduled = true; 189 } 190 } 191 192 private void applyTransaction() { 193 mFrameTransaction.apply(); 194 mApplyScheduled = false; 195 } 196 197 private static final class RunningAnimation { 198 final AnimationSpec mAnimSpec; 199 final SurfaceControl mLeash; 200 final Runnable mFinishCallback; 201 ValueAnimator mAnim; 202 203 @GuardedBy("mCancelLock") 204 private boolean mCancelled; 205 206 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) { 207 mAnimSpec = animSpec; 208 mLeash = leash; 209 mFinishCallback = finishCallback; 210 } 211 } 212 213 @VisibleForTesting 214 interface AnimatorFactory { 215 ValueAnimator makeAnimator(); 216 } 217 218 /** 219 * Value animator that uses sf-vsync signal to tick. 220 */ 221 private class SfValueAnimator extends ValueAnimator { 222 223 SfValueAnimator() { 224 setFloatValues(0f, 1f); 225 } 226 227 @Override 228 public AnimationHandler getAnimationHandler() { 229 return mAnimationHandler; 230 } 231 } 232} 233