SurfaceAnimationRunner.java revision a5e105728fb87264b9dc2e66b3703168f8864110
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 @VisibleForTesting 47 Choreographer mChoreographer; 48 49 private final Runnable mApplyTransactionRunnable = this::applyTransaction; 50 private final AnimationHandler mAnimationHandler; 51 private final Transaction mFrameTransaction; 52 private boolean mApplyScheduled; 53 54 @GuardedBy("mLock") 55 @VisibleForTesting 56 final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>(); 57 58 @GuardedBy("mLock") 59 @VisibleForTesting 60 final ArrayMap<SurfaceControl, ValueAnimator> mRunningAnimations = new ArrayMap<>(); 61 62 SurfaceAnimationRunner() { 63 this(null /* callbackProvider */, new Transaction()); 64 } 65 66 @VisibleForTesting 67 SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider, 68 Transaction frameTransaction) { 69 SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(), 70 0 /* timeout */); 71 mFrameTransaction = frameTransaction; 72 mAnimationHandler = new AnimationHandler(); 73 mAnimationHandler.setProvider(callbackProvider != null 74 ? callbackProvider 75 : new SfVsyncFrameCallbackProvider(mChoreographer)); 76 } 77 78 void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, 79 Runnable finishCallback) { 80 synchronized (mLock) { 81 final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, 82 finishCallback); 83 mPendingAnimations.put(animationLeash, runningAnim); 84 mChoreographer.postFrameCallback(this::stepAnimation); 85 86 // Some animations (e.g. move animations) require the initial transform to be applied 87 // immediately. 88 applyTransformation(runningAnim, t, 0 /* currentPlayTime */); 89 } 90 } 91 92 void onAnimationCancelled(SurfaceControl leash) { 93 synchronized (mLock) { 94 if (mPendingAnimations.containsKey(leash)) { 95 mPendingAnimations.remove(leash); 96 return; 97 } 98 final ValueAnimator anim = mRunningAnimations.get(leash); 99 if (anim != null) { 100 mRunningAnimations.remove(leash); 101 SurfaceAnimationThread.getHandler().post(() -> { 102 anim.cancel(); 103 applyTransaction(); 104 }); 105 } 106 } 107 } 108 109 private void startPendingAnimationsLocked() { 110 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 111 startAnimationLocked(mPendingAnimations.valueAt(i)); 112 } 113 mPendingAnimations.clear(); 114 } 115 116 private void startAnimationLocked(RunningAnimation a) { 117 final ValueAnimator result = new SfValueAnimator(); 118 119 // Animation length is already expected to be scaled. 120 result.overrideDurationScale(1.0f); 121 result.setDuration(a.mAnimSpec.getDuration()); 122 result.addUpdateListener(animation -> { 123 applyTransformation(a, mFrameTransaction, result.getCurrentPlayTime()); 124 125 // Transaction will be applied in the commit phase. 126 scheduleApplyTransaction(); 127 }); 128 result.addListener(new AnimatorListenerAdapter() { 129 130 private boolean mCancelled; 131 132 @Override 133 public void onAnimationStart(Animator animation) { 134 mFrameTransaction.show(a.mLeash); 135 } 136 137 @Override 138 public void onAnimationCancel(Animator animation) { 139 mCancelled = true; 140 } 141 142 @Override 143 public void onAnimationEnd(Animator animation) { 144 synchronized (mLock) { 145 mRunningAnimations.remove(a.mLeash); 146 } 147 if (!mCancelled) { 148 // Post on other thread that we can push final state without jank. 149 AnimationThread.getHandler().post(a.mFinishCallback); 150 } 151 } 152 }); 153 result.start(); 154 mRunningAnimations.put(a.mLeash, result); 155 } 156 157 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { 158 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); 159 } 160 161 private void stepAnimation(long frameTimeNanos) { 162 synchronized (mLock) { 163 startPendingAnimationsLocked(); 164 } 165 } 166 167 private void scheduleApplyTransaction() { 168 if (!mApplyScheduled) { 169 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, 170 null /* token */); 171 mApplyScheduled = true; 172 } 173 } 174 175 private void applyTransaction() { 176 mFrameTransaction.apply(); 177 mApplyScheduled = false; 178 } 179 180 private static final class RunningAnimation { 181 final AnimationSpec mAnimSpec; 182 final SurfaceControl mLeash; 183 final Runnable mFinishCallback; 184 185 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) { 186 mAnimSpec = animSpec; 187 mLeash = leash; 188 mFinishCallback = finishCallback; 189 } 190 } 191 192 /** 193 * Value animator that uses sf-vsync signal to tick. 194 */ 195 private class SfValueAnimator extends ValueAnimator { 196 197 SfValueAnimator() { 198 setFloatValues(0f, 1f); 199 } 200 201 @Override 202 public AnimationHandler getAnimationHandler() { 203 return mAnimationHandler; 204 } 205 } 206} 207