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