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 java.util.concurrent.TimeUnit.SECONDS; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertTrue; 23 import static org.mockito.Mockito.any; 24 import static org.mockito.Mockito.atLeast; 25 import static org.mockito.Mockito.atLeastOnce; 26 import static org.mockito.Mockito.eq; 27 import static org.mockito.Mockito.times; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.when; 30 31 import android.animation.AnimationHandler; 32 import android.animation.AnimationHandler.AnimationFrameCallbackProvider; 33 import android.animation.ValueAnimator; 34 import android.graphics.Matrix; 35 import android.graphics.Point; 36 import android.platform.test.annotations.Presubmit; 37 import android.support.test.filters.FlakyTest; 38 import android.support.test.filters.SmallTest; 39 import android.support.test.runner.AndroidJUnit4; 40 import android.util.Log; 41 import android.view.Choreographer; 42 import android.view.Choreographer.FrameCallback; 43 import android.view.SurfaceControl; 44 import android.view.SurfaceControl.Transaction; 45 import android.view.animation.Animation; 46 import android.view.animation.TranslateAnimation; 47 48 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; 49 import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory; 50 51 import org.junit.Before; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.mockito.Mock; 56 import org.mockito.junit.MockitoJUnit; 57 import org.mockito.junit.MockitoRule; 58 59 import java.util.concurrent.CountDownLatch; 60 61 /** 62 * Test class for {@link SurfaceAnimationRunner}. 63 * 64 * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest 65 */ 66 @SmallTest 67 @Presubmit 68 @RunWith(AndroidJUnit4.class) 69 public class SurfaceAnimationRunnerTest extends WindowTestsBase { 70 71 @Mock SurfaceControl mMockSurface; 72 @Mock Transaction mMockTransaction; 73 @Mock AnimationSpec mMockAnimationSpec; 74 @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); 75 76 private SurfaceAnimationRunner mSurfaceAnimationRunner; 77 private CountDownLatch mFinishCallbackLatch; 78 79 @Before 80 public void setUp() throws Exception { 81 super.setUp(); 82 mFinishCallbackLatch = new CountDownLatch(1); 83 mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null, 84 mMockTransaction); 85 } 86 87 private void finishedCallback() { 88 mFinishCallbackLatch.countDown(); 89 } 90 91 @Test 92 public void testAnimation() throws Exception { 93 mSurfaceAnimationRunner 94 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, 95 this::finishedCallback); 96 97 // Ensure that the initial transformation has been applied. 98 final Matrix m = new Matrix(); 99 m.setTranslate(-10, 0); 100 verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); 101 verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f)); 102 103 mFinishCallbackLatch.await(1, SECONDS); 104 assertFinishCallbackCalled(); 105 106 m.setTranslate(10, 0); 107 verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); 108 109 // At least 3 times: After initialization, first frame, last frame. 110 verify(mMockTransaction, atLeast(3)).setAlpha(eq(mMockSurface), eq(1.0f)); 111 } 112 113 @Test 114 public void testCancel_notStarted() throws Exception { 115 mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, 116 mMockTransaction); 117 mSurfaceAnimationRunner 118 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, 119 this::finishedCallback); 120 mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); 121 waitUntilHandlersIdle(); 122 assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty()); 123 assertFinishCallbackNotCalled(); 124 } 125 126 @Test 127 public void testCancel_running() throws Exception { 128 mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, 129 mMockTransaction); 130 mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, 131 mMockTransaction, this::finishedCallback); 132 waitUntilNextFrame(); 133 assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); 134 mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); 135 assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); 136 waitUntilHandlersIdle(); 137 assertFinishCallbackNotCalled(); 138 } 139 140 @FlakyTest(bugId = 71719744) 141 @Test 142 public void testCancel_sneakyCancelBeforeUpdate() throws Exception { 143 mSurfaceAnimationRunner = new SurfaceAnimationRunner(null, () -> new ValueAnimator() { 144 { 145 setFloatValues(0f, 1f); 146 } 147 148 @Override 149 public void addUpdateListener(AnimatorUpdateListener listener) { 150 super.addUpdateListener(animation -> { 151 // Sneaky test cancels animation just before applying frame to simulate 152 // interleaving of multiple threads. Muahahaha 153 if (animation.getCurrentPlayTime() > 0) { 154 mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); 155 } 156 listener.onAnimationUpdate(animation); 157 }); 158 } 159 }, mMockTransaction); 160 when(mMockAnimationSpec.getDuration()).thenReturn(200L); 161 mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction, 162 this::finishedCallback); 163 164 // We need to wait for two frames: The first frame starts the animation, the second frame 165 // actually cancels the animation. 166 waitUntilNextFrame(); 167 waitUntilNextFrame(); 168 assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); 169 verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L)); 170 } 171 172 @FlakyTest(bugId = 74780584) 173 @Test 174 public void testDeferStartingAnimations() throws Exception { 175 mSurfaceAnimationRunner.deferStartingAnimations(); 176 mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, 177 mMockTransaction, this::finishedCallback); 178 waitUntilNextFrame(); 179 assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); 180 mSurfaceAnimationRunner.continueStartingAnimations(); 181 waitUntilNextFrame(); 182 assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); 183 mFinishCallbackLatch.await(1, SECONDS); 184 assertFinishCallbackCalled(); 185 } 186 187 private void waitUntilNextFrame() throws Exception { 188 final CountDownLatch latch = new CountDownLatch(1); 189 mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, 190 latch::countDown, null /* token */); 191 latch.await(); 192 } 193 194 private void assertFinishCallbackCalled() { 195 assertEquals(0, mFinishCallbackLatch.getCount()); 196 } 197 198 private void assertFinishCallbackNotCalled() { 199 assertEquals(1, mFinishCallbackLatch.getCount()); 200 } 201 202 private AnimationSpec createTranslateAnimation() { 203 final Animation a = new TranslateAnimation(-10, 10, 0, 0); 204 a.initialize(0, 0, 0, 0); 205 a.setDuration(50); 206 return new WindowAnimationSpec(a, new Point(0, 0), false /* canSkipFirstFrame */); 207 } 208 209 /** 210 * Callback provider that doesn't animate at all. 211 */ 212 private static final class NoOpFrameCallbackProvider implements AnimationFrameCallbackProvider { 213 214 @Override 215 public void postFrameCallback(FrameCallback callback) { 216 } 217 218 @Override 219 public void postCommitCallback(Runnable runnable) { 220 } 221 222 @Override 223 public long getFrameTime() { 224 return 0; 225 } 226 227 @Override 228 public long getFrameDelay() { 229 return 0; 230 } 231 232 @Override 233 public void setFrameDelay(long delay) { 234 } 235 } 236 } 237