1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
25 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
26 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
27 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
28 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
29 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
30 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
31 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
32 import static android.view.WindowManager.TRANSIT_CHANGE;
33 import static android.view.WindowManager.TRANSIT_CLOSE;
34 import static android.view.WindowManager.TRANSIT_OPEN;
35 import static android.view.WindowManager.TRANSIT_TO_BACK;
36 import static android.window.TransitionInfo.FLAG_FILLS_TASK;
37 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
38 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
39 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
40 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
41 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
42 import static android.window.TransitionInfo.FLAG_SYNC;
43 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
44 import static android.window.TransitionInfo.isIndependent;
45 
46 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
48 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
49 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
50 import static com.android.server.wm.WindowContainer.POSITION_TOP;
51 
52 import static org.junit.Assert.assertEquals;
53 import static org.junit.Assert.assertFalse;
54 import static org.junit.Assert.assertNotEquals;
55 import static org.junit.Assert.assertNotNull;
56 import static org.junit.Assert.assertNull;
57 import static org.junit.Assert.assertTrue;
58 import static org.junit.Assume.assumeFalse;
59 import static org.junit.Assume.assumeTrue;
60 import static org.mockito.ArgumentMatchers.any;
61 import static org.mockito.ArgumentMatchers.anyBoolean;
62 import static org.mockito.ArgumentMatchers.anyInt;
63 import static org.mockito.ArgumentMatchers.eq;
64 import static org.mockito.Mockito.doAnswer;
65 import static org.mockito.Mockito.mock;
66 import static org.mockito.Mockito.spy;
67 import static org.mockito.Mockito.times;
68 import static org.mockito.Mockito.verify;
69 
70 import android.app.ActivityManager;
71 import android.content.res.Configuration;
72 import android.graphics.Color;
73 import android.graphics.Point;
74 import android.graphics.Rect;
75 import android.os.IBinder;
76 import android.platform.test.annotations.Presubmit;
77 import android.util.ArrayMap;
78 import android.util.ArraySet;
79 import android.view.SurfaceControl;
80 import android.view.WindowManager;
81 import android.window.IDisplayAreaOrganizer;
82 import android.window.IRemoteTransition;
83 import android.window.ITaskFragmentOrganizer;
84 import android.window.ITaskOrganizer;
85 import android.window.ITransitionPlayer;
86 import android.window.RemoteTransition;
87 import android.window.TaskFragmentOrganizer;
88 import android.window.TransitionInfo;
89 
90 import androidx.annotation.NonNull;
91 import androidx.test.filters.SmallTest;
92 
93 import com.android.internal.graphics.ColorUtils;
94 
95 import org.junit.Test;
96 import org.junit.runner.RunWith;
97 import org.mockito.ArgumentCaptor;
98 
99 import java.util.ArrayList;
100 import java.util.Objects;
101 import java.util.concurrent.CountDownLatch;
102 import java.util.concurrent.TimeUnit;
103 import java.util.function.Consumer;
104 import java.util.function.Function;
105 
106 /**
107  * Build/Install/Run:
108  *  atest WmTests:TransitionTests
109  */
110 @SmallTest
111 @Presubmit
112 @RunWith(WindowTestRunner.class)
113 public class TransitionTests extends WindowTestsBase {
114     final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class);
115     private BLASTSyncEngine mSyncEngine;
116 
createTestTransition(int transitType, TransitionController controller)117     private Transition createTestTransition(int transitType, TransitionController controller) {
118         return new Transition(transitType, 0 /* flags */, controller, controller.mSyncEngine);
119     }
120 
createTestTransition(int transitType)121     private Transition createTestTransition(int transitType) {
122         final TransitionController controller = new TestTransitionController(
123                 mock(ActivityTaskManagerService.class));
124 
125         mSyncEngine = createTestBLASTSyncEngine();
126         controller.setSyncEngine(mSyncEngine);
127         final Transition out = createTestTransition(transitType, controller);
128         out.startCollecting(0 /* timeoutMs */);
129         return out;
130     }
131 
132     @Test
testCreateInfo_NewTask()133     public void testCreateInfo_NewTask() {
134         final Transition transition = createTestTransition(TRANSIT_OPEN);
135         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
136         ArraySet<WindowContainer> participants = transition.mParticipants;
137 
138         final Task newTask = createTask(mDisplayContent);
139         final Task oldTask = createTask(mDisplayContent);
140         final ActivityRecord closing = createActivityRecord(oldTask);
141         final ActivityRecord opening = createActivityRecord(newTask);
142         // Start states.
143         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
144         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */));
145         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
146         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
147         fillChangeMap(changes, newTask);
148         // End states.
149         closing.setVisibleRequested(false);
150         opening.setVisibleRequested(true);
151 
152         final int transit = transition.mType;
153         int flags = 0;
154 
155         // Check basic both tasks participating
156         participants.add(oldTask);
157         participants.add(newTask);
158         ArrayList<Transition.ChangeInfo> targets =
159                 Transition.calculateTargets(participants, changes);
160         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
161         assertEquals(2, info.getChanges().size());
162         assertEquals(transit, info.getType());
163 
164         // Check that children are pruned
165         participants.add(opening);
166         participants.add(closing);
167         targets = Transition.calculateTargets(participants, changes);
168         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
169         assertEquals(2, info.getChanges().size());
170         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
171         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
172 
173         // Check combined prune and promote
174         participants.remove(newTask);
175         targets = Transition.calculateTargets(participants, changes);
176         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
177         assertEquals(2, info.getChanges().size());
178         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
179         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
180 
181         // Check multi promote
182         participants.remove(oldTask);
183         targets = Transition.calculateTargets(participants, changes);
184         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
185         assertEquals(2, info.getChanges().size());
186         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
187         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
188     }
189 
190     @Test
testCreateInfo_NestedTasks()191     public void testCreateInfo_NestedTasks() {
192         final Transition transition = createTestTransition(TRANSIT_OPEN);
193         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
194         ArraySet<WindowContainer> participants = transition.mParticipants;
195 
196         final Task newTask = createTask(mDisplayContent);
197         final Task newNestedTask = createTaskInRootTask(newTask, 0);
198         final Task newNestedTask2 = createTaskInRootTask(newTask, 0);
199         final Task oldTask = createTask(mDisplayContent);
200         final ActivityRecord closing = createActivityRecord(oldTask);
201         final ActivityRecord opening = createActivityRecord(newNestedTask);
202         final ActivityRecord opening2 = createActivityRecord(newNestedTask2);
203         // Start states.
204         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
205         changes.put(newNestedTask,
206                 new Transition.ChangeInfo(newNestedTask, false /* vis */, true /* exChg */));
207         changes.put(newNestedTask2,
208                 new Transition.ChangeInfo(newNestedTask2, false /* vis */, true /* exChg */));
209         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */));
210         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
211         changes.put(opening2,
212                 new Transition.ChangeInfo(opening2, false /* vis */, true /* exChg */));
213         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
214         fillChangeMap(changes, newTask);
215         // End states.
216         closing.setVisibleRequested(false);
217         opening.setVisibleRequested(true);
218         opening2.setVisibleRequested(true);
219 
220         final int transit = transition.mType;
221         int flags = 0;
222 
223         // Check full promotion from leaf
224         participants.add(oldTask);
225         participants.add(opening);
226         participants.add(opening2);
227         ArrayList<Transition.ChangeInfo> targets =
228                 Transition.calculateTargets(participants, changes);
229         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
230         assertEquals(2, info.getChanges().size());
231         assertEquals(transit, info.getType());
232         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
233         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
234 
235         // Check that unchanging but visible descendant of sibling prevents promotion
236         participants.remove(opening2);
237         targets = Transition.calculateTargets(participants, changes);
238         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
239         assertEquals(2, info.getChanges().size());
240         assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken()));
241         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
242     }
243 
244     @Test
testCreateInfo_DisplayArea()245     public void testCreateInfo_DisplayArea() {
246         assumeTrue(mDisplayContent.mTransitionController.useShellTransitionsRotation());
247 
248         final Transition transition = createTestTransition(TRANSIT_OPEN);
249         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
250         ArraySet<WindowContainer> participants = transition.mParticipants;
251         final Task showTask = createTask(mDisplayContent);
252         final Task showNestedTask = createTaskInRootTask(showTask, 0);
253         final Task showTask2 = createTask(mDisplayContent);
254         final DisplayArea tda = showTask.getDisplayArea();
255         final ActivityRecord showing = createActivityRecord(showNestedTask);
256         final ActivityRecord showing2 = createActivityRecord(showTask2);
257         // Start states.
258         changes.put(showTask,
259                 new Transition.ChangeInfo(showTask, false /* vis */, true /* exChg */));
260         changes.put(showNestedTask,
261                 new Transition.ChangeInfo(showNestedTask, false /* vis */, true /* exChg */));
262         changes.put(showTask2,
263                 new Transition.ChangeInfo(showTask2, false /* vis */, true /* exChg */));
264         changes.put(tda, new Transition.ChangeInfo(tda, false /* vis */, true /* exChg */));
265         changes.put(showing, new Transition.ChangeInfo(showing, false /* vis */, true /* exChg */));
266         changes.put(showing2,
267                 new Transition.ChangeInfo(showing2, false /* vis */, true /* exChg */));
268         fillChangeMap(changes, tda);
269 
270         // End states.
271         showing.setVisibleRequested(true);
272         showing2.setVisibleRequested(true);
273 
274         final int transit = transition.mType;
275         int flags = 0;
276 
277         // Check promotion to DisplayArea
278         participants.add(showing);
279         participants.add(showing2);
280         ArrayList<Transition.ChangeInfo> targets =
281                 Transition.calculateTargets(participants, changes);
282         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
283         assertEquals(1, info.getChanges().size());
284         assertEquals(transit, info.getType());
285         assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
286 
287         // Check that organized tasks get reported even if not top
288         makeTaskOrganized(showTask);
289         targets = Transition.calculateTargets(participants, changes);
290         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
291         assertEquals(2, info.getChanges().size());
292         assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
293         assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken()));
294         // Even if DisplayArea explicitly participating
295         participants.add(tda);
296         targets = Transition.calculateTargets(participants, changes);
297         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
298         assertEquals(2, info.getChanges().size());
299     }
300 
301     @Test
testCreateInfo_existenceChange()302     public void testCreateInfo_existenceChange() {
303         final Transition transition = createTestTransition(TRANSIT_OPEN);
304 
305         final Task openTask = createTask(mDisplayContent);
306         final ActivityRecord opening = createActivityRecord(openTask);
307         opening.setVisibleRequested(false); // starts invisible
308         final Task closeTask = createTask(mDisplayContent);
309         final ActivityRecord closing = createActivityRecord(closeTask);
310         closing.setVisibleRequested(true); // starts visible
311 
312         transition.collectExistenceChange(openTask);
313         transition.collect(opening);
314         transition.collect(closing);
315         opening.setVisibleRequested(true);
316         closing.setVisibleRequested(false);
317 
318         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
319                 transition.mParticipants, transition.mChanges);
320         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
321         assertEquals(2, info.getChanges().size());
322         // There was an existence change on open, so it should be OPEN rather than SHOW
323         assertEquals(TRANSIT_OPEN,
324                 info.getChange(openTask.mRemoteToken.toWindowContainerToken()).getMode());
325         // No exestence change on closing, so HIDE rather than CLOSE
326         assertEquals(TRANSIT_TO_BACK,
327                 info.getChange(closeTask.mRemoteToken.toWindowContainerToken()).getMode());
328     }
329 
330     @Test
testCreateInfo_ordering()331     public void testCreateInfo_ordering() {
332         final Transition transition = createTestTransition(TRANSIT_OPEN);
333         // pick some number with a high enough chance of being out-of-order when added to set.
334         final int taskCount = 6;
335 
336         final Task[] tasks = new Task[taskCount];
337         for (int i = 0; i < taskCount; ++i) {
338             // Each add goes on top, so at the end of this, task[9] should be on top
339             tasks[i] = createTask(mDisplayContent,
340                     WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
341             final ActivityRecord act = createActivityRecord(tasks[i]);
342             // alternate so that the transition doesn't get promoted to the display area
343             act.setVisibleRequested((i % 2) == 0); // starts invisible
344         }
345 
346         // doesn't matter which order collected since participants is a set
347         for (int i = 0; i < taskCount; ++i) {
348             transition.collectExistenceChange(tasks[i]);
349             final ActivityRecord act = tasks[i].getTopMostActivity();
350             transition.collect(act);
351             tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0);
352         }
353 
354         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
355                 transition.mParticipants, transition.mChanges);
356         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
357         assertEquals(taskCount, info.getChanges().size());
358         // verify order is top-to-bottem
359         for (int i = 0; i < taskCount; ++i) {
360             assertEquals(tasks[taskCount - i - 1].mRemoteToken.toWindowContainerToken(),
361                     info.getChanges().get(i).getContainer());
362         }
363     }
364 
365     @Test
testCreateInfo_wallpaper()366     public void testCreateInfo_wallpaper() {
367         final Transition transition = createTestTransition(TRANSIT_OPEN);
368         // pick some number with a high enough chance of being out-of-order when added to set.
369         final int taskCount = 4;
370         final int showWallpaperTask = 2;
371 
372         final Task[] tasks = new Task[taskCount];
373         for (int i = 0; i < taskCount; ++i) {
374             // Each add goes on top, so at the end of this, task[9] should be on top
375             tasks[i] = createTask(mDisplayContent,
376                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
377             final ActivityRecord act = createActivityRecord(tasks[i]);
378             // alternate so that the transition doesn't get promoted to the display area
379             act.setVisibleRequested((i % 2) == 0); // starts invisible
380             if (i == showWallpaperTask) {
381                 doReturn(true).when(act).showWallpaper();
382             }
383         }
384 
385         final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm,
386                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */));
387         final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
388                 "wallpaperWindow");
389         wallpaperWindowToken.setVisibleRequested(false);
390         transition.collect(wallpaperWindowToken);
391         wallpaperWindowToken.setVisibleRequested(true);
392         wallpaperWindow.mHasSurface = true;
393 
394         // doesn't matter which order collected since participants is a set
395         for (int i = 0; i < taskCount; ++i) {
396             transition.collectExistenceChange(tasks[i]);
397             final ActivityRecord act = tasks[i].getTopMostActivity();
398             transition.collect(act);
399             tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0);
400         }
401 
402         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
403                 transition.mParticipants, transition.mChanges);
404         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
405         // verify that wallpaper is at bottom
406         assertEquals(taskCount + 1, info.getChanges().size());
407         // The wallpaper is not organized, so it won't have a token; however, it will be marked
408         // as IS_WALLPAPER
409         assertEquals(FLAG_IS_WALLPAPER,
410                 info.getChanges().get(info.getChanges().size() - 1).getFlags());
411         assertEquals(FLAG_SHOW_WALLPAPER, info.getChange(
412                 tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags());
413     }
414 
415     @Test
testCreateInfo_PromoteSimilarClose()416     public void testCreateInfo_PromoteSimilarClose() {
417         final Transition transition = createTestTransition(TRANSIT_CLOSE);
418         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
419         ArraySet<WindowContainer> participants = transition.mParticipants;
420 
421         final Task topTask = createTask(mDisplayContent);
422         final Task belowTask = createTask(mDisplayContent);
423         final ActivityRecord showing = createActivityRecord(belowTask);
424         final ActivityRecord hiding = createActivityRecord(topTask);
425         final ActivityRecord closing = createActivityRecord(topTask);
426         // Start states.
427         changes.put(topTask, new Transition.ChangeInfo(topTask, true /* vis */, false /* exChg */));
428         changes.put(belowTask,
429                 new Transition.ChangeInfo(belowTask, false /* vis */, false /* exChg */));
430         changes.put(showing,
431                 new Transition.ChangeInfo(showing, false /* vis */, false /* exChg */));
432         changes.put(hiding, new Transition.ChangeInfo(hiding, true /* vis */, false /* exChg */));
433         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
434         fillChangeMap(changes, topTask);
435         // End states.
436         showing.setVisibleRequested(true);
437         closing.setVisibleRequested(false);
438         hiding.setVisibleRequested(false);
439 
440         participants.add(belowTask);
441         participants.add(hiding);
442         participants.add(closing);
443         ArrayList<Transition.ChangeInfo> targets =
444                 Transition.calculateTargets(participants, changes);
445         assertEquals(2, targets.size());
446         assertTrue(Transition.containsChangeFor(belowTask, targets));
447         assertTrue(Transition.containsChangeFor(topTask, targets));
448     }
449 
450     @Test
testCreateInfo_PromoteSimilarOpen()451     public void testCreateInfo_PromoteSimilarOpen() {
452         final Transition transition = createTestTransition(TRANSIT_OPEN);
453         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
454         ArraySet<WindowContainer> participants = transition.mParticipants;
455 
456         final Task topTask = createTask(mDisplayContent);
457         final Task belowTask = createTask(mDisplayContent);
458         final ActivityRecord showing = createActivityRecord(topTask);
459         final ActivityRecord opening = createActivityRecord(topTask);
460         final ActivityRecord closing = createActivityRecord(belowTask);
461         // Start states.
462         changes.put(topTask,
463                 new Transition.ChangeInfo(topTask, false /* vis */, false /* exChg */));
464         changes.put(belowTask,
465                 new Transition.ChangeInfo(belowTask, true /* vis */, false /* exChg */));
466         changes.put(showing,
467                 new Transition.ChangeInfo(showing, false /* vis */, false /* exChg */));
468         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
469         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
470         fillChangeMap(changes, topTask);
471         // End states.
472         showing.setVisibleRequested(true);
473         opening.setVisibleRequested(true);
474         closing.setVisibleRequested(false);
475 
476         participants.add(belowTask);
477         participants.add(showing);
478         participants.add(opening);
479         ArrayList<Transition.ChangeInfo> targets =
480                 Transition.calculateTargets(participants, changes);
481         assertEquals(2, targets.size());
482         assertTrue(Transition.containsChangeFor(belowTask, targets));
483         assertTrue(Transition.containsChangeFor(topTask, targets));
484     }
485 
486     @Test
testCreateInfo_NoAnimation()487     public void testCreateInfo_NoAnimation() {
488         final Transition transition = createTestTransition(TRANSIT_OPEN);
489         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
490         ArraySet<WindowContainer> participants = transition.mParticipants;
491 
492         final Task newTask = createTask(mDisplayContent);
493         final Task oldTask = createTask(mDisplayContent);
494         final ActivityRecord closing = createActivityRecord(oldTask);
495         final ActivityRecord opening = createActivityRecord(newTask);
496         // Start states.
497         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
498         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */));
499         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
500         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
501         transition.setNoAnimation(opening);
502         fillChangeMap(changes, newTask);
503         // End states.
504         closing.setVisibleRequested(false);
505         opening.setVisibleRequested(true);
506 
507         final int transit = transition.mType;
508         int flags = 0;
509 
510         // Check that no-animation flag is promoted
511         participants.add(oldTask);
512         participants.add(newTask);
513         participants.add(opening);
514         participants.add(closing);
515         ArrayList<Transition.ChangeInfo> targets =
516                 Transition.calculateTargets(participants, changes);
517         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
518         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
519         assertTrue(info.getChange(newTask.mRemoteToken.toWindowContainerToken())
520                 .hasFlags(TransitionInfo.FLAG_NO_ANIMATION));
521 
522         // Check that no-animation flag is NOT promoted if at-least on child *is* animated
523         final ActivityRecord opening2 = createActivityRecord(newTask);
524         changes.put(opening2,
525                 new Transition.ChangeInfo(opening2, false /* vis */, true /* exChg */));
526         participants.add(opening2);
527         targets = Transition.calculateTargets(participants, changes);
528         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
529         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
530         assertFalse(info.getChange(newTask.mRemoteToken.toWindowContainerToken())
531                 .hasFlags(TransitionInfo.FLAG_NO_ANIMATION));
532     }
533 
534     @Test
testCreateInfo_MultiDisplay()535     public void testCreateInfo_MultiDisplay() {
536         DisplayContent otherDisplay = createNewDisplay();
537         final Transition transition = createTestTransition(TRANSIT_OPEN);
538         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
539         ArraySet<WindowContainer> participants = transition.mParticipants;
540 
541         final Task display0Task = createTask(mDisplayContent);
542         final Task display1Task = createTask(otherDisplay);
543         // Start states.
544         changes.put(display0Task,
545                 new Transition.ChangeInfo(display0Task, false /* vis */, true /* exChg */));
546         changes.put(display1Task,
547                 new Transition.ChangeInfo(display1Task, false /* vis */, true /* exChg */));
548         fillChangeMap(changes, display0Task);
549         fillChangeMap(changes, display1Task);
550         // End states.
551         display0Task.setVisibleRequested(true);
552         display1Task.setVisibleRequested(true);
553 
554         final int transit = transition.mType;
555         int flags = 0;
556 
557         participants.add(display0Task);
558         participants.add(display1Task);
559         ArrayList<Transition.ChangeInfo> targets =
560                 Transition.calculateTargets(participants, changes);
561         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
562         assertEquals(2, info.getRootCount());
563         // Check that the changes are assigned to the correct display
564         assertEquals(mDisplayContent.getDisplayId(), info.getChange(
565                 display0Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId());
566         assertEquals(otherDisplay.getDisplayId(), info.getChange(
567                 display1Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId());
568         // Check that roots can be found by display and have the correct display
569         assertEquals(mDisplayContent.getDisplayId(),
570                 info.getRoot(info.findRootIndex(mDisplayContent.getDisplayId())).getDisplayId());
571         assertEquals(otherDisplay.getDisplayId(),
572                 info.getRoot(info.findRootIndex(otherDisplay.getDisplayId())).getDisplayId());
573     }
574 
575     @Test
testTargets_noIntermediatesToWallpaper()576     public void testTargets_noIntermediatesToWallpaper() {
577         final Transition transition = createTestTransition(TRANSIT_OPEN);
578 
579         final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
580                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
581         // Make DA organized so we can check that they don't get included.
582         WindowContainer parent = wallpaperWindowToken.getParent();
583         makeDisplayAreaOrganized(parent, mDisplayContent);
584         final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
585                 "wallpaperWindow");
586         wallpaperWindowToken.setVisibleRequested(false);
587         transition.collect(wallpaperWindowToken);
588         wallpaperWindowToken.setVisibleRequested(true);
589         wallpaperWindow.mHasSurface = true;
590         doReturn(true).when(mDisplayContent).isAttached();
591         transition.collect(mDisplayContent);
592         assertFalse("The change of non-interesting window container should be skipped",
593                 transition.mChanges.containsKey(mDisplayContent.getParent()));
594         mDisplayContent.getWindowConfiguration().setRotation(
595                 (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
596 
597         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
598                 transition.mParticipants, transition.mChanges);
599         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
600         // The wallpaper is not organized, so it won't have a token; however, it will be marked
601         // as IS_WALLPAPER
602         assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
603         // Make sure no intermediate display areas were pulled in between wallpaper and display.
604         assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(),
605                 info.getChanges().get(0).getParent());
606     }
607 
608     @Test
testRunningRemoteTransition()609     public void testRunningRemoteTransition() {
610         final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
611                 mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
612         final WindowProcessController playerProc = mSystemServicesTestRule.addProcess(
613                 "pkg.player", "proc.player", 5000 /* pid */, 5000 /* uid */);
614         testPlayer.mController.registerTransitionPlayer(testPlayer, playerProc);
615         doReturn(mock(IBinder.class)).when(playerProc.getThread()).asBinder();
616         final WindowProcessController delegateProc = mSystemServicesTestRule.addProcess(
617                 "pkg.delegate", "proc.delegate", 6000 /* pid */, 6000 /* uid */);
618         doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder();
619         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true)
620                 .setVisible(false).build();
621         final Task task = app.getTask();
622         task.setTaskOrganizer(mock(ITaskOrganizer.class), true /* skipTaskAppeared */);
623         app.setVisibleRequested(true);
624         final TransitionController controller = app.mTransitionController;
625         final Transition transition = controller.createTransition(TRANSIT_OPEN);
626         final RemoteTransition remoteTransition = new RemoteTransition(
627                 mock(IRemoteTransition.class));
628         remoteTransition.setAppThread(delegateProc.getThread());
629         transition.collect(app);
630         controller.requestStartTransition(transition, null /* startTask */, remoteTransition,
631                 null /* displayChange */);
632         testPlayer.startTransition();
633         app.onStartingWindowDrawn();
634         // The task appeared event should be deferred until transition ready.
635         assertFalse(task.taskAppearedReady());
636         testPlayer.onTransactionReady(app.getSyncTransaction());
637         assertTrue(task.taskAppearedReady());
638         assertTrue(playerProc.isRunningRemoteTransition());
639         assertTrue(delegateProc.isRunningRemoteTransition());
640         assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
641         assertTrue(app.isVisible());
642 
643         testPlayer.finish();
644         assertFalse(playerProc.isRunningRemoteTransition());
645         assertFalse(delegateProc.isRunningRemoteTransition());
646         assertFalse(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
647     }
648 
649     @Test
testOpenActivityInTheSameTaskWithDisplayChange()650     public void testOpenActivityInTheSameTaskWithDisplayChange() {
651         final ActivityRecord closing = createActivityRecord(mDisplayContent);
652         closing.setVisibleRequested(true);
653         final Task task = closing.getTask();
654         makeTaskOrganized(task);
655         final ActivityRecord opening = createActivityRecord(task);
656         opening.setVisibleRequested(false);
657         makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent);
658         final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent };
659         final Transition transition = createTestTransition(TRANSIT_OPEN);
660         for (WindowContainer<?> wc : wcs) {
661             transition.collect(wc);
662         }
663         closing.setVisibleRequested(false);
664         opening.setVisibleRequested(true);
665         final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1;
666         for (WindowContainer<?> wc : wcs) {
667             wc.getWindowConfiguration().setRotation(newRotation);
668         }
669 
670         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
671                 transition.mParticipants, transition.mChanges);
672         // Especially the activities must be in the targets.
673         for (WindowContainer<?> wc : wcs) {
674             assertTrue(Transition.containsChangeFor(wc, targets));
675         }
676     }
677 
678     @Test
testIndependent()679     public void testIndependent() {
680         final Transition transition = createTestTransition(TRANSIT_OPEN);
681         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
682         ArraySet<WindowContainer> participants = transition.mParticipants;
683 
684         final Task openTask = createTask(mDisplayContent);
685         final Task openInOpenTask = createTaskInRootTask(openTask, 0);
686         final ActivityRecord openInOpen = createActivityRecord(openInOpenTask);
687 
688         final Task changeTask = createTask(mDisplayContent);
689         final Task changeInChangeTask = createTaskInRootTask(changeTask, 0);
690         final Task openInChangeTask = createTaskInRootTask(changeTask, 0);
691         final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
692         final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
693         // set organizer for everything so that they all get added to transition info
694         makeTaskOrganized(openTask, openInOpenTask, changeTask, changeInChangeTask,
695                 openInChangeTask);
696 
697         // Start states.
698         changes.put(openTask,
699                 new Transition.ChangeInfo(openTask, false /* vis */, true /* exChg */));
700         changes.put(changeTask,
701                 new Transition.ChangeInfo(changeTask, true /* vis */, false /* exChg */));
702         changes.put(openInOpenTask,
703                 new Transition.ChangeInfo(openInOpenTask, false /* vis */, true /* exChg */));
704         changes.put(openInChangeTask,
705                 new Transition.ChangeInfo(openInChangeTask, false /* vis */, true /* exChg */));
706         changes.put(changeInChangeTask,
707                 new Transition.ChangeInfo(changeInChangeTask, true /* vis */, false /* exChg */));
708         changes.put(openInOpen,
709                 new Transition.ChangeInfo(openInOpen, false /* vis */, true /* exChg */));
710         changes.put(openInChange,
711                 new Transition.ChangeInfo(openInChange, false /* vis */, true /* exChg */));
712         changes.put(changeInChange,
713                 new Transition.ChangeInfo(changeInChange, true /* vis */, false /* exChg */));
714         fillChangeMap(changes, openTask);
715         // End states.
716         changeInChange.setVisibleRequested(true);
717         openInOpen.setVisibleRequested(true);
718         openInChange.setVisibleRequested(true);
719         // Force the change-type changes to be "dirty" so they aren't skipped
720         changes.get(changeTask).mKnownConfigChanges = 1;
721         changes.get(changeInChangeTask).mKnownConfigChanges = 1;
722         changes.get(changeInChange).mKnownConfigChanges = 1;
723 
724         final int transit = transition.mType;
725         int flags = 0;
726 
727         // Check full promotion from leaf
728         participants.add(changeInChange);
729         participants.add(openInOpen);
730         participants.add(openInChange);
731         // Explicitly add changeTask (to test independence with parents)
732         participants.add(changeTask);
733         final ArrayList<Transition.ChangeInfo> targets =
734                 Transition.calculateTargets(participants, changes);
735         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
736         // Root changes should always be considered independent
737         assertTrue(isIndependent(
738                 info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info));
739         assertTrue(isIndependent(
740                 info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info));
741 
742         // Children of a open/close change are not independent
743         assertFalse(isIndependent(
744                 info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info));
745 
746         // Non-root changes are not independent
747         assertFalse(isIndependent(
748                 info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info));
749 
750         // open/close within a change are independent
751         assertTrue(isIndependent(
752                 info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
753     }
754 
755     @Test
testOpenOpaqueTask()756     public void testOpenOpaqueTask() {
757         final Transition transition = createTestTransition(TRANSIT_OPEN);
758         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
759         ArraySet<WindowContainer> participants = transition.mParticipants;
760 
761         final Task oldTask = createTask(mDisplayContent);
762         final Task newTask = createTask(mDisplayContent);
763 
764         final ActivityRecord closing = createActivityRecord(oldTask);
765         closing.setOccludesParent(true);
766         final ActivityRecord opening = createActivityRecord(newTask);
767         opening.setOccludesParent(true);
768         // Start states.
769         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
770         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
771         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
772         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
773         fillChangeMap(changes, newTask);
774         // End states.
775         closing.setVisibleRequested(false);
776         opening.setVisibleRequested(true);
777 
778         final int transit = transition.mType;
779         int flags = 0;
780 
781         // Check basic both tasks participating
782         participants.add(oldTask);
783         participants.add(newTask);
784         ArrayList<Transition.ChangeInfo> targets =
785                 Transition.calculateTargets(participants, changes);
786         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
787         assertEquals(2, info.getChanges().size());
788         assertEquals(transit, info.getType());
789 
790         assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
791         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
792     }
793 
794     @Test
testOpenTranslucentTask()795     public void testOpenTranslucentTask() {
796         final Transition transition = createTestTransition(TRANSIT_OPEN);
797         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
798         ArraySet<WindowContainer> participants = transition.mParticipants;
799 
800         final Task oldTask = createTask(mDisplayContent);
801         final Task newTask = createTask(mDisplayContent);
802 
803         final ActivityRecord closing = createActivityRecord(oldTask);
804         closing.setOccludesParent(true);
805         final ActivityRecord opening = createActivityRecord(newTask);
806         opening.setOccludesParent(false);
807         // Start states.
808         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
809         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
810         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
811         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
812         fillChangeMap(changes, newTask);
813         // End states.
814         closing.setVisibleRequested(false);
815         opening.setVisibleRequested(true);
816 
817         final int transit = transition.mType;
818         int flags = 0;
819 
820         // Check basic both tasks participating
821         participants.add(oldTask);
822         participants.add(newTask);
823         ArrayList<Transition.ChangeInfo> targets =
824                 Transition.calculateTargets(participants, changes);
825         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
826         assertEquals(2, info.getChanges().size());
827         assertEquals(transit, info.getType());
828 
829         assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
830         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
831     }
832 
833     @Test
testOpenOpaqueTaskFragment()834     public void testOpenOpaqueTaskFragment() {
835         final Transition transition = createTestTransition(TRANSIT_OPEN);
836         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
837         ArraySet<WindowContainer> participants = transition.mParticipants;
838 
839         final Task task = createTask(mDisplayContent);
840         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
841         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
842 
843         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
844         closing.setOccludesParent(true);
845         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
846         opening.setOccludesParent(true);
847         // Start states.
848         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
849                 false /* vis */, true /* exChg */));
850         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
851                 true /* vis */, false /* exChg */));
852         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
853         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
854         fillChangeMap(changes, openingTaskFragment);
855         // End states.
856         closing.setVisibleRequested(false);
857         opening.setVisibleRequested(true);
858 
859         final int transit = transition.mType;
860         int flags = 0;
861 
862         // Check basic both tasks participating
863         participants.add(closingTaskFragment);
864         participants.add(openingTaskFragment);
865         ArrayList<Transition.ChangeInfo> targets =
866                 Transition.calculateTargets(participants, changes);
867         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
868         assertEquals(2, info.getChanges().size());
869         assertEquals(transit, info.getType());
870 
871         assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
872         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
873     }
874 
875     @Test
testOpenTranslucentTaskFragment()876     public void testOpenTranslucentTaskFragment() {
877         final Transition transition = createTestTransition(TRANSIT_OPEN);
878         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
879         ArraySet<WindowContainer> participants = transition.mParticipants;
880 
881         final Task task = createTask(mDisplayContent);
882         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
883         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
884 
885         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
886         closing.setOccludesParent(true);
887         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
888         opening.setOccludesParent(false);
889         // Start states.
890         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
891                 false /* vis */, true /* exChg */));
892         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
893                 true /* vis */, false /* exChg */));
894         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
895         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
896         fillChangeMap(changes, openingTaskFragment);
897         // End states.
898         closing.setVisibleRequested(false);
899         opening.setVisibleRequested(true);
900 
901         final int transit = transition.mType;
902         int flags = 0;
903 
904         // Check basic both tasks participating
905         participants.add(closingTaskFragment);
906         participants.add(openingTaskFragment);
907         ArrayList<Transition.ChangeInfo> targets =
908                 Transition.calculateTargets(participants, changes);
909         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
910         assertEquals(2, info.getChanges().size());
911         assertEquals(transit, info.getType());
912 
913         assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
914         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
915     }
916 
917     @Test
testCloseOpaqueTaskFragment_withFinishingActivity()918     public void testCloseOpaqueTaskFragment_withFinishingActivity() {
919         final Transition transition = createTestTransition(TRANSIT_CLOSE);
920         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
921         ArraySet<WindowContainer> participants = transition.mParticipants;
922 
923         final Task task = createTask(mDisplayContent);
924         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
925         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
926 
927         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
928         opening.setOccludesParent(true);
929         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
930         closing.setOccludesParent(true);
931         closing.finishing = true;
932         // Start states.
933         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
934                 false /* vis */, true /* exChg */));
935         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
936                 true /* vis */, false /* exChg */));
937         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
938         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
939         fillChangeMap(changes, openingTaskFragment);
940         // End states.
941         closing.setVisibleRequested(false);
942         opening.setVisibleRequested(true);
943 
944         final int transit = transition.mType;
945         int flags = 0;
946 
947         // Check basic both tasks participating
948         participants.add(closingTaskFragment);
949         participants.add(openingTaskFragment);
950         ArrayList<Transition.ChangeInfo> targets =
951                 Transition.calculateTargets(participants, changes);
952         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
953         assertEquals(2, info.getChanges().size());
954         assertEquals(transit, info.getType());
955 
956         assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
957         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
958     }
959 
960     @Test
testCloseTranslucentTaskFragment_withFinishingActivity()961     public void testCloseTranslucentTaskFragment_withFinishingActivity() {
962         final Transition transition = createTestTransition(TRANSIT_CLOSE);
963         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
964         ArraySet<WindowContainer> participants = transition.mParticipants;
965 
966         final Task task = createTask(mDisplayContent);
967         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
968         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
969 
970         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
971         opening.setOccludesParent(true);
972         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
973         closing.setOccludesParent(false);
974         closing.finishing = true;
975         // Start states.
976         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
977                 false /* vis */, true /* exChg */));
978         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
979                 true /* vis */, false /* exChg */));
980         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
981         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
982         fillChangeMap(changes, openingTaskFragment);
983         // End states.
984         closing.setVisibleRequested(false);
985         opening.setVisibleRequested(true);
986 
987         final int transit = transition.mType;
988         int flags = 0;
989 
990         // Check basic both tasks participating
991         participants.add(closingTaskFragment);
992         participants.add(openingTaskFragment);
993         ArrayList<Transition.ChangeInfo> targets =
994                 Transition.calculateTargets(participants, changes);
995         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
996         assertEquals(2, info.getChanges().size());
997         assertEquals(transit, info.getType());
998 
999         assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
1000         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
1001     }
1002 
1003     @Test
testTimeout()1004     public void testTimeout() {
1005         final TransitionController controller = new TestTransitionController(mAtm);
1006         final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
1007         final CountDownLatch latch = new CountDownLatch(1);
1008         // When the timeout is reached, it will finish the sync-group and notify transaction ready.
1009         final Transition t = new Transition(TRANSIT_OPEN, 0 /* flags */, controller, sync) {
1010             @Override
1011             public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
1012                 latch.countDown();
1013             }
1014         };
1015         t.startCollecting(10 /* timeoutMs */);
1016         assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS)));
1017     }
1018 
1019     @Test
testTransitionBounds()1020     public void testTransitionBounds() {
1021         registerTestTransitionPlayer();
1022         final int offset = 10;
1023         final Function<WindowContainer<?>, TransitionInfo.Change> test = wc -> {
1024             final Transition transition = wc.mTransitionController.createTransition(TRANSIT_OPEN);
1025             transition.collect(wc);
1026             final int nextRotation = (wc.getWindowConfiguration().getRotation() + 1) % 4;
1027             wc.getWindowConfiguration().setRotation(nextRotation);
1028             wc.getWindowConfiguration().setDisplayRotation(nextRotation);
1029             final Rect bounds = wc.getWindowConfiguration().getBounds();
1030             // Flip the bounds with offset.
1031             wc.getWindowConfiguration().setBounds(
1032                     new Rect(offset, offset, bounds.height(), bounds.width()));
1033             final int flags = 0;
1034             final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, flags,
1035                     Transition.calculateTargets(transition.mParticipants, transition.mChanges),
1036                     mMockT);
1037             transition.abort();
1038             return info.getChanges().get(0);
1039         };
1040 
1041         final ActivityRecord app = createActivityRecord(mDisplayContent);
1042         final TransitionInfo.Change changeOfActivity = test.apply(app);
1043         // There will be letterbox if the activity bounds don't match parent, so always use its
1044         // parent bounds for animation.
1045         assertEquals(app.getParent().getBounds(), changeOfActivity.getEndAbsBounds());
1046         final int endRotation = app.mTransitionController.useShellTransitionsRotation()
1047                 ? app.getWindowConfiguration().getRotation()
1048                 // Without shell rotation, fixed rotation is done by core so the info should not
1049                 // contain rotation change.
1050                 : app.getParent().getWindowConfiguration().getRotation();
1051         assertEquals(endRotation, changeOfActivity.getEndRotation());
1052 
1053         // Non-activity target always uses its configuration for end info.
1054         final Task task = app.getTask();
1055         final TransitionInfo.Change changeOfTask = test.apply(task);
1056         assertEquals(task.getBounds(), changeOfTask.getEndAbsBounds());
1057         assertEquals(new Point(offset, offset), changeOfTask.getEndRelOffset());
1058         assertEquals(task.getWindowConfiguration().getRotation(), changeOfTask.getEndRotation());
1059     }
1060 
1061     @Test
testDisplayRotationChange()1062     public void testDisplayRotationChange() {
1063         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
1064         spyOn(displayPolicy);
1065         // Simulate gesture navigation (non-movable) so it is not seamless.
1066         doReturn(false).when(displayPolicy).navigationBarCanMove();
1067         final Task task = createActivityRecord(mDisplayContent).getTask();
1068         final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
1069         final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
1070         final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
1071         final WindowToken decorToken = new WindowToken.Builder(mWm, mock(IBinder.class),
1072                 TYPE_NAVIGATION_BAR_PANEL).setDisplayContent(mDisplayContent)
1073                 .setRoundedCornerOverlay(true).build();
1074         final WindowState screenDecor =
1075                 createWindow(null, decorToken.windowType, decorToken, "screenDecor");
1076         final WindowState[] windows = { statusBar, navBar, ime, screenDecor };
1077         makeWindowVisible(windows);
1078         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
1079         mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
1080         final TestTransitionPlayer player = registerTestTransitionPlayer();
1081 
1082         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
1083         mDisplayContent.setLastHasContent();
1084         mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */,
1085                 null /* displayChange */);
1086         assertEquals(WindowContainer.SYNC_STATE_NONE, statusBar.mSyncState);
1087         assertEquals(WindowContainer.SYNC_STATE_NONE, navBar.mSyncState);
1088         assertEquals(WindowContainer.SYNC_STATE_NONE, screenDecor.mSyncState);
1089         assertEquals(WindowContainer.SYNC_STATE_WAITING_FOR_DRAW, ime.mSyncState);
1090 
1091         final AsyncRotationController asyncRotationController =
1092                 mDisplayContent.getAsyncRotationController();
1093         assertNotNull(asyncRotationController);
1094         player.startTransition();
1095 
1096         assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
1097         assertFalse(mDisplayContent.mTransitionController.isCollecting(decorToken));
1098         assertTrue(ime.mToken.inTransition());
1099         assertTrue(task.inTransition());
1100         assertTrue(asyncRotationController.isTargetToken(decorToken));
1101         assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
1102 
1103         // Only seamless window syncs its draw transaction with transition.
1104         assertTrue(asyncRotationController.handleFinishDrawing(screenDecor, mMockT));
1105         // Status bar finishes drawing before the start transaction. Its fade-in animation will be
1106         // executed until the transaction is committed, so it is still in target tokens.
1107         assertFalse(asyncRotationController.handleFinishDrawing(statusBar, mMockT));
1108         assertTrue(asyncRotationController.isTargetToken(statusBar.mToken));
1109 
1110         final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
1111         final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
1112                 onRotationTransactionReady(player, startTransaction);
1113 
1114         // The transaction is committed, so fade-in animation for status bar is consumed.
1115         transactionCommittedListener.onTransactionCommitted();
1116         assertFalse(asyncRotationController.isTargetToken(statusBar.mToken));
1117         assertShouldFreezeInsetsPosition(asyncRotationController, navBar, false);
1118 
1119         // Navigation bar finishes drawing after the start transaction, so its fade-in animation
1120         // can execute directly.
1121         navBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
1122         asyncRotationController.updateTargetWindows();
1123         assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
1124         assertNull(mDisplayContent.getAsyncRotationController());
1125     }
1126 
1127     @Test
testAppTransitionWithRotationChange()1128     public void testAppTransitionWithRotationChange() {
1129         final TestTransitionPlayer player = registerTestTransitionPlayer();
1130         final boolean useFixedRotation = !player.mController.useShellTransitionsRotation();
1131         if (useFixedRotation) {
1132             testFixedRotationOpen(player);
1133         } else {
1134             testShellRotationOpen(player);
1135         }
1136     }
1137 
testShellRotationOpen(TestTransitionPlayer player)1138     private void testShellRotationOpen(TestTransitionPlayer player) {
1139         final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
1140         makeWindowVisible(statusBar);
1141         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
1142         final ActivityRecord app = createActivityRecord(mDisplayContent);
1143         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
1144         app.mTransitionController.requestStartTransition(transition, app.getTask(),
1145                 null /* remoteTransition */, null /* displayChange */);
1146         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
1147         final int anyChanges = 1;
1148         mDisplayContent.setLastHasContent();
1149         mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
1150         transition.setKnownConfigChanges(mDisplayContent, anyChanges);
1151         final AsyncRotationController asyncRotationController =
1152                 mDisplayContent.getAsyncRotationController();
1153         assertNotNull(asyncRotationController);
1154         assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
1155 
1156         player.startTransition();
1157         // Non-app windows should not be collected.
1158         assertFalse(statusBar.mToken.inTransition());
1159         assertTrue(app.getTask().inTransition());
1160 
1161         final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
1162         final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
1163         doReturn(true).when(leash).isValid();
1164         final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
1165                 onRotationTransactionReady(player, startTransaction);
1166         // The leash should be unrotated.
1167         verify(startTransaction).setMatrix(eq(leash), any(), any());
1168 
1169         // The redrawn window will be faded in when the transition finishes. And because this test
1170         // only use one non-activity window, the fade rotation controller should also be cleared.
1171         statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
1172         final SurfaceControl.Transaction postDrawTransaction =
1173                 mock(SurfaceControl.Transaction.class);
1174         final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction,
1175                 Integer.MAX_VALUE);
1176         assertFalse(layoutNeeded);
1177 
1178         transactionCommittedListener.onTransactionCommitted();
1179         player.finish();
1180         // The controller should capture the draw transaction and merge it when preparing to run
1181         // fade-in animation.
1182         verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction));
1183         assertNull(mDisplayContent.getAsyncRotationController());
1184     }
1185 
testFixedRotationOpen(TestTransitionPlayer player)1186     private void testFixedRotationOpen(TestTransitionPlayer player) {
1187         final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
1188         makeWindowVisible(statusBar);
1189         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
1190         final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
1191         final ActivityRecord app = createActivityRecord(mDisplayContent);
1192         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
1193         app.mTransitionController.requestStartTransition(transition, app.getTask(),
1194                 null /* remoteTransition */, null /* displayChange */);
1195         app.mTransitionController.collectExistenceChange(app.getTask());
1196         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
1197         final AsyncRotationController asyncRotationController =
1198                 mDisplayContent.getAsyncRotationController();
1199         assertNotNull(asyncRotationController);
1200         assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
1201         assertTrue(app.getTask().inTransition());
1202 
1203         player.start();
1204         player.finish();
1205         app.getTask().finishSync(mWm.mTransactionFactory.get(), app.getTask().getSyncGroup(),
1206                 false /* cancel */);
1207 
1208         // The open transition is finished. Continue to play seamless display change transition,
1209         // so the previous async rotation controller should still exist.
1210         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
1211         mDisplayContent.setLastHasContent();
1212         mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */);
1213         assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
1214         assertNotNull(mDisplayContent.getAsyncRotationController());
1215 
1216         // The app is still in transition, so the callback should be no-op.
1217         mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app);
1218         assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
1219 
1220         // The bar was invisible so it is not handled by the controller. But if it becomes visible
1221         // and drawn before the transition starts,
1222         assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
1223         navBar.finishDrawing(null /* postDrawTransaction */, Integer.MAX_VALUE);
1224         assertTrue(asyncRotationController.isTargetToken(navBar.mToken));
1225         assertTrue(asyncRotationController.shouldFreezeInsetsPosition(navBar));
1226 
1227         player.startTransition();
1228         // Non-app windows should not be collected.
1229         assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
1230         // Avoid DeviceStateController disturbing the test by triggering another rotation change.
1231         doReturn(false).when(mDisplayContent).updateRotationUnchecked();
1232 
1233         onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
1234         assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
1235                 mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
1236         player.finish();
1237 
1238         // The controller should be cleared if the target windows are drawn.
1239         statusBar.finishDrawing(mWm.mTransactionFactory.get(), Integer.MAX_VALUE);
1240         assertNull(mDisplayContent.getAsyncRotationController());
1241     }
1242 
assertShouldFreezeInsetsPosition(AsyncRotationController controller, WindowState w, boolean freeze)1243     private static void assertShouldFreezeInsetsPosition(AsyncRotationController controller,
1244             WindowState w, boolean freeze) {
1245         if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
1246             // Non blast sync should never freeze insets position.
1247             freeze = false;
1248         }
1249         assertEquals(freeze, controller.shouldFreezeInsetsPosition(w));
1250     }
1251 
1252     @Test
testFinishRotationControllerWithFixedRotation()1253     public void testFinishRotationControllerWithFixedRotation() {
1254         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
1255         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
1256         registerTestTransitionPlayer();
1257         mDisplayContent.setLastHasContent();
1258         mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */);
1259         assertNotNull(mDisplayContent.getAsyncRotationController());
1260         mDisplayContent.setFixedRotationLaunchingAppUnchecked(null);
1261         assertNull("Clear rotation controller if rotation is not changed",
1262                 mDisplayContent.getAsyncRotationController());
1263 
1264         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
1265         assertNotNull(mDisplayContent.getAsyncRotationController());
1266         mDisplayContent.getDisplayRotation().setRotation(
1267                 mDisplayContent.getWindowConfiguration().getRotation() + 1);
1268         mDisplayContent.setFixedRotationLaunchingAppUnchecked(null);
1269         assertNotNull("Keep rotation controller if rotation will be changed",
1270                 mDisplayContent.getAsyncRotationController());
1271     }
1272 
1273     @Test
testDeferRotationForTransientLaunch()1274     public void testDeferRotationForTransientLaunch() {
1275         final TestTransitionPlayer player = registerTestTransitionPlayer();
1276         assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation());
1277         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
1278         final ActivityRecord home = new ActivityBuilder(mAtm)
1279                 .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask())
1280                 .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build();
1281         final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN);
1282         final int prevRotation = mDisplayContent.getRotation();
1283         transition.setTransientLaunch(home, null /* restoreBelow */);
1284         home.mTransitionController.requestStartTransition(transition, home.getTask(),
1285                 null /* remoteTransition */, null /* displayChange */);
1286         transition.collectExistenceChange(home);
1287         home.setVisibleRequested(true);
1288         mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
1289         doReturn(true).when(home).hasFixedRotationTransform(any());
1290         player.startTransition();
1291         player.onTransactionReady(mDisplayContent.getSyncTransaction());
1292 
1293         final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
1294         final RemoteDisplayChangeController displayChangeController = mDisplayContent
1295                 .mRemoteDisplayChangeController;
1296         spyOn(displayRotation);
1297         spyOn(displayChangeController);
1298         doReturn(true).when(displayChangeController).isWaitingForRemoteDisplayChange();
1299         doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation(
1300                 anyInt() /* orientation */, anyInt() /* lastRotation */);
1301         // Rotation update is skipped while the recents animation is running.
1302         assertFalse(mDisplayContent.updateRotationUnchecked());
1303         assertEquals(SCREEN_ORIENTATION_UNSET, displayRotation.getLastOrientation());
1304         // Return to the app without fixed orientation from recents.
1305         app.moveFocusableActivityToTop("test");
1306         player.finish();
1307         // The display should be updated to the changed orientation after the animation is finish.
1308         assertNotEquals(mDisplayContent.getRotation(), prevRotation);
1309     }
1310 
1311     @Test
testIntermediateVisibility()1312     public void testIntermediateVisibility() {
1313         final TransitionController controller = new TestTransitionController(mAtm);
1314         controller.setSyncEngine(mWm.mSyncEngine);
1315         final ITransitionPlayer player = new ITransitionPlayer.Default();
1316         controller.registerTransitionPlayer(player, null /* playerProc */);
1317         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
1318 
1319         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1320         final Task task1 = createTask(mDisplayContent);
1321         final ActivityRecord activity1 = createActivityRecord(task1);
1322         activity1.setVisibleRequested(false);
1323         activity1.setVisible(false);
1324         final Task task2 = createTask(mDisplayContent);
1325         makeTaskOrganized(task1, task2);
1326         final ActivityRecord activity2 = createActivityRecord(task1);
1327         activity2.setVisibleRequested(true);
1328         activity2.setVisible(true);
1329 
1330         openTransition.collectExistenceChange(task1);
1331         openTransition.collectExistenceChange(activity1);
1332         openTransition.collectExistenceChange(task2);
1333         openTransition.collectExistenceChange(activity2);
1334 
1335         activity1.setVisibleRequested(true);
1336         activity1.setVisible(true);
1337         activity2.setVisibleRequested(false);
1338 
1339         // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
1340         // We didn't call abort on the transition itself, so it will still run onTransactionReady
1341         // normally.
1342         mWm.mSyncEngine.abort(openTransition.getSyncId());
1343 
1344         // Before finishing openTransition, we are now going to simulate closing task1 to return
1345         // back to (open) task2.
1346         final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
1347 
1348         closeTransition.collectExistenceChange(task1);
1349         closeTransition.collectExistenceChange(activity1);
1350         closeTransition.collectExistenceChange(task2);
1351         closeTransition.collectExistenceChange(activity2);
1352 
1353         activity1.setVisibleRequested(false);
1354         activity2.setVisibleRequested(true);
1355 
1356         openTransition.finishTransition();
1357 
1358         // We finished the openTransition. Even though activity1 is visibleRequested=false, since
1359         // the closeTransition animation hasn't played yet, make sure that we didn't commit
1360         // visible=false on activity1 since it needs to remain visible for the animation.
1361         assertTrue(activity1.isVisible());
1362         assertTrue(activity2.isVisible());
1363 
1364         // Using abort to force-finish the sync (since we obviously can't wait for drawing).
1365         // We didn't call abort on the actual transition, so it will still run onTransactionReady
1366         // normally.
1367         mWm.mSyncEngine.abort(closeTransition.getSyncId());
1368 
1369         closeTransition.finishTransition();
1370 
1371         assertFalse(activity1.isVisible());
1372         assertTrue(activity2.isVisible());
1373 
1374         // The abort should still commit visible-requested to visible.
1375         final Transition abortTransition = controller.createTransition(TRANSIT_OPEN);
1376         abortTransition.collect(activity1);
1377         activity1.setVisibleRequested(true);
1378         activity1.setVisible(false);
1379         abortTransition.abort();
1380         assertTrue(activity1.isVisible());
1381 
1382         // The mLaunchTaskBehind flag of an invisible initializing activity should not be cleared.
1383         final Transition noChangeTransition = controller.createTransition(TRANSIT_OPEN);
1384         noChangeTransition.collect(activity1);
1385         activity1.setVisibleRequested(false);
1386         activity1.setState(ActivityRecord.State.INITIALIZING, "test");
1387         activity1.mLaunchTaskBehind = true;
1388         mWm.mSyncEngine.abort(noChangeTransition.getSyncId());
1389         noChangeTransition.finishTransition();
1390         assertTrue(activity1.mLaunchTaskBehind);
1391     }
1392 
1393     @Test
testTransientLaunch()1394     public void testTransientLaunch() {
1395         spyOn(mWm.mSnapshotController.mTaskSnapshotController);
1396         final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>();
1397         final TransitionController controller = new TestTransitionController(mAtm) {
1398             @Override
1399             protected void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
1400                 if (ar.mEnteringAnimation) {
1401                     enteringAnimReports.add(ar);
1402                 }
1403                 super.dispatchLegacyAppTransitionFinished(ar);
1404             }
1405         };
1406         controller.setSyncEngine(mWm.mSyncEngine);
1407         controller.mSnapshotController = mWm.mSnapshotController;
1408         final TaskSnapshotController taskSnapshotController = controller.mSnapshotController
1409                 .mTaskSnapshotController;
1410         final ITransitionPlayer player = new ITransitionPlayer.Default();
1411         controller.registerTransitionPlayer(player, null /* playerProc */);
1412         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
1413 
1414         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1415         final Task task1 = createTask(mDisplayContent);
1416         final ActivityRecord activity1 = createActivityRecord(task1);
1417         activity1.setVisibleRequested(false);
1418         activity1.setVisible(false);
1419         final Task task2 = createTask(mDisplayContent);
1420         makeTaskOrganized(task1, task2);
1421         final ActivityRecord activity2 = createActivityRecord(task2);
1422         activity2.setVisibleRequested(true);
1423         activity2.setVisible(true);
1424 
1425         openTransition.collectExistenceChange(task1);
1426         openTransition.collectExistenceChange(activity1);
1427         openTransition.collectExistenceChange(task2);
1428         openTransition.collectExistenceChange(activity2);
1429 
1430         activity1.setVisibleRequested(true);
1431         activity1.setVisible(true);
1432         activity2.setVisibleRequested(false);
1433 
1434         // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
1435         // We didn't call abort on the transition itself, so it will still run onTransactionReady
1436         // normally.
1437         mWm.mSyncEngine.abort(openTransition.getSyncId());
1438 
1439         verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2), eq(false));
1440 
1441         controller.finishTransition(openTransition);
1442 
1443         // We are now going to simulate closing task1 to return back to (open) task2.
1444         final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
1445 
1446         closeTransition.collectExistenceChange(task2);
1447         closeTransition.collectExistenceChange(activity2);
1448         closeTransition.setTransientLaunch(activity2, task1);
1449         final Transition.ChangeInfo task1ChangeInfo = closeTransition.mChanges.get(task1);
1450         assertNotNull(task1ChangeInfo);
1451         assertTrue(task1ChangeInfo.hasChanged());
1452         // Make sure the unrelated activity is NOT collected.
1453         final Transition.ChangeInfo activity1ChangeInfo = closeTransition.mChanges.get(activity1);
1454         assertNull(activity1ChangeInfo);
1455         // No need to wait for the activity in transient hide task.
1456         assertEquals(WindowContainer.SYNC_STATE_NONE, activity1.mSyncState);
1457 
1458         // An active transient launch overrides idle state to avoid clearing power mode before the
1459         // transition is finished.
1460         spyOn(mRootWindowContainer.mTransitionController);
1461         doAnswer(invocation -> controller.isTransientLaunch(invocation.getArgument(0))).when(
1462                 mRootWindowContainer.mTransitionController).isTransientLaunch(any());
1463         activity2.getTask().setResumedActivity(activity2, "test");
1464         activity2.idle = true;
1465         assertFalse(mRootWindowContainer.allResumedActivitiesIdle());
1466 
1467         activity1.setVisibleRequested(false);
1468         activity2.setVisibleRequested(true);
1469         activity2.setVisible(true);
1470 
1471         // Using abort to force-finish the sync (since we obviously can't wait for drawing).
1472         // We didn't call abort on the actual transition, so it will still run onTransactionReady
1473         // normally.
1474         mWm.mSyncEngine.abort(closeTransition.getSyncId());
1475 
1476         // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be
1477         // called until finish).
1478         verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1), eq(false));
1479 
1480         enteringAnimReports.clear();
1481         doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(),
1482                 anyInt(), anyBoolean(), anyBoolean());
1483         final boolean[] wasInFinishingTransition = { false };
1484         controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
1485             @Override
1486             public void onAppTransitionFinishedLocked(IBinder token) {
1487                 final ActivityRecord r = ActivityRecord.forToken(token);
1488                 if (r != null) {
1489                     wasInFinishingTransition[0] = controller.inFinishingTransition(r);
1490                 }
1491             }
1492         });
1493         assertTrue(activity1.isVisible());
1494         doReturn(false).when(task1).isTranslucent(null);
1495         assertTrue(controller.canApplyDim(task1));
1496         doReturn(true).when(task1).isTranslucent(null);
1497         assertFalse(controller.canApplyDim(task1));
1498 
1499         controller.finishTransition(closeTransition);
1500         assertTrue(wasInFinishingTransition[0]);
1501         assertNull(controller.mFinishingTransition);
1502 
1503         assertTrue(activity2.isVisible());
1504         assertEquals(ActivityTaskManagerService.APP_SWITCH_DISALLOW, mAtm.getBalAppSwitchesState());
1505         // Because task1 is occluded by task2, finishTransition should make activity1 invisible.
1506         assertFalse(activity1.isVisibleRequested());
1507         // Make sure activity1 visibility was committed
1508         assertFalse(activity1.isVisible());
1509         assertFalse(activity1.app.hasActivityInVisibleTask());
1510 
1511         verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1), eq(false));
1512         assertTrue(enteringAnimReports.contains(activity2));
1513     }
1514 
1515     @Test
testIsTransientVisible()1516     public void testIsTransientVisible() {
1517         final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
1518                 .setVisible(false).build();
1519         final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
1520                 .setVisible(false).build();
1521         final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
1522         final Task taskA = appA.getTask();
1523         final Task taskB = appB.getTask();
1524         final Task taskRecent = recent.getTask();
1525         registerTestTransitionPlayer();
1526         final TransitionController controller = mRootWindowContainer.mTransitionController;
1527         final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
1528         controller.moveToCollecting(transition);
1529         transition.collect(recent);
1530         transition.collect(taskA);
1531         transition.setTransientLaunch(recent, taskA);
1532         taskRecent.moveToFront("move-recent-to-front");
1533 
1534         // During collecting and playing, the recent is on top so it is visible naturally.
1535         // While B needs isTransientVisible to keep visibility because it is occluded by recents.
1536         assertFalse(controller.isTransientVisible(taskB));
1537         assertTrue(controller.isTransientVisible(taskA));
1538         assertFalse(controller.isTransientVisible(taskRecent));
1539         // Switch to playing state.
1540         transition.onTransactionReady(transition.getSyncId(), mMockT);
1541         assertTrue(controller.isTransientVisible(taskA));
1542 
1543         // Switch to another task. For example, use gesture navigation to switch tasks.
1544         taskB.moveToFront("move-b-to-front");
1545         // The previous app (taskA) should be paused first so it loses transient visible. Because
1546         // visually it is taskA -> taskB, the pause -> resume order should be the same.
1547         assertFalse(controller.isTransientVisible(taskA));
1548         // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
1549         // to avoid the latency to resume the current top, i.e. appB.
1550         assertTrue(controller.isTransientVisible(taskRecent));
1551         // The recent is paused after the transient transition is finished.
1552         controller.finishTransition(transition);
1553         assertFalse(controller.isTransientVisible(taskRecent));
1554     }
1555 
1556     @Test
testNotReadyPushPop()1557     public void testNotReadyPushPop() {
1558         final TransitionController controller = new TestTransitionController(mAtm);
1559         controller.setSyncEngine(mWm.mSyncEngine);
1560         final ITransitionPlayer player = new ITransitionPlayer.Default();
1561         controller.registerTransitionPlayer(player, null /* playerProc */);
1562         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
1563 
1564         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1565         final Task task1 = createTask(mDisplayContent);
1566         openTransition.collectExistenceChange(task1);
1567 
1568         assertFalse(openTransition.allReady());
1569 
1570         openTransition.setAllReady();
1571 
1572         openTransition.deferTransitionReady();
1573         assertFalse(openTransition.allReady());
1574 
1575         openTransition.continueTransitionReady();
1576         assertTrue(openTransition.allReady());
1577     }
1578 
1579     @Test
testIsBehindStartingWindowChange()1580     public void testIsBehindStartingWindowChange() {
1581         final Transition transition = createTestTransition(TRANSIT_OPEN);
1582         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1583         final ArraySet<WindowContainer> participants = transition.mParticipants;
1584 
1585         final Task task = createTask(mDisplayContent);
1586         final ActivityRecord activity0 = createActivityRecord(task);
1587         final ActivityRecord activity1 = createActivityRecord(task);
1588         doReturn(true).when(activity1).hasStartingWindow();
1589 
1590         // Start states.
1591         changes.put(activity0,
1592                 new Transition.ChangeInfo(activity0, true /* vis */, false /* exChg */));
1593         changes.put(activity1,
1594                 new Transition.ChangeInfo(activity1, false /* vis */, false /* exChg */));
1595         // End states.
1596         activity0.setVisibleRequested(false);
1597         activity1.setVisibleRequested(true);
1598 
1599         participants.add(activity0);
1600         participants.add(activity1);
1601         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1602                 participants, changes);
1603         final TransitionInfo info = Transition.calculateTransitionInfo(
1604                 transition.mType, 0 /* flags */, targets, mMockT);
1605 
1606         // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting
1607         // window should cover the whole Task.
1608         assertEquals(2, info.getChanges().size());
1609         assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
1610         assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
1611 
1612     }
1613 
1614     @Test
testFlagInTaskWithEmbeddedActivity()1615     public void testFlagInTaskWithEmbeddedActivity() {
1616         final Transition transition = createTestTransition(TRANSIT_OPEN);
1617         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1618         final ArraySet<WindowContainer> participants = transition.mParticipants;
1619 
1620         final Task task = createTask(mDisplayContent);
1621         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1622         assertFalse(nonEmbeddedActivity.isEmbedded());
1623         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1624         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1625                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1626         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1627                 .setParentTask(task)
1628                 .createActivityCount(2)
1629                 .setOrganizer(organizer)
1630                 .build();
1631         final ActivityRecord closingActivity = embeddedTf.getBottomMostActivity();
1632         final ActivityRecord openingActivity = embeddedTf.getTopMostActivity();
1633         // Start states.
1634         changes.put(embeddedTf,
1635                 new Transition.ChangeInfo(embeddedTf, true /* vis */, false /* exChg */));
1636         changes.put(closingActivity,
1637                 new Transition.ChangeInfo(closingActivity, true /* vis */, false /* exChg */));
1638         changes.put(openingActivity,
1639                 new Transition.ChangeInfo(openingActivity, false /* vis */, true /* exChg */));
1640         changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity,
1641                 true /* vis */, false /* exChg */));
1642         // End states.
1643         closingActivity.setVisibleRequested(false);
1644         openingActivity.setVisibleRequested(true);
1645         nonEmbeddedActivity.setVisibleRequested(false);
1646 
1647         participants.add(closingActivity);
1648         participants.add(openingActivity);
1649         participants.add(nonEmbeddedActivity);
1650         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1651                 participants, changes);
1652         final TransitionInfo info = Transition.calculateTransitionInfo(
1653                 transition.mType, 0 /* flags */, targets, mMockT);
1654 
1655         // All windows in the Task should have FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY because the Task
1656         // contains embedded activity.
1657         assertEquals(3, info.getChanges().size());
1658         assertTrue(info.getChanges().get(0).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1659         assertTrue(info.getChanges().get(1).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1660         assertTrue(info.getChanges().get(2).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1661     }
1662 
1663     @Test
testFlagFillsTask_embeddingNotFillingTask()1664     public void testFlagFillsTask_embeddingNotFillingTask() {
1665         final Transition transition = createTestTransition(TRANSIT_OPEN);
1666         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1667         final ArraySet<WindowContainer> participants = transition.mParticipants;
1668 
1669         final Task task = createTask(mDisplayContent);
1670         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1671         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1672         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1673         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1674         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1675                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1676         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1677                 .setParentTask(task)
1678                 .createActivityCount(1)
1679                 .setOrganizer(organizer)
1680                 .build();
1681         final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
1682         // Start states.
1683         changes.put(task, new Transition.ChangeInfo(task, true /* vis */, false /* exChg */));
1684         changes.put(nonEmbeddedActivity,
1685                 new Transition.ChangeInfo(nonEmbeddedActivity, true /* vis */, false /* exChg */));
1686         changes.put(embeddedTf,
1687                 new Transition.ChangeInfo(embeddedTf, false /* vis */, true /* exChg */));
1688         // End states.
1689         nonEmbeddedActivity.setVisibleRequested(false);
1690         embeddedActivity.setVisibleRequested(true);
1691         embeddedTf.setBounds(new Rect(0, 0, 500, 500));
1692 
1693         participants.add(nonEmbeddedActivity);
1694         participants.add(embeddedTf);
1695         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1696                 participants, changes);
1697         final TransitionInfo info = Transition.calculateTransitionInfo(
1698                 transition.mType, 0 /* flags */, targets, mMockT);
1699 
1700         // The embedded with bounds overridden should not have the flag.
1701         assertEquals(2, info.getChanges().size());
1702         assertFalse(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1703         assertEquals(embeddedTf.getBounds(), info.getChanges().get(0).getEndAbsBounds());
1704         assertTrue(info.getChanges().get(1).hasFlags(FLAG_FILLS_TASK));
1705     }
1706 
1707     @Test
testFlagFillsTask_openActivityFillingTask()1708     public void testFlagFillsTask_openActivityFillingTask() {
1709         final Transition transition = createTestTransition(TRANSIT_OPEN);
1710         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1711         final ArraySet<WindowContainer> participants = transition.mParticipants;
1712 
1713         final Task task = createTask(mDisplayContent);
1714         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1715         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1716         final ActivityRecord activity = createActivityRecord(task);
1717         // Start states: set bounds to make sure the start bounds is ignored if it is not visible.
1718         activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
1719         activity.setVisibleRequested(false);
1720         changes.put(activity, new Transition.ChangeInfo(activity));
1721         // End states: reset bounds to fill Task.
1722         activity.getConfiguration().windowConfiguration.setBounds(taskBounds);
1723         activity.setVisibleRequested(true);
1724 
1725         participants.add(activity);
1726         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1727                 participants, changes);
1728         final TransitionInfo info = Transition.calculateTransitionInfo(
1729                 transition.mType, 0 /* flags */, targets, mMockT);
1730 
1731         // Opening activity that is filling Task after transition should have the flag.
1732         assertEquals(1, info.getChanges().size());
1733         assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1734     }
1735 
1736     @Test
testFlagFillsTask_closeActivityFillingTask()1737     public void testFlagFillsTask_closeActivityFillingTask() {
1738         final Transition transition = createTestTransition(TRANSIT_CLOSE);
1739         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1740         final ArraySet<WindowContainer> participants = transition.mParticipants;
1741 
1742         final Task task = createTask(mDisplayContent);
1743         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1744         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1745         final ActivityRecord activity = createActivityRecord(task);
1746         // Start states: fills Task without override.
1747         activity.setVisibleRequested(true);
1748         changes.put(activity, new Transition.ChangeInfo(activity));
1749         // End states: set bounds to make sure the start bounds is ignored if it is not visible.
1750         activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
1751         activity.setVisibleRequested(false);
1752 
1753         participants.add(activity);
1754         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1755                 participants, changes);
1756         final TransitionInfo info = Transition.calculateTransitionInfo(
1757                 transition.mType, 0 /* flags */, targets, mMockT);
1758 
1759         // Closing activity that is filling Task before transition should have the flag.
1760         assertEquals(1, info.getChanges().size());
1761         assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1762     }
1763 
1764     @Test
testReparentChangeLastParent()1765     public void testReparentChangeLastParent() {
1766         final Transition transition = createTestTransition(TRANSIT_CHANGE);
1767         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1768         final ArraySet<WindowContainer> participants = transition.mParticipants;
1769 
1770         // Reparent activity in transition.
1771         final Task lastParent = createTask(mDisplayContent);
1772         final Task newParent = createTask(mDisplayContent);
1773         final ActivityRecord activity = createActivityRecord(lastParent);
1774         activity.setVisibleRequested(true);
1775         // Skip manipulate the SurfaceControl.
1776         doNothing().when(activity).setDropInputMode(anyInt());
1777         changes.put(activity, new Transition.ChangeInfo(activity));
1778         activity.reparent(newParent, POSITION_TOP);
1779         activity.setVisibleRequested(false);
1780 
1781         participants.add(activity);
1782         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1783                 participants, changes);
1784         final TransitionInfo info = Transition.calculateTransitionInfo(
1785                 transition.mType, 0 /* flags */, targets, mMockT);
1786 
1787         // Change contains last parent info.
1788         assertEquals(1, info.getChanges().size());
1789         assertEquals(lastParent.mRemoteToken.toWindowContainerToken(),
1790                 info.getChanges().get(0).getLastParent());
1791     }
1792 
1793     @Test
testIncludeEmbeddedActivityReparent()1794     public void testIncludeEmbeddedActivityReparent() {
1795         final Transition transition = createTestTransition(TRANSIT_OPEN);
1796         final Task task = createTask(mDisplayContent);
1797         task.setBounds(new Rect(0, 0, 2000, 1000));
1798         final ActivityRecord activity = createActivityRecord(task);
1799         activity.setVisibleRequested(true);
1800         // Skip manipulate the SurfaceControl.
1801         doNothing().when(activity).setDropInputMode(anyInt());
1802         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1803         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1804                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1805         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1806                 .setParentTask(task)
1807                 .setOrganizer(organizer)
1808                 .build();
1809         // TaskFragment with different bounds from Task.
1810         embeddedTf.setBounds(new Rect(0, 0, 1000, 1000));
1811 
1812         // Start states.
1813         transition.collect(activity);
1814         transition.collectExistenceChange(embeddedTf);
1815 
1816         // End states.
1817         activity.reparent(embeddedTf, POSITION_TOP);
1818 
1819         // Verify that both activity and TaskFragment are included.
1820         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1821                 transition.mParticipants, transition.mChanges);
1822         assertTrue(Transition.containsChangeFor(embeddedTf, targets));
1823         assertTrue(Transition.containsChangeFor(activity, targets));
1824     }
1825 
1826     @Test
testChangeSetBackgroundColor()1827     public void testChangeSetBackgroundColor() {
1828         final Transition transition = createTestTransition(TRANSIT_CHANGE);
1829         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1830         final ArraySet<WindowContainer> participants = transition.mParticipants;
1831 
1832         // Test background color for Activity and embedded TaskFragment.
1833         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1834         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1835                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1836         final Task task = createTask(mDisplayContent);
1837         final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
1838         final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
1839         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1840         final ActivityManager.TaskDescription taskDescription =
1841                 new ActivityManager.TaskDescription.Builder()
1842                         .setBackgroundColor(Color.YELLOW)
1843                         .build();
1844         task.setTaskDescription(taskDescription);
1845 
1846         // Start states:
1847         embeddedActivity.setVisibleRequested(true);
1848         nonEmbeddedActivity.setVisibleRequested(false);
1849         changes.put(embeddedTf, new Transition.ChangeInfo(embeddedTf));
1850         changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity));
1851         // End states:
1852         embeddedActivity.setVisibleRequested(false);
1853         nonEmbeddedActivity.setVisibleRequested(true);
1854 
1855         participants.add(embeddedTf);
1856         participants.add(nonEmbeddedActivity);
1857         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1858                 participants, changes);
1859         final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType,
1860                 0 /* flags */, targets, mMockT);
1861 
1862         // Background color should be set on both Activity and embedded TaskFragment.
1863         final int expectedBackgroundColor = ColorUtils.setAlphaComponent(
1864                 taskDescription.getBackgroundColor(), 255);
1865         assertEquals(2, info.getChanges().size());
1866         assertEquals(expectedBackgroundColor, info.getChanges().get(0).getBackgroundColor());
1867         assertEquals(expectedBackgroundColor, info.getChanges().get(1).getBackgroundColor());
1868     }
1869 
1870     @Test
testTransitionVisibleChange()1871     public void testTransitionVisibleChange() {
1872         registerTestTransitionPlayer();
1873         final ActivityRecord app = createActivityRecord(
1874                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
1875         final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */,
1876                 app.mTransitionController, mWm.mSyncEngine);
1877         app.mTransitionController.moveToCollecting(transition);
1878         mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE);
1879         final ArrayList<WindowContainer> freezeCalls = new ArrayList<>();
1880         transition.setContainerFreezer(new Transition.IContainerFreezer() {
1881             @Override
1882             public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
1883                 freezeCalls.add(wc);
1884                 return true;
1885             }
1886 
1887             @Override
1888             public void cleanUp(SurfaceControl.Transaction t) {
1889             }
1890         });
1891         final Task task = app.getTask();
1892         transition.collect(task);
1893         final Rect bounds = new Rect(task.getBounds());
1894         Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
1895         bounds.inset(10, 10);
1896         c.windowConfiguration.setBounds(bounds);
1897         task.onRequestedOverrideConfigurationChanged(c);
1898         assertTrue(freezeCalls.contains(task));
1899         transition.abort();
1900     }
1901 
1902     @Test
testDeferTransitionReady_deferStartedTransition()1903     public void testDeferTransitionReady_deferStartedTransition() {
1904         final Transition transition = createTestTransition(TRANSIT_OPEN);
1905         transition.setAllReady();
1906         transition.start();
1907 
1908         assertTrue(mSyncEngine.isReady(transition.getSyncId()));
1909 
1910         transition.deferTransitionReady();
1911 
1912         // Both transition ready tracker and sync engine should be deferred.
1913         assertFalse(transition.allReady());
1914         assertFalse(mSyncEngine.isReady(transition.getSyncId()));
1915 
1916         transition.continueTransitionReady();
1917 
1918         assertTrue(transition.allReady());
1919         assertTrue(mSyncEngine.isReady(transition.getSyncId()));
1920     }
1921 
1922     @Test
testVisibleChange_snapshot()1923     public void testVisibleChange_snapshot() {
1924         registerTestTransitionPlayer();
1925         final ActivityRecord app = createActivityRecord(
1926                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
1927         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
1928                 app.mTransitionController, mWm.mSyncEngine);
1929         app.mTransitionController.moveToCollecting(transition);
1930         mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE);
1931         final SurfaceControl mockSnapshot = mock(SurfaceControl.class);
1932         transition.setContainerFreezer(new Transition.IContainerFreezer() {
1933             @Override
1934             public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
1935                 Objects.requireNonNull(transition.mChanges.get(wc)).mSnapshot = mockSnapshot;
1936                 return true;
1937             }
1938 
1939             @Override
1940             public void cleanUp(SurfaceControl.Transaction t) {
1941             }
1942         });
1943         final Task task = app.getTask();
1944         transition.collect(task);
1945         final Rect bounds = new Rect(task.getBounds());
1946         Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
1947         bounds.inset(10, 10);
1948         c.windowConfiguration.setBounds(bounds);
1949         task.onRequestedOverrideConfigurationChanged(c);
1950 
1951         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1952                 transition.mParticipants, transition.mChanges);
1953         TransitionInfo info = Transition.calculateTransitionInfo(
1954                 TRANSIT_CHANGE, 0, targets, mMockT);
1955         assertEquals(mockSnapshot,
1956                 info.getChange(task.mRemoteToken.toWindowContainerToken()).getSnapshot());
1957         transition.abort();
1958     }
1959 
1960     @Test
testCollectReparentChange()1961     public void testCollectReparentChange() {
1962         registerTestTransitionPlayer();
1963 
1964         // Reparent activity in transition.
1965         final Task lastParent = createTask(mDisplayContent);
1966         final Task newParent = createTask(mDisplayContent);
1967         final ActivityRecord activity = createActivityRecord(lastParent);
1968         doReturn(true).when(lastParent).shouldRemoveSelfOnLastChildRemoval();
1969         doNothing().when(activity).setDropInputMode(anyInt());
1970         activity.setVisibleRequested(true);
1971 
1972         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
1973                 activity.mTransitionController, mWm.mSyncEngine);
1974         activity.mTransitionController.moveToCollecting(transition);
1975         transition.collect(activity);
1976         activity.reparent(newParent, POSITION_TOP);
1977 
1978         // ChangeInfo#mCommonAncestor should be set after reparent.
1979         final Transition.ChangeInfo change = transition.mChanges.get(activity);
1980         assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
1981     }
1982 
1983     @Test
testMoveToTopWhileVisible()1984     public void testMoveToTopWhileVisible() {
1985         final Transition transition = createTestTransition(TRANSIT_OPEN);
1986         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1987         final ArraySet<WindowContainer> participants = transition.mParticipants;
1988 
1989         // Start with taskB on top and taskA on bottom but both visible.
1990         final Task rootTaskA = createTask(mDisplayContent);
1991         final Task leafTaskA = createTaskInRootTask(rootTaskA, 0 /* userId */);
1992         final Task taskB = createTask(mDisplayContent);
1993         leafTaskA.setVisibleRequested(true);
1994         taskB.setVisibleRequested(true);
1995         // manually collect since this is a test transition and not known by transitionController.
1996         transition.collect(leafTaskA);
1997         rootTaskA.moveToFront("test", leafTaskA);
1998 
1999         // All the tasks were already visible, so there shouldn't be any changes
2000         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
2001                 participants, changes);
2002         assertTrue(targets.isEmpty());
2003 
2004         // After collecting order changes, it should recognize that a task moved to top.
2005         transition.collectOrderChanges(true);
2006         targets = Transition.calculateTargets(participants, changes);
2007         assertEquals(1, targets.size());
2008 
2009         // Make sure the flag is set
2010         final TransitionInfo info = Transition.calculateTransitionInfo(
2011                 transition.mType, 0 /* flags */, targets, mMockT);
2012         assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
2013         assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
2014     }
2015 
2016     private class OrderChangeTestSetup {
2017         final TransitionController mController;
2018         final TestTransitionPlayer mPlayer;
2019         final Transition mTransitA;
2020         final Transition mTransitB;
2021 
OrderChangeTestSetup()2022         OrderChangeTestSetup() {
2023             mController = mAtm.getTransitionController();
2024             mPlayer = registerTestTransitionPlayer();
2025             mController.setSyncEngine(mWm.mSyncEngine);
2026 
2027             mTransitA = createTestTransition(TRANSIT_OPEN, mController);
2028             mTransitA.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2029             mTransitB = createTestTransition(TRANSIT_OPEN, mController);
2030             mTransitB.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2031         }
2032 
startParallelCollect(boolean activityLevelFirst)2033         void startParallelCollect(boolean activityLevelFirst) {
2034             // Start with taskB on top and taskA on bottom but both visible.
2035             final Task taskA = createTask(mDisplayContent);
2036             taskA.setVisibleRequested(true);
2037             final ActivityRecord actA = createActivityRecord(taskA);
2038             final TestWindowState winA = createWindowState(
2039                     new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), actA);
2040             actA.addWindow(winA);
2041             final ActivityRecord actB = createActivityRecord(taskA);
2042             final TestWindowState winB = createWindowState(
2043                     new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), actB);
2044             actB.addWindow(winB);
2045 
2046             final Task taskB = createTask(mDisplayContent);
2047             actA.setVisibleRequested(true);
2048             actB.setVisibleRequested(false);
2049             taskB.setVisibleRequested(true);
2050             assertTrue(actA.isAttached());
2051 
2052             final Consumer<Boolean> startAndCollectA = (doReady) -> {
2053                 mController.startCollectOrQueue(mTransitA, (deferred) -> {
2054                 });
2055 
2056                 // Collect activity-level change into A
2057                 mTransitA.collect(actA);
2058                 actA.setVisibleRequested(false);
2059                 winA.onSyncFinishedDrawing();
2060                 mTransitA.collect(actB);
2061                 actB.setVisibleRequested(true);
2062                 winB.onSyncFinishedDrawing();
2063                 mTransitA.start();
2064                 if (doReady) {
2065                     mTransitA.setReady(mDisplayContent, true);
2066                 }
2067             };
2068             final Consumer<Boolean> startAndCollectB = (doReady) -> {
2069                 mController.startCollectOrQueue(mTransitB, (deferred) -> {
2070                 });
2071                 mTransitB.collect(taskA);
2072                 taskA.moveToFront("test");
2073                 mTransitB.start();
2074                 if (doReady) {
2075                     mTransitB.setReady(mDisplayContent, true);
2076                 }
2077             };
2078 
2079             if (activityLevelFirst) {
2080                 startAndCollectA.accept(true);
2081                 startAndCollectB.accept(false);
2082             } else {
2083                 startAndCollectB.accept(true);
2084                 startAndCollectA.accept(false);
2085             }
2086         }
2087     }
2088 
2089     @Test
testMoveToTopStartAfterReadyAfterParallel()2090     public void testMoveToTopStartAfterReadyAfterParallel() {
2091         // Start collect activity-only transit A
2092         // Start collect task transit B in parallel
2093         // finish A first -> should not include order change from B.
2094         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2095         setup.startParallelCollect(true /* activity first */);
2096 
2097         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2098         waitUntilHandlersIdle();
2099         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2100             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2101         }
2102 
2103         setup.mTransitB.setAllReady();
2104         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2105         waitUntilHandlersIdle();
2106         boolean hasOrderChange = false;
2107         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2108             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2109             if (chg.getTaskInfo() == null) continue;
2110             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2111         }
2112         assertTrue(hasOrderChange);
2113     }
2114 
2115     @Test
testMoveToTopStartAfterReadyBeforeParallel()2116     public void testMoveToTopStartAfterReadyBeforeParallel() {
2117         // Start collect activity-only transit A
2118         // Start collect task transit B in parallel
2119         // finish B first -> should include order change
2120         // then finish A -> should NOT include order change.
2121         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2122         setup.startParallelCollect(true /* activity first */);
2123         // Make it unready now so that it doesn't get dequeued automatically.
2124         setup.mTransitA.setReady(mDisplayContent, false);
2125 
2126         // Make task change ready first
2127         setup.mTransitB.setAllReady();
2128         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2129         waitUntilHandlersIdle();
2130         boolean hasOrderChange = false;
2131         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2132             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2133             if (chg.getTaskInfo() == null) continue;
2134             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2135         }
2136         assertTrue(hasOrderChange);
2137 
2138         setup.mTransitA.setAllReady();
2139         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2140         waitUntilHandlersIdle();
2141         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2142             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2143         }
2144     }
2145 
2146     @Test
testMoveToTopStartBeforeReadyAfterParallel()2147     public void testMoveToTopStartBeforeReadyAfterParallel() {
2148         // Start collect task transit B
2149         // Start collect activity-only transit A in parallel
2150         // finish A first -> should not include order change from B.
2151         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2152         setup.startParallelCollect(false /* activity first */);
2153         // Make B unready now so that it doesn't get dequeued automatically.
2154         setup.mTransitB.setReady(mDisplayContent, false);
2155 
2156         setup.mTransitA.setAllReady();
2157         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2158         waitUntilHandlersIdle();
2159         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2160             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2161         }
2162 
2163         setup.mTransitB.setAllReady();
2164         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2165         waitUntilHandlersIdle();
2166         boolean hasOrderChange = false;
2167         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2168             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2169             if (chg.getTaskInfo() == null) continue;
2170             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2171         }
2172         assertTrue(hasOrderChange);
2173     }
2174 
2175     @Test
testMoveToTopStartBeforeReadyBeforeParallel()2176     public void testMoveToTopStartBeforeReadyBeforeParallel() {
2177         // Start collect task transit B
2178         // Start collect activity-only transit A in parallel
2179         // finish B first -> should include order change
2180         // then finish A -> should NOT include order change.
2181         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2182         setup.startParallelCollect(false /* activity first */);
2183 
2184         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2185         waitUntilHandlersIdle();
2186         boolean hasOrderChange = false;
2187         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2188             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2189             if (chg.getTaskInfo() == null) continue;
2190             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2191         }
2192         assertTrue(hasOrderChange);
2193 
2194         setup.mTransitA.setAllReady();
2195         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2196         waitUntilHandlersIdle();
2197         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2198             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2199         }
2200     }
2201 
2202     @Test
testQueueStartCollect()2203     public void testQueueStartCollect() {
2204         final TransitionController controller = mAtm.getTransitionController();
2205         final TestTransitionPlayer player = registerTestTransitionPlayer();
2206 
2207         mSyncEngine = createTestBLASTSyncEngine();
2208         controller.setSyncEngine(mSyncEngine);
2209 
2210         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2211         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2212         final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
2213 
2214         final boolean[] onStartA = new boolean[]{false, false};
2215         final boolean[] onStartB = new boolean[]{false, false};
2216         controller.startCollectOrQueue(transitA, (deferred) -> {
2217             onStartA[0] = true;
2218             onStartA[1] = deferred;
2219         });
2220         controller.startCollectOrQueue(transitB, (deferred) -> {
2221             onStartB[0] = true;
2222             onStartB[1] = deferred;
2223         });
2224         waitUntilHandlersIdle();
2225 
2226         assertTrue(onStartA[0]);
2227         assertFalse(onStartA[1]);
2228         assertTrue(transitA.isCollecting());
2229 
2230         // B should be queued, so no calls yet
2231         assertFalse(onStartB[0]);
2232         assertTrue(transitB.isPending());
2233 
2234         // finish collecting A
2235         transitA.start();
2236         transitA.setAllReady();
2237         mSyncEngine.tryFinishForTest(transitA.getSyncId());
2238         waitUntilHandlersIdle();
2239 
2240         assertTrue(transitA.isPlaying());
2241         assertTrue(transitB.isCollecting());
2242         assertTrue(onStartB[0]);
2243         // Should receive deferred = true
2244         assertTrue(onStartB[1]);
2245 
2246         // finish collecting B
2247         transitB.start();
2248         transitB.setAllReady();
2249         mSyncEngine.tryFinishForTest(transitB.getSyncId());
2250         assertTrue(transitB.isPlaying());
2251 
2252         // Now we should be able to start collecting directly a new transition
2253         final boolean[] onStartC = new boolean[]{false, false};
2254         controller.startCollectOrQueue(transitC, (deferred) -> {
2255             onStartC[0] = true;
2256             onStartC[1] = deferred;
2257         });
2258         waitUntilHandlersIdle();
2259         assertTrue(onStartC[0]);
2260         assertFalse(onStartC[1]);
2261         assertTrue(transitC.isCollecting());
2262     }
2263 
2264     @Test
testQueueWithLegacy()2265     public void testQueueWithLegacy() {
2266         final TransitionController controller = mAtm.getTransitionController();
2267         final TestTransitionPlayer player = registerTestTransitionPlayer();
2268 
2269         mSyncEngine = createTestBLASTSyncEngine();
2270         controller.setSyncEngine(mSyncEngine);
2271 
2272         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2273         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2274 
2275         controller.startCollectOrQueue(transitA, (deferred) -> {});
2276 
2277         BLASTSyncEngine.SyncGroup legacySync = mSyncEngine.prepareSyncSet(
2278                 mock(BLASTSyncEngine.TransactionReadyListener.class), "test");
2279         final boolean[] applyLegacy = new boolean[2];
2280         controller.startLegacySyncOrQueue(legacySync, (deferred) -> {
2281             applyLegacy[0] = true;
2282             applyLegacy[1] = deferred;
2283         });
2284         assertFalse(applyLegacy[0]);
2285         waitUntilHandlersIdle();
2286 
2287         controller.startCollectOrQueue(transitB, (deferred) -> {});
2288         assertTrue(transitA.isCollecting());
2289 
2290         // finish collecting A
2291         transitA.start();
2292         transitA.setAllReady();
2293         mSyncEngine.tryFinishForTest(transitA.getSyncId());
2294         waitUntilHandlersIdle();
2295 
2296         assertTrue(transitA.isPlaying());
2297         // legacy sync should start now
2298         assertTrue(applyLegacy[0]);
2299         assertTrue(applyLegacy[1]);
2300         // transitB must wait
2301         assertTrue(transitB.isPending());
2302 
2303         // finish legacy sync
2304         mSyncEngine.setReady(legacySync.mSyncId);
2305         mSyncEngine.tryFinishForTest(legacySync.mSyncId);
2306         // transitioncontroller should be notified so it can start collecting B
2307         assertTrue(transitB.isCollecting());
2308     }
2309 
2310     @Test
testQueueParallel()2311     public void testQueueParallel() {
2312         final TransitionController controller = mAtm.getTransitionController();
2313         final TestTransitionPlayer player = registerTestTransitionPlayer();
2314 
2315         mSyncEngine = createTestBLASTSyncEngine();
2316         controller.setSyncEngine(mSyncEngine);
2317 
2318         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2319         transitA.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2320         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2321         transitB.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2322         final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
2323         transitC.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2324         final Transition transitSync = createTestTransition(TRANSIT_OPEN, controller);
2325         final Transition transitD = createTestTransition(TRANSIT_OPEN, controller);
2326 
2327         controller.startCollectOrQueue(transitA, (deferred) -> {});
2328         controller.startCollectOrQueue(transitB, (deferred) -> {});
2329         controller.startCollectOrQueue(transitC, (deferred) -> {});
2330         controller.startCollectOrQueue(transitSync, (deferred) -> {});
2331         controller.startCollectOrQueue(transitD, (deferred) -> {});
2332 
2333         assertTrue(transitA.isCollecting() && !transitA.isStarted());
2334         // We still serialize on readiness
2335         assertTrue(transitB.isPending());
2336         assertTrue(transitC.isPending());
2337 
2338         transitA.start();
2339         transitA.setAllReady();
2340         transitB.start();
2341         transitB.setAllReady();
2342 
2343         // A, B, and C should be collecting in parallel now.
2344         assertTrue(transitA.isStarted());
2345         assertTrue(transitB.isStarted());
2346         assertTrue(transitC.isCollecting() && !transitC.isStarted());
2347 
2348         transitC.start();
2349         transitC.setAllReady();
2350 
2351         assertTrue(transitA.isStarted());
2352         assertTrue(transitB.isStarted());
2353         assertTrue(transitC.isStarted());
2354         // Not parallel so should remain pending
2355         assertTrue(transitSync.isPending());
2356         // After Sync, so should also remain pending.
2357         assertTrue(transitD.isPending());
2358         // There should always be a collector, since Sync can't collect yet, C should remain.
2359         assertEquals(transitC, controller.getCollectingTransition());
2360 
2361         mSyncEngine.tryFinishForTest(transitB.getSyncId());
2362 
2363         // The other transitions should remain waiting.
2364         assertTrue(transitA.isStarted());
2365         assertTrue(transitB.isPlaying());
2366         assertTrue(transitC.isStarted());
2367         assertEquals(transitC, controller.getCollectingTransition());
2368 
2369         mSyncEngine.tryFinishForTest(transitC.getSyncId());
2370         assertTrue(transitA.isStarted());
2371         assertTrue(transitC.isPlaying());
2372         // The "collecting" one became ready, so the first "waiting" should move back to collecting.
2373         assertEquals(transitA, controller.getCollectingTransition());
2374 
2375         assertTrue(transitSync.isPending());
2376         assertTrue(transitD.isPending());
2377         mSyncEngine.tryFinishForTest(transitA.getSyncId());
2378 
2379         // Now all collectors are done, so sync can be pulled-off the queue.
2380         assertTrue(transitSync.isCollecting() && !transitSync.isStarted());
2381         transitSync.start();
2382         transitSync.setAllReady();
2383         // Since D can run in parallel, it should be pulled-off the queue.
2384         assertTrue(transitSync.isStarted());
2385         assertTrue(transitD.isPending());
2386 
2387         mSyncEngine.tryFinishForTest(transitSync.getSyncId());
2388         assertTrue(transitD.isCollecting());
2389 
2390         transitD.start();
2391         transitD.setAllReady();
2392         mSyncEngine.tryFinishForTest(transitD.getSyncId());
2393 
2394         // Now nothing should be collecting
2395         assertFalse(controller.isCollecting());
2396     }
2397 
2398     @Test
testNoSyncFlagIfOneTrack()2399     public void testNoSyncFlagIfOneTrack() {
2400         final TransitionController controller = mAtm.getTransitionController();
2401         final TestTransitionPlayer player = registerTestTransitionPlayer();
2402 
2403         mSyncEngine = createTestBLASTSyncEngine();
2404         controller.setSyncEngine(mSyncEngine);
2405 
2406         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2407         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2408         final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
2409 
2410         controller.startCollectOrQueue(transitA, (deferred) -> {});
2411         controller.startCollectOrQueue(transitB, (deferred) -> {});
2412         controller.startCollectOrQueue(transitC, (deferred) -> {});
2413 
2414         // Verify that, as-long as there is <= 1 track, we won't get a SYNC flag
2415         transitA.start();
2416         transitA.setAllReady();
2417         mSyncEngine.tryFinishForTest(transitA.getSyncId());
2418         assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
2419         transitB.start();
2420         transitB.setAllReady();
2421         mSyncEngine.tryFinishForTest(transitB.getSyncId());
2422         assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
2423         transitC.start();
2424         transitC.setAllReady();
2425         mSyncEngine.tryFinishForTest(transitC.getSyncId());
2426         assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
2427     }
2428 
makeTaskOrganized(Task... tasks)2429     private static void makeTaskOrganized(Task... tasks) {
2430         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
2431         for (Task t : tasks) {
2432             t.mTaskOrganizer = organizer;
2433         }
2434     }
2435 
makeDisplayAreaOrganized(WindowContainer<?> from, WindowContainer<?> end)2436     private static void makeDisplayAreaOrganized(WindowContainer<?> from,
2437             WindowContainer<?> end) {
2438         final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
2439         while (from != null && from != end) {
2440             if (from.asDisplayArea() != null) {
2441                 from.asDisplayArea().mOrganizer = organizer;
2442             }
2443             from = from.getParent();
2444         }
2445         if (end.asDisplayArea() != null) {
2446             end.asDisplayArea().mOrganizer = organizer;
2447         }
2448     }
2449 
2450     /** Fill the change map with all the parents of top. Change maps are usually fully populated */
fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, WindowContainer top)2451     private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
2452             WindowContainer top) {
2453         for (WindowContainer curr = top.getParent(); curr != null; curr = curr.getParent()) {
2454             changes.put(curr, new Transition.ChangeInfo(curr, true /* vis */, false /* exChg */));
2455         }
2456     }
2457 
onRotationTransactionReady( TestTransitionPlayer player, SurfaceControl.Transaction startTransaction)2458     private static SurfaceControl.TransactionCommittedListener onRotationTransactionReady(
2459             TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) {
2460         final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
2461                 ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
2462         player.onTransactionReady(startTransaction);
2463         verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
2464         return listenerCaptor.getValue();
2465     }
2466 }
2467