1 /* 2 * Copyright (C) 2021 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.wm.shell.transition; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 25 import static android.view.Display.DEFAULT_DISPLAY; 26 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 27 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; 28 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; 29 import static android.view.WindowManager.TRANSIT_CHANGE; 30 import static android.view.WindowManager.TRANSIT_CLOSE; 31 import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; 32 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; 33 import static android.view.WindowManager.TRANSIT_OPEN; 34 import static android.view.WindowManager.TRANSIT_SLEEP; 35 import static android.view.WindowManager.TRANSIT_TO_BACK; 36 import static android.view.WindowManager.TRANSIT_TO_FRONT; 37 import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; 38 import static android.window.TransitionInfo.FLAG_IS_DISPLAY; 39 import static android.window.TransitionInfo.FLAG_SYNC; 40 import static android.window.TransitionInfo.FLAG_TRANSLUCENT; 41 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 44 import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo; 45 46 import static org.junit.Assert.assertEquals; 47 import static org.junit.Assert.assertFalse; 48 import static org.junit.Assert.assertNotNull; 49 import static org.junit.Assert.assertNull; 50 import static org.junit.Assert.assertTrue; 51 import static org.mockito.ArgumentMatchers.any; 52 import static org.mockito.ArgumentMatchers.anyBoolean; 53 import static org.mockito.ArgumentMatchers.anyInt; 54 import static org.mockito.ArgumentMatchers.eq; 55 import static org.mockito.ArgumentMatchers.isA; 56 import static org.mockito.ArgumentMatchers.isNull; 57 import static org.mockito.Mockito.clearInvocations; 58 import static org.mockito.Mockito.doAnswer; 59 import static org.mockito.Mockito.inOrder; 60 import static org.mockito.Mockito.mock; 61 import static org.mockito.Mockito.times; 62 import static org.mockito.Mockito.verify; 63 64 import android.app.ActivityManager.RunningTaskInfo; 65 import android.app.IApplicationThread; 66 import android.app.PendingIntent; 67 import android.content.ComponentName; 68 import android.content.Context; 69 import android.content.Intent; 70 import android.os.Binder; 71 import android.os.Bundle; 72 import android.os.Handler; 73 import android.os.IBinder; 74 import android.os.Looper; 75 import android.os.RemoteException; 76 import android.platform.test.flag.junit.SetFlagsRule; 77 import android.util.ArraySet; 78 import android.util.Pair; 79 import android.view.IRecentsAnimationRunner; 80 import android.view.Surface; 81 import android.view.SurfaceControl; 82 import android.view.WindowManager; 83 import android.window.IRemoteTransition; 84 import android.window.IRemoteTransitionFinishedCallback; 85 import android.window.IWindowContainerToken; 86 import android.window.RemoteTransition; 87 import android.window.RemoteTransitionStub; 88 import android.window.TransitionFilter; 89 import android.window.TransitionInfo; 90 import android.window.TransitionRequestInfo; 91 import android.window.WindowAnimationState; 92 import android.window.WindowContainerToken; 93 import android.window.WindowContainerTransaction; 94 95 import androidx.annotation.NonNull; 96 import androidx.annotation.Nullable; 97 import androidx.test.ext.junit.runners.AndroidJUnit4; 98 import androidx.test.filters.SmallTest; 99 import androidx.test.platform.app.InstrumentationRegistry; 100 101 import com.android.internal.R; 102 import com.android.internal.policy.TransitionAnimation; 103 import com.android.systemui.shared.Flags; 104 import com.android.wm.shell.RootTaskDisplayAreaOrganizer; 105 import com.android.wm.shell.ShellTaskOrganizer; 106 import com.android.wm.shell.ShellTestCase; 107 import com.android.wm.shell.TestShellExecutor; 108 import com.android.wm.shell.common.DisplayController; 109 import com.android.wm.shell.common.DisplayLayout; 110 import com.android.wm.shell.common.ShellExecutor; 111 import com.android.wm.shell.common.TransactionPool; 112 import com.android.wm.shell.recents.RecentTasksController; 113 import com.android.wm.shell.recents.RecentsTransitionHandler; 114 import com.android.wm.shell.sysui.ShellController; 115 import com.android.wm.shell.sysui.ShellInit; 116 import com.android.wm.shell.sysui.ShellSharedConstants; 117 import com.android.wm.shell.util.StubTransaction; 118 119 import org.junit.Before; 120 import org.junit.Rule; 121 import org.junit.Test; 122 import org.junit.runner.RunWith; 123 import org.mockito.Answers; 124 import org.mockito.InOrder; 125 126 import java.util.ArrayList; 127 import java.util.function.Function; 128 129 /** 130 * Tests for the shell transitions. 131 * 132 * Build/Install/Run: 133 * atest WMShellUnitTests:ShellTransitionTests 134 */ 135 @SmallTest 136 @RunWith(AndroidJUnit4.class) 137 public class ShellTransitionTests extends ShellTestCase { 138 139 private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class); 140 private final TransactionPool mTransactionPool = mock(TransactionPool.class); 141 private final Context mContext = 142 InstrumentationRegistry.getInstrumentation().getTargetContext(); 143 private final TestShellExecutor mMainExecutor = new TestShellExecutor(); 144 private final ShellExecutor mAnimExecutor = new TestShellExecutor(); 145 private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); 146 private final Handler mMainHandler = new Handler(Looper.getMainLooper()); 147 148 @Rule 149 public final SetFlagsRule setFlagsRule = new SetFlagsRule(); 150 151 @Before setUp()152 public void setUp() { 153 doAnswer(invocation -> new Binder()) 154 .when(mOrganizer).startNewTransition(anyInt(), any()); 155 } 156 157 @Test instantiate_addInitCallback()158 public void instantiate_addInitCallback() { 159 ShellInit shellInit = mock(ShellInit.class); 160 final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), 161 mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, 162 mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); 163 // One from Transitions, one from RootTaskDisplayAreaOrganizer 164 verify(shellInit).addInitCallback(any(), eq(t)); 165 verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class)); 166 } 167 168 @Test instantiateController_addExternalInterface()169 public void instantiateController_addExternalInterface() { 170 ShellInit shellInit = new ShellInit(mMainExecutor); 171 ShellController shellController = mock(ShellController.class); 172 final Transitions t = new Transitions(mContext, shellInit, shellController, 173 mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, 174 mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); 175 shellInit.init(); 176 verify(shellController, times(1)).addExternalInterface( 177 eq(ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS), any(), any()); 178 } 179 180 @Test testBasicTransitionFlow()181 public void testBasicTransitionFlow() { 182 Transitions transitions = createTestTransitions(); 183 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 184 185 IBinder transitToken = new Binder(); 186 transitions.requestStartTransition(transitToken, 187 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 188 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 189 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 190 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 191 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 192 new StubTransaction()); 193 assertEquals(1, mDefaultHandler.activeCount()); 194 mDefaultHandler.finishAll(); 195 mMainExecutor.flushAll(); 196 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); 197 } 198 199 @Test testNonDefaultHandler()200 public void testNonDefaultHandler() { 201 Transitions transitions = createTestTransitions(); 202 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 203 204 final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); 205 // Make a test handler that only responds to multi-window triggers AND only animates 206 // Change transitions. 207 TestTransitionHandler testHandler = new TestTransitionHandler() { 208 @Override 209 public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 210 @NonNull SurfaceControl.Transaction startTransaction, 211 @NonNull SurfaceControl.Transaction finishTransaction, 212 @NonNull Transitions.TransitionFinishCallback finishCallback) { 213 for (TransitionInfo.Change chg : info.getChanges()) { 214 if (chg.getMode() == TRANSIT_CHANGE) { 215 return super.startAnimation(transition, info, startTransaction, 216 finishTransaction, finishCallback); 217 } 218 } 219 return false; 220 } 221 222 @Nullable 223 @Override 224 public WindowContainerTransaction handleRequest(@NonNull IBinder transition, 225 @NonNull TransitionRequestInfo request) { 226 final RunningTaskInfo task = request.getTriggerTask(); 227 return (task != null && task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) 228 ? handlerWCT : null; 229 } 230 }; 231 transitions.addHandler(testHandler); 232 233 IBinder transitToken = new Binder(); 234 TransitionInfo open = new TransitionInfoBuilder(TRANSIT_OPEN) 235 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 236 237 // Make a request that will be rejected by the testhandler. 238 transitions.requestStartTransition(transitToken, 239 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 240 verify(mOrganizer, times(1)).startTransition(eq(transitToken), isNull()); 241 transitions.onTransitionReady(transitToken, open, new StubTransaction(), 242 new StubTransaction()); 243 assertEquals(1, mDefaultHandler.activeCount()); 244 assertEquals(0, testHandler.activeCount()); 245 mDefaultHandler.finishAll(); 246 mMainExecutor.flushAll(); 247 248 // Make a request that will be handled by testhandler but not animated by it. 249 RunningTaskInfo mwTaskInfo = 250 createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 251 // Make the wct non-empty. 252 handlerWCT.setFocusable(new WindowContainerToken(mock(IWindowContainerToken.class)), true); 253 transitions.requestStartTransition(transitToken, 254 new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */)); 255 verify(mOrganizer, times(1)).startTransition( 256 eq(transitToken), eq(handlerWCT)); 257 transitions.onTransitionReady(transitToken, open, new StubTransaction(), 258 new StubTransaction()); 259 assertEquals(1, mDefaultHandler.activeCount()); 260 assertEquals(0, testHandler.activeCount()); 261 mDefaultHandler.finishAll(); 262 mMainExecutor.flushAll(); 263 264 // Make a request that will be handled AND animated by testhandler. 265 // Add an aggressive handler (doesn't handle but always animates) on top to make sure that 266 // the test handler gets first shot at animating since it claimed to handle it. 267 TestTransitionHandler topHandler = new TestTransitionHandler(); 268 transitions.addHandler(topHandler); 269 transitions.requestStartTransition(transitToken, 270 new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */)); 271 verify(mOrganizer, times(2)).startTransition( 272 eq(transitToken), eq(handlerWCT)); 273 TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) 274 .addChange(TRANSIT_CHANGE).build(); 275 transitions.onTransitionReady(transitToken, change, new StubTransaction(), 276 new StubTransaction()); 277 assertEquals(0, mDefaultHandler.activeCount()); 278 assertEquals(1, testHandler.activeCount()); 279 assertEquals(0, topHandler.activeCount()); 280 testHandler.finishAll(); 281 mMainExecutor.flushAll(); 282 } 283 284 @Test testRequestRemoteTransition()285 public void testRequestRemoteTransition() { 286 Transitions transitions = createTestTransitions(); 287 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 288 289 final boolean[] remoteCalled = new boolean[]{false}; 290 final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction(); 291 IRemoteTransition testRemote = new RemoteTransitionStub() { 292 @Override 293 public void startAnimation(IBinder token, TransitionInfo info, 294 SurfaceControl.Transaction t, 295 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 296 remoteCalled[0] = true; 297 finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); 298 } 299 }; 300 IBinder transitToken = new Binder(); 301 transitions.requestStartTransition(transitToken, 302 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, 303 new RemoteTransition(testRemote, "Test"))); 304 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 305 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 306 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 307 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 308 new StubTransaction()); 309 assertEquals(0, mDefaultHandler.activeCount()); 310 assertTrue(remoteCalled[0]); 311 mDefaultHandler.finishAll(); 312 mMainExecutor.flushAll(); 313 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), eq(remoteFinishWCT)); 314 } 315 316 @Test testTransitionFilterActivityType()317 public void testTransitionFilterActivityType() { 318 TransitionFilter filter = new TransitionFilter(); 319 filter.mRequirements = 320 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 321 filter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME; 322 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 323 324 final TransitionInfo openHome = new TransitionInfoBuilder(TRANSIT_OPEN) 325 .addChange(TRANSIT_OPEN, 326 createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME)).build(); 327 assertTrue(filter.matches(openHome)); 328 329 final TransitionInfo openStd = new TransitionInfoBuilder(TRANSIT_OPEN) 330 .addChange(TRANSIT_OPEN, createTaskInfo( 331 1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD)).build(); 332 assertFalse(filter.matches(openStd)); 333 } 334 335 @Test testTransitionFilterMultiRequirement()336 public void testTransitionFilterMultiRequirement() { 337 // filter that requires at-least one opening and one closing app 338 TransitionFilter filter = new TransitionFilter(); 339 filter.mRequirements = new TransitionFilter.Requirement[]{ 340 new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; 341 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 342 filter.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; 343 344 final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) 345 .addChange(TRANSIT_OPEN).build(); 346 assertFalse(filter.matches(openOnly)); 347 348 final TransitionInfo openClose = new TransitionInfoBuilder(TRANSIT_OPEN) 349 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 350 assertTrue(filter.matches(openClose)); 351 } 352 353 @Test testTransitionFilterNotRequirement()354 public void testTransitionFilterNotRequirement() { 355 // filter that requires one opening and NO translucent apps 356 TransitionFilter filter = new TransitionFilter(); 357 filter.mRequirements = new TransitionFilter.Requirement[]{ 358 new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; 359 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 360 filter.mRequirements[1].mFlags = FLAG_TRANSLUCENT; 361 filter.mRequirements[1].mNot = true; 362 363 final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) 364 .addChange(TRANSIT_OPEN).build(); 365 assertTrue(filter.matches(openOnly)); 366 367 final TransitionInfo openAndTranslucent = new TransitionInfoBuilder(TRANSIT_OPEN) 368 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 369 openAndTranslucent.getChanges().get(1).setFlags(FLAG_TRANSLUCENT); 370 assertFalse(filter.matches(openAndTranslucent)); 371 } 372 373 @Test testTransitionFilterChecksTypeSet()374 public void testTransitionFilterChecksTypeSet() { 375 TransitionFilter filter = new TransitionFilter(); 376 filter.mTypeSet = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 377 378 final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) 379 .addChange(TRANSIT_OPEN).build(); 380 assertTrue(filter.matches(openOnly)); 381 382 final TransitionInfo toFrontOnly = new TransitionInfoBuilder(TRANSIT_TO_FRONT) 383 .addChange(TRANSIT_TO_FRONT).build(); 384 assertTrue(filter.matches(toFrontOnly)); 385 386 final TransitionInfo closeOnly = new TransitionInfoBuilder(TRANSIT_CLOSE) 387 .addChange(TRANSIT_CLOSE).build(); 388 assertFalse(filter.matches(closeOnly)); 389 } 390 391 @Test testTransitionFilterChecksFlags()392 public void testTransitionFilterChecksFlags() { 393 TransitionFilter filter = new TransitionFilter(); 394 filter.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; 395 396 final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK, 397 TRANSIT_FLAG_KEYGUARD_GOING_AWAY) 398 .addChange(TRANSIT_TO_BACK).build(); 399 assertTrue(filter.matches(withFlag)); 400 401 final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN) 402 .addChange(TRANSIT_OPEN).build(); 403 assertFalse(filter.matches(withoutFlag)); 404 } 405 406 @Test testTransitionFilterChecksNotFlags()407 public void testTransitionFilterChecksNotFlags() { 408 TransitionFilter filter = new TransitionFilter(); 409 filter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; 410 411 final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK, 412 TRANSIT_FLAG_KEYGUARD_GOING_AWAY) 413 .addChange(TRANSIT_TO_BACK).build(); 414 assertFalse(filter.matches(withFlag)); 415 416 final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN) 417 .addChange(TRANSIT_OPEN).build(); 418 assertTrue(filter.matches(withoutFlag)); 419 } 420 421 @Test testTransitionFilterActivityComponent()422 public void testTransitionFilterActivityComponent() { 423 TransitionFilter filter = new TransitionFilter(); 424 ComponentName cmpt = new ComponentName("testpak", "testcls"); 425 filter.mRequirements = 426 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 427 filter.mRequirements[0].mTopActivity = cmpt; 428 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 429 430 final RunningTaskInfo taskInf = createTaskInfo(1); 431 final TransitionInfo openTask = new TransitionInfoBuilder(TRANSIT_OPEN) 432 .addChange(TRANSIT_OPEN, taskInf).build(); 433 assertFalse(filter.matches(openTask)); 434 435 taskInf.topActivity = cmpt; 436 final TransitionInfo openTaskCmpt = new TransitionInfoBuilder(TRANSIT_OPEN) 437 .addChange(TRANSIT_OPEN, taskInf).build(); 438 assertTrue(filter.matches(openTaskCmpt)); 439 440 final TransitionInfo openAct = new TransitionInfoBuilder(TRANSIT_OPEN) 441 .addChange(TRANSIT_OPEN, cmpt).build(); 442 assertTrue(filter.matches(openAct)); 443 } 444 445 @Test testRegisteredRemoteTransition()446 public void testRegisteredRemoteTransition() { 447 Transitions transitions = createTestTransitions(); 448 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 449 450 final boolean[] remoteCalled = new boolean[]{false}; 451 IRemoteTransition testRemote = new RemoteTransitionStub() { 452 @Override 453 public void startAnimation(IBinder token, TransitionInfo info, 454 SurfaceControl.Transaction t, 455 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 456 remoteCalled[0] = true; 457 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 458 } 459 }; 460 461 TransitionFilter filter = new TransitionFilter(); 462 filter.mRequirements = 463 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 464 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 465 466 transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test")); 467 mMainExecutor.flushAll(); 468 469 IBinder transitToken = new Binder(); 470 transitions.requestStartTransition(transitToken, 471 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 472 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 473 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 474 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 475 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 476 new StubTransaction()); 477 assertEquals(0, mDefaultHandler.activeCount()); 478 assertTrue(remoteCalled[0]); 479 mDefaultHandler.finishAll(); 480 mMainExecutor.flushAll(); 481 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); 482 } 483 484 @Test testRegisteredRemoteTransitionTakeover()485 public void testRegisteredRemoteTransitionTakeover() { 486 Transitions transitions = createTestTransitions(); 487 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 488 489 IRemoteTransition testRemote = new RemoteTransitionStub() { 490 @Override 491 public void startAnimation(IBinder token, TransitionInfo info, 492 SurfaceControl.Transaction t, 493 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 494 final Transitions.TransitionHandler takeoverHandler = 495 transitions.getHandlerForTakeover(token, info); 496 497 if (takeoverHandler == null) { 498 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 499 return; 500 } 501 502 takeoverHandler.takeOverAnimation(token, info, new SurfaceControl.Transaction(), 503 wct -> { 504 try { 505 finishCallback.onTransitionFinished(wct, null /* sct */); 506 } catch (RemoteException e) { 507 // Fail 508 } 509 }, new WindowAnimationState[info.getChanges().size()]); 510 } 511 }; 512 final boolean[] takeoverRemoteCalled = new boolean[]{false}; 513 IRemoteTransition testTakeoverRemote = new RemoteTransitionStub() { 514 @Override 515 public void startAnimation(IBinder token, TransitionInfo info, 516 SurfaceControl.Transaction t, 517 IRemoteTransitionFinishedCallback finishCallback) {} 518 519 @Override 520 public void takeOverAnimation(IBinder transition, TransitionInfo info, 521 SurfaceControl.Transaction startTransaction, 522 IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states) 523 throws RemoteException { 524 takeoverRemoteCalled[0] = true; 525 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 526 } 527 }; 528 529 TransitionFilter filter = new TransitionFilter(); 530 filter.mRequirements = 531 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 532 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 533 534 transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test")); 535 transitions.registerRemoteForTakeover( 536 filter, new RemoteTransition(testTakeoverRemote, "Test")); 537 mMainExecutor.flushAll(); 538 539 // Takeover shouldn't happen when the flag is disabled. 540 setFlagsRule.disableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY); 541 IBinder transitToken = new Binder(); 542 transitions.requestStartTransition(transitToken, 543 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 544 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 545 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 546 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 547 new StubTransaction()); 548 assertEquals(0, mDefaultHandler.activeCount()); 549 assertFalse(takeoverRemoteCalled[0]); 550 mDefaultHandler.finishAll(); 551 mMainExecutor.flushAll(); 552 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); 553 554 // Takeover should happen when the flag is enabled. 555 setFlagsRule.enableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY); 556 transitions.requestStartTransition(transitToken, 557 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 558 info = new TransitionInfoBuilder(TRANSIT_OPEN) 559 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 560 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 561 new StubTransaction()); 562 assertEquals(0, mDefaultHandler.activeCount()); 563 assertTrue(takeoverRemoteCalled[0]); 564 mDefaultHandler.finishAll(); 565 mMainExecutor.flushAll(); 566 verify(mOrganizer, times(2)).finishTransition(eq(transitToken), any()); 567 } 568 569 @Test testOneShotRemoteHandler()570 public void testOneShotRemoteHandler() { 571 Transitions transitions = createTestTransitions(); 572 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 573 574 final boolean[] remoteCalled = new boolean[]{false}; 575 final boolean[] takeoverRemoteCalled = new boolean[]{false}; 576 final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction(); 577 IRemoteTransition testRemote = new RemoteTransitionStub() { 578 @Override 579 public void startAnimation(IBinder token, TransitionInfo info, 580 SurfaceControl.Transaction t, 581 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 582 remoteCalled[0] = true; 583 finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); 584 } 585 586 @Override 587 public void takeOverAnimation(IBinder transition, TransitionInfo info, 588 SurfaceControl.Transaction startTransaction, 589 IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states) 590 throws RemoteException { 591 takeoverRemoteCalled[0] = true; 592 finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); 593 } 594 }; 595 596 final int transitType = TRANSIT_FIRST_CUSTOM + 1; 597 598 OneShotRemoteHandler oneShot = new OneShotRemoteHandler(mMainExecutor, 599 new RemoteTransition(testRemote, "Test")); 600 601 // Verify that it responds to the remote but not other things. 602 IBinder transitToken = new Binder(); 603 assertNotNull(oneShot.handleRequest(transitToken, 604 new TransitionRequestInfo(transitType, null, 605 new RemoteTransition(testRemote, "Test")))); 606 assertNull(oneShot.handleRequest(transitToken, 607 new TransitionRequestInfo(transitType, null, null))); 608 609 Transitions.TransitionFinishCallback testFinish = 610 mock(Transitions.TransitionFinishCallback.class); 611 612 // Verify that it responds to animation properly 613 oneShot.setTransition(transitToken); 614 IBinder anotherToken = new Binder(); 615 assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0), 616 new StubTransaction(), new StubTransaction(), 617 testFinish)); 618 assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0), 619 new StubTransaction(), new StubTransaction(), 620 testFinish)); 621 assertTrue(remoteCalled[0]); 622 623 // Verify that it handles takeovers properly 624 IBinder newToken = new Binder(); 625 oneShot.setTransition(newToken); 626 assertFalse(oneShot.takeOverAnimation(transitToken, new TransitionInfo(transitType, 0), 627 new StubTransaction(), testFinish, new WindowAnimationState[0])); 628 assertTrue(oneShot.takeOverAnimation(newToken, new TransitionInfo(transitType, 0), 629 new StubTransaction(), testFinish, new WindowAnimationState[0])); 630 assertTrue(takeoverRemoteCalled[0]); 631 } 632 633 @Test testTransitionQueueing()634 public void testTransitionQueueing() { 635 Transitions transitions = createTestTransitions(); 636 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 637 638 IBinder transitToken1 = new Binder(); 639 transitions.requestStartTransition(transitToken1, 640 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 641 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 642 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 643 transitions.onTransitionReady(transitToken1, info1, new StubTransaction(), 644 new StubTransaction()); 645 assertEquals(1, mDefaultHandler.activeCount()); 646 647 IBinder transitToken2 = new Binder(); 648 transitions.requestStartTransition(transitToken2, 649 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 650 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 651 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 652 transitions.onTransitionReady(transitToken2, info2, new StubTransaction(), 653 new StubTransaction()); 654 // default handler doesn't merge by default, so it shouldn't increment active count. 655 assertEquals(1, mDefaultHandler.activeCount()); 656 assertEquals(0, mDefaultHandler.mergeCount()); 657 verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any()); 658 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 659 660 mDefaultHandler.finishAll(); 661 mMainExecutor.flushAll(); 662 // first transition finished 663 verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); 664 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 665 // But now the "queued" transition is running 666 assertEquals(1, mDefaultHandler.activeCount()); 667 668 mDefaultHandler.finishAll(); 669 mMainExecutor.flushAll(); 670 verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); 671 } 672 673 @Test testTransitionMerging()674 public void testTransitionMerging() { 675 Transitions transitions = createTestTransitions(); 676 mDefaultHandler.setSimulateMerge(true); 677 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 678 679 IBinder transitToken1 = new Binder(); 680 transitions.requestStartTransition(transitToken1, 681 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 682 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 683 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 684 transitions.onTransitionReady(transitToken1, info1, new StubTransaction(), 685 new StubTransaction()); 686 assertEquals(1, mDefaultHandler.activeCount()); 687 688 IBinder transitToken2 = new Binder(); 689 transitions.requestStartTransition(transitToken2, 690 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 691 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 692 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 693 transitions.onTransitionReady(transitToken2, info2, new StubTransaction(), 694 new StubTransaction()); 695 // it should still only have 1 active, but then show 1 merged 696 assertEquals(1, mDefaultHandler.activeCount()); 697 assertEquals(1, mDefaultHandler.mergeCount()); 698 verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any()); 699 // We don't tell organizer it is finished yet (since we still want to maintain ordering) 700 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 701 702 mDefaultHandler.finishAll(); 703 mMainExecutor.flushAll(); 704 // transition + merged all finished. 705 verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); 706 verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); 707 // Make sure nothing was queued 708 assertEquals(0, mDefaultHandler.activeCount()); 709 } 710 711 712 @Test testTransitionMergingOnFinish()713 public void testTransitionMergingOnFinish() { 714 final Transitions transitions = createTestTransitions(); 715 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 716 717 // The current transition. 718 final IBinder transitToken1 = new Binder(); 719 requestStartTransition(transitions, transitToken1); 720 onTransitionReady(transitions, transitToken1); 721 722 // The next ready transition. 723 final IBinder transitToken2 = new Binder(); 724 requestStartTransition(transitions, transitToken2); 725 onTransitionReady(transitions, transitToken2); 726 727 // The non-ready merge candidate. 728 final IBinder transitTokenNotReady = new Binder(); 729 requestStartTransition(transitions, transitTokenNotReady); 730 731 mDefaultHandler.setSimulateMerge(true); 732 mDefaultHandler.mFinishes.get(0).second.onTransitionFinished(null /* wct */); 733 734 // Make sure that the non-ready transition is not merged. 735 assertEquals(0, mDefaultHandler.mergeCount()); 736 } 737 738 @Test testInterleavedMerging()739 public void testInterleavedMerging() { 740 Transitions transitions = createTestTransitions(); 741 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 742 743 Function<Boolean, IBinder> startATransition = (doMerge) -> { 744 IBinder token = new Binder(); 745 if (doMerge) { 746 mDefaultHandler.setShouldMerge(token); 747 } 748 transitions.requestStartTransition(token, 749 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 750 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 751 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 752 transitions.onTransitionReady(token, info, new StubTransaction(), 753 new StubTransaction()); 754 return token; 755 }; 756 757 IBinder transitToken1 = startATransition.apply(false); 758 // merge first one 759 IBinder transitToken2 = startATransition.apply(true); 760 assertEquals(1, mDefaultHandler.activeCount()); 761 assertEquals(1, mDefaultHandler.mergeCount()); 762 763 // don't merge next one 764 IBinder transitToken3 = startATransition.apply(false); 765 // make sure nothing happened (since it wasn't merged) 766 assertEquals(1, mDefaultHandler.activeCount()); 767 assertEquals(1, mDefaultHandler.mergeCount()); 768 769 // make a mergable 770 IBinder transitToken4 = startATransition.apply(true); 771 // make sure nothing happened since there is a non-mergable pending. 772 assertEquals(1, mDefaultHandler.activeCount()); 773 assertEquals(1, mDefaultHandler.mergeCount()); 774 775 // Queue up another mergable 776 IBinder transitToken5 = startATransition.apply(true); 777 778 // Queue up a non-mergable 779 IBinder transitToken6 = startATransition.apply(false); 780 781 // Our active now looks like: [playing, merged] 782 // and ready queue: [non-mergable, mergable, mergable, non-mergable] 783 // finish the playing one 784 mDefaultHandler.finishOne(); 785 mMainExecutor.flushAll(); 786 // Now we should have the non-mergable playing now with 2 merged: 787 // active: [playing, merged, merged] queue: [non-mergable] 788 assertEquals(1, mDefaultHandler.activeCount()); 789 assertEquals(2, mDefaultHandler.mergeCount()); 790 791 mDefaultHandler.finishOne(); 792 mMainExecutor.flushAll(); 793 assertEquals(1, mDefaultHandler.activeCount()); 794 assertEquals(0, mDefaultHandler.mergeCount()); 795 796 mDefaultHandler.finishOne(); 797 mMainExecutor.flushAll(); 798 } 799 800 @Test testTransitionOrderMatchesCore()801 public void testTransitionOrderMatchesCore() { 802 Transitions transitions = createTestTransitions(); 803 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 804 805 IBinder transitToken = new Binder(); 806 IBinder shellInit = transitions.startTransition(TRANSIT_CLOSE, 807 new WindowContainerTransaction(), null /* handler */); 808 // make sure we are testing the "New" API. 809 verify(mOrganizer, times(1)).startNewTransition(eq(TRANSIT_CLOSE), any()); 810 // WMCore may not receive the new transition before requesting its own. 811 transitions.requestStartTransition(transitToken, 812 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 813 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 814 815 // At this point, WM is working on its transition (the shell-initialized one is still 816 // queued), so continue the transition lifecycle for that. 817 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 818 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 819 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 820 new StubTransaction()); 821 // At this point, if things are not working, we'd get an NPE due to attempting to merge 822 // into the shellInit transition which hasn't started yet. 823 assertEquals(1, mDefaultHandler.activeCount()); 824 } 825 826 @Test testShouldRotateSeamlessly()827 public void testShouldRotateSeamlessly() throws Exception { 828 final RunningTaskInfo taskInfo = 829 createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 830 final RunningTaskInfo taskInfoPip = 831 createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); 832 833 final DisplayController displays = createTestDisplayController(); 834 final DisplayLayout displayLayout = displays.getDisplayLayout(DEFAULT_DISPLAY); 835 final @Surface.Rotation int upsideDown = displayLayout.getUpsideDownRotation(); 836 837 TransitionInfo.Change displayChange = new ChangeBuilder(TRANSIT_CHANGE) 838 .setFlags(FLAG_IS_DISPLAY).setRotate().build(); 839 // Set non-square display so nav bar won't be allowed to move. 840 displayChange.getStartAbsBounds().set(0, 0, 1000, 2000); 841 final TransitionInfo normalDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE) 842 .addChange(displayChange) 843 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo).setRotate().build()) 844 .build(); 845 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 846 displayChange, normalDispRotate, displays)); 847 848 // Seamless if all tasks are seamless 849 final TransitionInfo rotateSeamless = new TransitionInfoBuilder(TRANSIT_CHANGE) 850 .addChange(displayChange) 851 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 852 .setRotate(ROTATION_ANIMATION_SEAMLESS).build()) 853 .build(); 854 assertEquals(ROTATION_ANIMATION_SEAMLESS, DefaultTransitionHandler.getRotationAnimationHint( 855 displayChange, rotateSeamless, displays)); 856 857 // Not seamless if there is PiP (or any other non-seamless task) 858 final TransitionInfo pipDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE) 859 .addChange(displayChange) 860 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 861 .setRotate(ROTATION_ANIMATION_SEAMLESS).build()) 862 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfoPip) 863 .setRotate().build()) 864 .build(); 865 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 866 displayChange, pipDispRotate, displays)); 867 868 // Not seamless if there is no changed task. 869 final TransitionInfo noTask = new TransitionInfoBuilder(TRANSIT_CHANGE) 870 .addChange(displayChange) 871 .build(); 872 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 873 displayChange, noTask, displays)); 874 875 // Not seamless if the nav bar cares rotation and one of rotations is upside-down. 876 doReturn(false).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving(); 877 displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY) 878 .setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build(); 879 final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE) 880 .addChange(displayChange) 881 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 882 .setRotate(upsideDown, ROTATION_ANIMATION_SEAMLESS).build()) 883 .build(); 884 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 885 displayChange, seamlessUpsideDown, displays)); 886 887 // Not seamless if system alert windows 888 displayChange = new ChangeBuilder(TRANSIT_CHANGE) 889 .setFlags(FLAG_IS_DISPLAY | FLAG_DISPLAY_HAS_ALERT_WINDOWS).setRotate().build(); 890 final TransitionInfo seamlessButAlert = new TransitionInfoBuilder(TRANSIT_CHANGE) 891 .addChange(displayChange) 892 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 893 .setRotate(ROTATION_ANIMATION_SEAMLESS).build()) 894 .build(); 895 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 896 displayChange, seamlessButAlert, displays)); 897 898 // Seamless if display is explicitly seamless. 899 displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY) 900 .setRotate(ROTATION_ANIMATION_SEAMLESS).build(); 901 final TransitionInfo seamlessDisplay = new TransitionInfoBuilder(TRANSIT_CHANGE) 902 .addChange(displayChange) 903 // The animation hint of task will be ignored. 904 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 905 .setRotate(ROTATION_ANIMATION_ROTATE).build()) 906 .build(); 907 assertEquals(ROTATION_ANIMATION_SEAMLESS, DefaultTransitionHandler.getRotationAnimationHint( 908 displayChange, seamlessDisplay, displays)); 909 } 910 911 @Test testRunWhenIdle()912 public void testRunWhenIdle() { 913 Transitions transitions = createTestTransitions(); 914 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 915 916 Runnable runnable1 = mock(Runnable.class); 917 Runnable runnable2 = mock(Runnable.class); 918 Runnable runnable3 = mock(Runnable.class); 919 Runnable runnable4 = mock(Runnable.class); 920 921 transitions.runOnIdle(runnable1); 922 923 // runnable1 is executed immediately because there are no active transitions. 924 verify(runnable1, times(1)).run(); 925 926 clearInvocations(runnable1); 927 928 IBinder transitToken1 = new Binder(); 929 transitions.requestStartTransition(transitToken1, 930 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 931 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 932 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 933 transitions.onTransitionReady(transitToken1, info1, new StubTransaction(), 934 new StubTransaction()); 935 assertEquals(1, mDefaultHandler.activeCount()); 936 937 transitions.runOnIdle(runnable2); 938 transitions.runOnIdle(runnable3); 939 940 // runnable2 and runnable3 aren't executed immediately because there is an active 941 // transaction. 942 943 IBinder transitToken2 = new Binder(); 944 transitions.requestStartTransition(transitToken2, 945 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 946 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 947 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 948 transitions.onTransitionReady(transitToken2, info2, new StubTransaction(), 949 new StubTransaction()); 950 assertEquals(1, mDefaultHandler.activeCount()); 951 952 mDefaultHandler.finishAll(); 953 mMainExecutor.flushAll(); 954 // first transition finished 955 verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); 956 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 957 // But now the "queued" transition is running 958 assertEquals(1, mDefaultHandler.activeCount()); 959 960 // runnable2 and runnable3 are still not executed because the second transition is still 961 // active. 962 verify(runnable2, times(0)).run(); 963 verify(runnable3, times(0)).run(); 964 965 mDefaultHandler.finishAll(); 966 mMainExecutor.flushAll(); 967 verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); 968 969 // runnable2 and runnable3 are executed after the second transition finishes because there 970 // are no other active transitions, runnable1 isn't executed again. 971 verify(runnable1, times(0)).run(); 972 verify(runnable2, times(1)).run(); 973 verify(runnable3, times(1)).run(); 974 975 clearInvocations(runnable2); 976 clearInvocations(runnable3); 977 978 transitions.runOnIdle(runnable4); 979 980 // runnable4 is executed immediately because there are no active transitions, all other 981 // runnables aren't executed again. 982 verify(runnable1, times(0)).run(); 983 verify(runnable2, times(0)).run(); 984 verify(runnable3, times(0)).run(); 985 verify(runnable4, times(1)).run(); 986 } 987 988 @Test testObserverLifecycle_basicTransitionFlow()989 public void testObserverLifecycle_basicTransitionFlow() { 990 Transitions transitions = createTestTransitions(); 991 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 992 transitions.registerObserver(observer); 993 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 994 995 IBinder transitToken = new Binder(); 996 transitions.requestStartTransition(transitToken, 997 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 998 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 999 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1000 SurfaceControl.Transaction startT = new StubTransaction(); 1001 SurfaceControl.Transaction finishT = new StubTransaction(); 1002 transitions.onTransitionReady(transitToken, info, startT, finishT); 1003 1004 InOrder observerOrder = inOrder(observer); 1005 observerOrder.verify(observer).onTransitionReady(transitToken, info, startT, finishT); 1006 observerOrder.verify(observer).onTransitionStarting(transitToken); 1007 verify(observer, times(0)).onTransitionFinished(eq(transitToken), anyBoolean()); 1008 mDefaultHandler.finishAll(); 1009 mMainExecutor.flushAll(); 1010 verify(observer).onTransitionFinished(transitToken, false); 1011 } 1012 1013 @Test testObserverLifecycle_queueing()1014 public void testObserverLifecycle_queueing() { 1015 Transitions transitions = createTestTransitions(); 1016 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1017 transitions.registerObserver(observer); 1018 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1019 1020 IBinder transitToken1 = new Binder(); 1021 transitions.requestStartTransition(transitToken1, 1022 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1023 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 1024 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1025 SurfaceControl.Transaction startT1 = new StubTransaction(); 1026 SurfaceControl.Transaction finishT1 = new StubTransaction(); 1027 transitions.onTransitionReady(transitToken1, info1, startT1, finishT1); 1028 verify(observer).onTransitionReady(transitToken1, info1, startT1, finishT1); 1029 1030 IBinder transitToken2 = new Binder(); 1031 transitions.requestStartTransition(transitToken2, 1032 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 1033 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 1034 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1035 SurfaceControl.Transaction startT2 = new StubTransaction(); 1036 SurfaceControl.Transaction finishT2 = new StubTransaction(); 1037 transitions.onTransitionReady(transitToken2, info2, startT2, finishT2); 1038 verify(observer, times(1)).onTransitionReady(transitToken2, info2, startT2, finishT2); 1039 verify(observer, times(0)).onTransitionStarting(transitToken2); 1040 verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean()); 1041 verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); 1042 1043 mDefaultHandler.finishAll(); 1044 mMainExecutor.flushAll(); 1045 // first transition finished 1046 verify(observer, times(1)).onTransitionFinished(transitToken1, false); 1047 verify(observer, times(1)).onTransitionStarting(transitToken2); 1048 verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); 1049 1050 mDefaultHandler.finishAll(); 1051 mMainExecutor.flushAll(); 1052 verify(observer, times(1)).onTransitionFinished(transitToken2, false); 1053 } 1054 1055 1056 @Test testObserverLifecycle_merging()1057 public void testObserverLifecycle_merging() { 1058 Transitions transitions = createTestTransitions(); 1059 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1060 transitions.registerObserver(observer); 1061 mDefaultHandler.setSimulateMerge(true); 1062 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1063 1064 IBinder transitToken1 = new Binder(); 1065 transitions.requestStartTransition(transitToken1, 1066 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1067 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 1068 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1069 SurfaceControl.Transaction startT1 = new StubTransaction(); 1070 SurfaceControl.Transaction finishT1 = new StubTransaction(); 1071 transitions.onTransitionReady(transitToken1, info1, startT1, finishT1); 1072 1073 IBinder transitToken2 = new Binder(); 1074 transitions.requestStartTransition(transitToken2, 1075 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 1076 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 1077 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1078 SurfaceControl.Transaction startT2 = new StubTransaction(); 1079 SurfaceControl.Transaction finishT2 = new StubTransaction(); 1080 transitions.onTransitionReady(transitToken2, info2, startT2, finishT2); 1081 1082 InOrder observerOrder = inOrder(observer); 1083 observerOrder.verify(observer).onTransitionReady(transitToken2, info2, startT2, finishT2); 1084 observerOrder.verify(observer).onTransitionMerged(transitToken2, transitToken1); 1085 verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean()); 1086 1087 mDefaultHandler.finishAll(); 1088 mMainExecutor.flushAll(); 1089 // transition + merged all finished. 1090 verify(observer, times(1)).onTransitionFinished(transitToken1, false); 1091 // Merged transition won't receive any lifecycle calls beyond ready 1092 verify(observer, times(0)).onTransitionStarting(transitToken2); 1093 verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); 1094 } 1095 1096 @Test testObserverLifecycle_mergingAfterQueueing()1097 public void testObserverLifecycle_mergingAfterQueueing() { 1098 Transitions transitions = createTestTransitions(); 1099 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1100 transitions.registerObserver(observer); 1101 mDefaultHandler.setSimulateMerge(true); 1102 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1103 1104 // Make a test handler that only responds to multi-window triggers AND only animates 1105 // Change transitions. 1106 final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); 1107 TestTransitionHandler testHandler = new TestTransitionHandler() { 1108 @Override 1109 public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 1110 @NonNull SurfaceControl.Transaction startTransaction, 1111 @NonNull SurfaceControl.Transaction finishTransaction, 1112 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1113 for (TransitionInfo.Change chg : info.getChanges()) { 1114 if (chg.getMode() == TRANSIT_CHANGE) { 1115 return super.startAnimation(transition, info, startTransaction, 1116 finishTransaction, finishCallback); 1117 } 1118 } 1119 return false; 1120 } 1121 1122 @Nullable 1123 @Override 1124 public WindowContainerTransaction handleRequest(@NonNull IBinder transition, 1125 @NonNull TransitionRequestInfo request) { 1126 final RunningTaskInfo task = request.getTriggerTask(); 1127 return (task != null && task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) 1128 ? handlerWCT : null; 1129 } 1130 }; 1131 transitions.addHandler(testHandler); 1132 1133 // Use test handler to play an animation 1134 IBinder transitToken1 = new Binder(); 1135 RunningTaskInfo mwTaskInfo = 1136 createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 1137 transitions.requestStartTransition(transitToken1, 1138 new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */)); 1139 TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) 1140 .addChange(TRANSIT_CHANGE).build(); 1141 SurfaceControl.Transaction startT1 = new StubTransaction(); 1142 SurfaceControl.Transaction finishT1 = new StubTransaction(); 1143 transitions.onTransitionReady(transitToken1, change, startT1, finishT1); 1144 1145 // Request the second transition that should be handled by the default handler 1146 IBinder transitToken2 = new Binder(); 1147 TransitionInfo open = new TransitionInfoBuilder(TRANSIT_OPEN) 1148 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1149 transitions.requestStartTransition(transitToken2, 1150 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1151 SurfaceControl.Transaction startT2 = new StubTransaction(); 1152 SurfaceControl.Transaction finishT2 = new StubTransaction(); 1153 transitions.onTransitionReady(transitToken2, open, startT2, finishT2); 1154 verify(observer).onTransitionReady(transitToken2, open, startT2, finishT2); 1155 verify(observer, times(0)).onTransitionStarting(transitToken2); 1156 1157 // Request the third transition that should be merged into the second one 1158 IBinder transitToken3 = new Binder(); 1159 transitions.requestStartTransition(transitToken3, 1160 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1161 SurfaceControl.Transaction startT3 = new StubTransaction(); 1162 SurfaceControl.Transaction finishT3 = new StubTransaction(); 1163 transitions.onTransitionReady(transitToken3, open, startT3, finishT3); 1164 verify(observer, times(0)).onTransitionStarting(transitToken2); 1165 verify(observer).onTransitionReady(transitToken3, open, startT3, finishT3); 1166 verify(observer, times(0)).onTransitionStarting(transitToken3); 1167 1168 testHandler.finishAll(); 1169 mMainExecutor.flushAll(); 1170 1171 verify(observer).onTransitionFinished(transitToken1, false); 1172 1173 mDefaultHandler.finishAll(); 1174 mMainExecutor.flushAll(); 1175 1176 InOrder observerOrder = inOrder(observer); 1177 observerOrder.verify(observer).onTransitionStarting(transitToken2); 1178 observerOrder.verify(observer).onTransitionMerged(transitToken3, transitToken2); 1179 observerOrder.verify(observer).onTransitionFinished(transitToken2, false); 1180 1181 // Merged transition won't receive any lifecycle calls beyond ready 1182 verify(observer, times(0)).onTransitionStarting(transitToken3); 1183 verify(observer, times(0)).onTransitionFinished(eq(transitToken3), anyBoolean()); 1184 } 1185 1186 @Test testTransitSleep_squashesRecents()1187 public void testTransitSleep_squashesRecents() { 1188 ShellInit shellInit = new ShellInit(mMainExecutor); 1189 final Transitions transitions = 1190 new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer, 1191 mTransactionPool, createTestDisplayController(), mMainExecutor, 1192 mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); 1193 final RecentsTransitionHandler recentsHandler = 1194 new RecentsTransitionHandler(shellInit, transitions, 1195 mock(RecentTasksController.class), mock(HomeTransitionObserver.class)); 1196 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1197 shellInit.init(); 1198 1199 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1200 transitions.registerObserver(observer); 1201 1202 RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS); 1203 RunningTaskInfo task2 = createTaskInfo(2); 1204 1205 // Start an open transition for the purpose of occupying the ready queue 1206 final IBinder transitOpen1 = new Binder("transitOpen1"); 1207 final TransitionInfo infoOpen1 = 1208 new TransitionInfoBuilder(TRANSIT_OPEN) 1209 .addChange(TRANSIT_OPEN, task1) 1210 .build(); 1211 mMainExecutor.execute(() -> { 1212 transitions.requestStartTransition(transitOpen1, new TransitionRequestInfo( 1213 TRANSIT_OPEN, task1 /* trigger */, null /* remote */)); 1214 onTransitionReady(transitions, transitOpen1, infoOpen1); 1215 }); 1216 1217 // First transition on the queue should start immediately. 1218 mMainExecutor.flushAll(); 1219 verify(observer).onTransitionReady(eq(transitOpen1), any(), any(), any()); 1220 verify(observer).onTransitionStarting(eq(transitOpen1)); 1221 1222 // Start recents 1223 final IRecentsAnimationRunner recentsListener = 1224 mock(IRecentsAnimationRunner.class, Answers.RETURNS_DEEP_STUBS); 1225 final IBinder transitRecents = recentsHandler.startRecentsTransition( 1226 mock(PendingIntent.class) /* intent */, 1227 mock(Intent.class) /* fillIn */, 1228 new Bundle() /* options */, 1229 mock(IApplicationThread.class) /* appThread */, 1230 recentsListener); 1231 final TransitionInfo infoRecents = 1232 new TransitionInfoBuilder(TRANSIT_TO_FRONT) 1233 .addChange(TRANSIT_TO_FRONT, task1) 1234 .addChange(TRANSIT_CLOSE, task2) 1235 .build(); 1236 onTransitionReady(transitions, transitRecents, infoRecents); 1237 1238 // Start another open transition during recents 1239 final IBinder transitOpen2 = new Binder("transitOpen2"); 1240 final TransitionInfo infoOpen2 = 1241 new TransitionInfoBuilder(TRANSIT_OPEN) 1242 .addChange(TRANSIT_OPEN, task2) 1243 .addChange(TRANSIT_TO_BACK, task1) 1244 .build(); 1245 mMainExecutor.execute(() -> { 1246 transitions.requestStartTransition(transitOpen2, new TransitionRequestInfo( 1247 TRANSIT_OPEN, task2 /* trigger */, null /* remote */)); 1248 onTransitionReady(transitions, transitOpen2, infoOpen2); 1249 }); 1250 1251 // Finish testOpen1 to start processing the other transitions 1252 mMainExecutor.execute(() -> { 1253 mDefaultHandler.finishOne(); 1254 }); 1255 mMainExecutor.flushAll(); 1256 1257 // Recents transition SHOULD start, and merge the open transition, which should NOT start. 1258 verify(observer).onTransitionFinished(eq(transitOpen1), eq(false) /* aborted */); 1259 verify(observer).onTransitionReady(eq(transitRecents), any(), any(), any()); 1260 verify(observer).onTransitionStarting(eq(transitRecents)); 1261 verify(observer).onTransitionReady(eq(transitOpen2), any(), any(), any()); 1262 verify(observer).onTransitionMerged(eq(transitOpen2), eq(transitRecents)); 1263 // verify(observer).onTransitionFinished(eq(transitOpen2), eq(true) /* aborted */); 1264 1265 // Go to sleep 1266 final IBinder transitSleep = new Binder("transitSleep"); 1267 final TransitionInfo infoSleep = new TransitionInfoBuilder(TRANSIT_SLEEP).build(); 1268 mMainExecutor.execute(() -> { 1269 transitions.requestStartTransition(transitSleep, new TransitionRequestInfo( 1270 TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1271 onTransitionReady(transitions, transitSleep, infoSleep); 1272 }); 1273 mMainExecutor.flushAll(); 1274 1275 // Recents transition should finish itself when it sees the sleep transition coming. 1276 verify(observer).onTransitionFinished(eq(transitRecents), eq(false)); 1277 verify(observer).onTransitionFinished(eq(transitSleep), eq(false)); 1278 } 1279 onTransitionReady(Transitions transitions, IBinder token, TransitionInfo info)1280 private void onTransitionReady(Transitions transitions, IBinder token, TransitionInfo info) { 1281 transitions.onTransitionReady(token, info, new StubTransaction(), 1282 new StubTransaction()); 1283 } 1284 1285 @Test testEmptyTransition_withKeyguardGoingAway_plays()1286 public void testEmptyTransition_withKeyguardGoingAway_plays() { 1287 Transitions transitions = createTestTransitions(); 1288 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1289 1290 IBinder transitToken = new Binder(); 1291 transitions.requestStartTransition(transitToken, 1292 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1293 1294 // Make a no-op transition 1295 TransitionInfo info = new TransitionInfoBuilder( 1296 TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, true /* noOp */).build(); 1297 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1298 new StubTransaction()); 1299 1300 // If keyguard-going-away flag set, then it shouldn't be aborted. 1301 assertEquals(1, mDefaultHandler.activeCount()); 1302 } 1303 1304 @Test testSleepTransition_withKeyguardGoingAway_plays()1305 public void testSleepTransition_withKeyguardGoingAway_plays(){ 1306 Transitions transitions = createTestTransitions(); 1307 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1308 1309 IBinder transitToken = new Binder(); 1310 transitions.requestStartTransition(transitToken, 1311 new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1312 1313 // Make a no-op transition 1314 TransitionInfo info = new TransitionInfoBuilder( 1315 TRANSIT_SLEEP, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, true /* noOp */).build(); 1316 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1317 new StubTransaction()); 1318 1319 // If keyguard-going-away flag set, then it shouldn't be aborted. 1320 assertEquals(1, mDefaultHandler.activeCount()); 1321 } 1322 1323 @Test testSleepTransition_withChanges_plays()1324 public void testSleepTransition_withChanges_plays(){ 1325 Transitions transitions = createTestTransitions(); 1326 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1327 1328 IBinder transitToken = new Binder(); 1329 transitions.requestStartTransition(transitToken, 1330 new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1331 1332 // Make a transition with some changes 1333 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_SLEEP) 1334 .addChange(TRANSIT_OPEN).build(); 1335 info.setTrack(0); 1336 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1337 new StubTransaction()); 1338 1339 // If there is an actual change, then it shouldn't be aborted. 1340 assertEquals(1, mDefaultHandler.activeCount()); 1341 } 1342 1343 1344 @Test testSleepTransition_empty_SyncBySleepHandler()1345 public void testSleepTransition_empty_SyncBySleepHandler() { 1346 Transitions transitions = createTestTransitions(); 1347 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1348 1349 IBinder transitToken = new Binder(); 1350 transitions.requestStartTransition(transitToken, 1351 new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1352 1353 // Make a no-op transition 1354 TransitionInfo info = new TransitionInfoBuilder( 1355 TRANSIT_SLEEP, 0x0, true /* noOp */).build(); 1356 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1357 new StubTransaction()); 1358 1359 // If there is nothing to actually play, it should not be offered to handlers. 1360 assertEquals(0, mDefaultHandler.activeCount()); 1361 } 1362 1363 @Test testMultipleTracks()1364 public void testMultipleTracks() { 1365 Transitions transitions = createTestTransitions(); 1366 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1367 TestTransitionHandler alwaysMergeHandler = new TestTransitionHandler(); 1368 alwaysMergeHandler.setSimulateMerge(true); 1369 1370 final boolean[] becameIdle = new boolean[]{false}; 1371 1372 final WindowContainerTransaction emptyWCT = new WindowContainerTransaction(); 1373 final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class); 1374 1375 // Make this always merge so we can ensure that it does NOT get a merge-attempt for a 1376 // different track. 1377 IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, alwaysMergeHandler); 1378 // start tracking idle 1379 transitions.runOnIdle(() -> becameIdle[0] = true); 1380 1381 IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1382 IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler); 1383 1384 TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN) 1385 .addChange(TRANSIT_OPEN).build(); 1386 infoA.setTrack(0); 1387 TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN) 1388 .addChange(TRANSIT_OPEN).build(); 1389 infoB.setTrack(1); 1390 TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE) 1391 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1392 infoC.setTrack(1); 1393 1394 transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT); 1395 assertEquals(1, alwaysMergeHandler.activeCount()); 1396 transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT); 1397 // should now be running in parallel 1398 assertEquals(1, mDefaultHandler.activeCount()); 1399 assertEquals(1, alwaysMergeHandler.activeCount()); 1400 // make sure we didn't try to merge into a different track. 1401 assertEquals(0, alwaysMergeHandler.mergeCount()); 1402 1403 // This should be queued-up since it is on track 1 (same as B) 1404 transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT); 1405 assertEquals(1, mDefaultHandler.activeCount()); 1406 assertEquals(1, alwaysMergeHandler.activeCount()); 1407 1408 // Now finish B and make sure C starts 1409 mDefaultHandler.finishOne(); 1410 mMainExecutor.flushAll(); 1411 1412 // Now C and A running in parallel 1413 assertEquals(1, mDefaultHandler.activeCount()); 1414 assertEquals(1, alwaysMergeHandler.activeCount()); 1415 assertEquals(0, alwaysMergeHandler.mergeCount()); 1416 1417 // Finish A 1418 alwaysMergeHandler.finishOne(); 1419 mMainExecutor.flushAll(); 1420 1421 // C still running 1422 assertEquals(0, alwaysMergeHandler.activeCount()); 1423 assertEquals(1, mDefaultHandler.activeCount()); 1424 assertFalse(becameIdle[0]); 1425 1426 mDefaultHandler.finishOne(); 1427 mMainExecutor.flushAll(); 1428 1429 assertEquals(0, mDefaultHandler.activeCount()); 1430 assertTrue(becameIdle[0]); 1431 } 1432 1433 @Test testSyncMultipleTracks()1434 public void testSyncMultipleTracks() { 1435 Transitions transitions = createTestTransitions(); 1436 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1437 TestTransitionHandler secondHandler = new TestTransitionHandler(); 1438 1439 // Disable the forced early-sync-finish so that we can test the ordering mechanics. 1440 transitions.setDisableForceSyncForTest(true); 1441 mDefaultHandler.mFinishOnSync = false; 1442 secondHandler.mFinishOnSync = false; 1443 1444 final WindowContainerTransaction emptyWCT = new WindowContainerTransaction(); 1445 final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class); 1446 1447 // Make this always merge so we can ensure that it does NOT get a merge-attempt for a 1448 // different track. 1449 IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1450 IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler); 1451 IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler); 1452 IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler); 1453 IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler); 1454 IBinder transitE = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1455 1456 TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN) 1457 .addChange(TRANSIT_OPEN).build(); 1458 infoA.setTrack(0); 1459 TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN) 1460 .addChange(TRANSIT_OPEN).build(); 1461 infoB.setTrack(1); 1462 TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE) 1463 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1464 infoC.setTrack(1); 1465 TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE) 1466 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1467 infoSync.setTrack(0); 1468 infoSync.setFlags(FLAG_SYNC); 1469 TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN) 1470 .addChange(TRANSIT_OPEN).build(); 1471 infoD.setTrack(1); 1472 TransitionInfo infoE = new TransitionInfoBuilder(TRANSIT_OPEN) 1473 .addChange(TRANSIT_OPEN).build(); 1474 infoE.setTrack(0); 1475 1476 // Start A B and C where A is track 0, B and C are track 1 (C should be queued) 1477 transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT); 1478 transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT); 1479 transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT); 1480 // should now be running in parallel (with one queued) 1481 assertEquals(1, mDefaultHandler.activeCount()); 1482 assertEquals(1, secondHandler.activeCount()); 1483 1484 // Make the sync ready and the following (D, E) ready. 1485 transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT); 1486 transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT); 1487 transitions.onTransitionReady(transitE, infoE, mockSCT, mockSCT); 1488 1489 // nothing should have happened yet since the sync is queued and blocking everything. 1490 assertEquals(1, mDefaultHandler.activeCount()); 1491 assertEquals(1, secondHandler.activeCount()); 1492 1493 // Finish A (which is track 0 like the sync). 1494 mDefaultHandler.finishOne(); 1495 mMainExecutor.flushAll(); 1496 1497 // Even though the sync is on track 0 and track 0 became idle, it should NOT be started yet 1498 // because it must wait for everything. Additionally, D/E shouldn't start yet either. 1499 assertEquals(0, mDefaultHandler.activeCount()); 1500 assertEquals(1, secondHandler.activeCount()); 1501 1502 // Now finish B and C -- this should then allow the sync to start and D to run (in parallel) 1503 secondHandler.finishOne(); 1504 secondHandler.finishOne(); 1505 mMainExecutor.flushAll(); 1506 1507 // Now the sync and D (on track 1) should be running 1508 assertEquals(1, mDefaultHandler.activeCount()); 1509 assertEquals(1, secondHandler.activeCount()); 1510 1511 // finish the sync. track 0 still has E 1512 mDefaultHandler.finishOne(); 1513 mMainExecutor.flushAll(); 1514 assertEquals(1, mDefaultHandler.activeCount()); 1515 1516 mDefaultHandler.finishOne(); 1517 secondHandler.finishOne(); 1518 mMainExecutor.flushAll(); 1519 1520 assertEquals(0, mDefaultHandler.activeCount()); 1521 assertEquals(0, secondHandler.activeCount()); 1522 } 1523 1524 @Test testForceSyncTracks()1525 public void testForceSyncTracks() { 1526 Transitions transitions = createTestTransitions(); 1527 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1528 TestTransitionHandler secondHandler = new TestTransitionHandler(); 1529 1530 final WindowContainerTransaction emptyWCT = new WindowContainerTransaction(); 1531 final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class); 1532 1533 // Make this always merge so we can ensure that it does NOT get a merge-attempt for a 1534 // different track. 1535 IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1536 IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1537 IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler); 1538 IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler); 1539 IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler); 1540 1541 TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN) 1542 .addChange(TRANSIT_OPEN).build(); 1543 infoA.setTrack(0); 1544 TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN) 1545 .addChange(TRANSIT_OPEN).build(); 1546 infoB.setTrack(0); 1547 TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE) 1548 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1549 infoC.setTrack(1); 1550 TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN) 1551 .addChange(TRANSIT_OPEN).build(); 1552 infoD.setTrack(1); 1553 TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE) 1554 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1555 infoSync.setTrack(0); 1556 infoSync.setFlags(FLAG_SYNC); 1557 1558 transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT); 1559 transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT); 1560 transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT); 1561 transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT); 1562 // should now be running in parallel (with one queued in each) 1563 assertEquals(1, mDefaultHandler.activeCount()); 1564 assertEquals(1, secondHandler.activeCount()); 1565 1566 // Make the sync ready. 1567 transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT); 1568 mMainExecutor.flushAll(); 1569 1570 // Everything should be forced-finish now except the sync 1571 assertEquals(1, mDefaultHandler.activeCount()); 1572 assertEquals(0, secondHandler.activeCount()); 1573 1574 mDefaultHandler.finishOne(); 1575 mMainExecutor.flushAll(); 1576 1577 assertEquals(0, mDefaultHandler.activeCount()); 1578 } 1579 1580 @Test testCloseTransitAnimationWhenClosingChangesExists()1581 public void testCloseTransitAnimationWhenClosingChangesExists() { 1582 Transitions transitions = createTestTransitions(); 1583 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1584 transitions.registerObserver(observer); 1585 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1586 final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false, 1587 Transitions.TAG); 1588 spyOn(transitionAnimation); 1589 1590 // Creating a transition by the app hooking the back key event to start the 1591 // previous activity with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 1592 // flags in order to clear the top activity and bring the exist previous activity to front. 1593 // Expects the activity transition should playing the close animation instead the initiated 1594 // open animation made by startActivity. 1595 IBinder transitToken = new Binder(); 1596 transitions.requestStartTransition(transitToken, 1597 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1598 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 1599 .addChange(TRANSIT_CLOSE).addChange(TRANSIT_TO_FRONT).build(); 1600 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1601 new StubTransaction()); 1602 1603 final int type = getTransitionTypeFromInfo(info); 1604 assertEquals(TRANSIT_CLOSE, type); 1605 1606 TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(0), 0, 1607 transitionAnimation, false); 1608 verify(transitionAnimation).loadDefaultAnimationAttr( 1609 eq(R.styleable.WindowAnimation_activityCloseExitAnimation), anyBoolean()); 1610 1611 TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(1), 0, 1612 transitionAnimation, false); 1613 verify(transitionAnimation).loadDefaultAnimationAttr( 1614 eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean()); 1615 } 1616 1617 class ChangeBuilder { 1618 final TransitionInfo.Change mChange; 1619 ChangeBuilder(@indowManager.TransitionType int mode)1620 ChangeBuilder(@WindowManager.TransitionType int mode) { 1621 mChange = new TransitionInfo.Change(null /* token */, createMockSurface(true)); 1622 mChange.setMode(mode); 1623 } 1624 setFlags(@ransitionInfo.ChangeFlags int flags)1625 ChangeBuilder setFlags(@TransitionInfo.ChangeFlags int flags) { 1626 mChange.setFlags(flags); 1627 return this; 1628 } 1629 setTask(RunningTaskInfo taskInfo)1630 ChangeBuilder setTask(RunningTaskInfo taskInfo) { 1631 mChange.setTaskInfo(taskInfo); 1632 return this; 1633 } 1634 setRotate(int anim)1635 ChangeBuilder setRotate(int anim) { 1636 return setRotate(Surface.ROTATION_90, anim); 1637 } 1638 setRotate()1639 ChangeBuilder setRotate() { 1640 return setRotate(ROTATION_ANIMATION_UNSPECIFIED); 1641 } 1642 setRotate(@urface.Rotation int target, int anim)1643 ChangeBuilder setRotate(@Surface.Rotation int target, int anim) { 1644 mChange.setRotation(Surface.ROTATION_0, target); 1645 mChange.setRotationAnimation(anim); 1646 return this; 1647 } 1648 build()1649 TransitionInfo.Change build() { 1650 return mChange; 1651 } 1652 } 1653 1654 class TestTransitionHandler implements Transitions.TransitionHandler { 1655 ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> mFinishes = 1656 new ArrayList<>(); 1657 final ArrayList<IBinder> mMerged = new ArrayList<>(); 1658 boolean mSimulateMerge = false; 1659 boolean mFinishOnSync = true; 1660 final ArraySet<IBinder> mShouldMerge = new ArraySet<>(); 1661 1662 @Override startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback)1663 public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 1664 @NonNull SurfaceControl.Transaction startTransaction, 1665 @NonNull SurfaceControl.Transaction finishTransaction, 1666 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1667 mFinishes.add(new Pair<>(transition, finishCallback)); 1668 return true; 1669 } 1670 1671 @Override mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback)1672 public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 1673 @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, 1674 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1675 if (mFinishOnSync && info.getType() == TRANSIT_SLEEP) { 1676 for (int i = 0; i < mFinishes.size(); ++i) { 1677 if (mFinishes.get(i).first != mergeTarget) continue; 1678 mFinishes.remove(i).second.onTransitionFinished(null); 1679 return; 1680 } 1681 } 1682 if (!(mSimulateMerge || mShouldMerge.contains(transition))) return; 1683 mMerged.add(transition); 1684 finishCallback.onTransitionFinished(null /* wct */); 1685 } 1686 1687 @Nullable 1688 @Override handleRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request)1689 public WindowContainerTransaction handleRequest(@NonNull IBinder transition, 1690 @NonNull TransitionRequestInfo request) { 1691 return null; 1692 } 1693 setSimulateMerge(boolean sim)1694 void setSimulateMerge(boolean sim) { 1695 mSimulateMerge = sim; 1696 } 1697 setShouldMerge(IBinder toMerge)1698 void setShouldMerge(IBinder toMerge) { 1699 mShouldMerge.add(toMerge); 1700 } 1701 finishAll()1702 void finishAll() { 1703 final ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> finishes = 1704 mFinishes; 1705 mFinishes = new ArrayList<>(); 1706 for (int i = finishes.size() - 1; i >= 0; --i) { 1707 finishes.get(i).second.onTransitionFinished(null /* wct */); 1708 } 1709 mShouldMerge.clear(); 1710 } 1711 finishOne()1712 void finishOne() { 1713 Pair<IBinder, Transitions.TransitionFinishCallback> fin = mFinishes.remove(0); 1714 mMerged.clear(); 1715 fin.second.onTransitionFinished(null /* wct */); 1716 } 1717 activeCount()1718 int activeCount() { 1719 return mFinishes.size(); 1720 } 1721 mergeCount()1722 int mergeCount() { 1723 return mMerged.size(); 1724 } 1725 } 1726 requestStartTransition(Transitions transitions, IBinder token)1727 private static void requestStartTransition(Transitions transitions, IBinder token) { 1728 transitions.requestStartTransition(token, 1729 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1730 } 1731 onTransitionReady(Transitions transitions, IBinder token)1732 private static void onTransitionReady(Transitions transitions, IBinder token) { 1733 transitions.onTransitionReady(token, createTransitionInfo(), 1734 new StubTransaction(), new StubTransaction()); 1735 } 1736 createTransitionInfo()1737 private static TransitionInfo createTransitionInfo() { 1738 return new TransitionInfoBuilder(TRANSIT_OPEN) 1739 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1740 } 1741 createMockSurface(boolean valid)1742 private static SurfaceControl createMockSurface(boolean valid) { 1743 SurfaceControl sc = mock(SurfaceControl.class); 1744 doReturn(valid).when(sc).isValid(); 1745 return sc; 1746 } 1747 createTaskInfo(int taskId, int windowingMode, int activityType)1748 private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode, int activityType) { 1749 RunningTaskInfo taskInfo = new RunningTaskInfo(); 1750 taskInfo.taskId = taskId; 1751 taskInfo.topActivityType = activityType; 1752 taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); 1753 taskInfo.configuration.windowConfiguration.setActivityType(activityType); 1754 taskInfo.token = mock(WindowContainerToken.class); 1755 return taskInfo; 1756 } 1757 createTaskInfo(int taskId)1758 private static RunningTaskInfo createTaskInfo(int taskId) { 1759 return createTaskInfo(taskId, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 1760 } 1761 createTestDisplayController()1762 private DisplayController createTestDisplayController() { 1763 DisplayLayout displayLayout = mock(DisplayLayout.class); 1764 doReturn(Surface.ROTATION_180).when(displayLayout).getUpsideDownRotation(); 1765 // By default we ignore nav bar in deciding if a seamless rotation is allowed. 1766 doReturn(true).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving(); 1767 1768 DisplayController out = mock(DisplayController.class); 1769 doReturn(displayLayout).when(out).getDisplayLayout(DEFAULT_DISPLAY); 1770 return out; 1771 } 1772 createTestTransitions()1773 private Transitions createTestTransitions() { 1774 ShellInit shellInit = new ShellInit(mMainExecutor); 1775 final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), 1776 mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor, 1777 mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class)); 1778 shellInit.init(); 1779 return t; 1780 } 1781 } 1782