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