• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_APPLICATION_STARTING;
27 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
28 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
29 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
30 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
31 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
32 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
33 import static android.view.WindowManager.TRANSIT_CHANGE;
34 import static android.view.WindowManager.TRANSIT_CLOSE;
35 import static android.view.WindowManager.TRANSIT_OPEN;
36 import static android.view.WindowManager.TRANSIT_TO_BACK;
37 import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
38 import static android.window.TransitionInfo.FLAG_FILLS_TASK;
39 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
40 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
41 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
42 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
43 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
44 import static android.window.TransitionInfo.FLAG_SYNC;
45 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
46 import static android.window.TransitionInfo.isIndependent;
47 
48 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
49 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
50 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
51 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
52 import static com.android.server.wm.WindowContainer.POSITION_TOP;
53 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
54 
55 import static org.junit.Assert.assertEquals;
56 import static org.junit.Assert.assertFalse;
57 import static org.junit.Assert.assertNotEquals;
58 import static org.junit.Assert.assertNotNull;
59 import static org.junit.Assert.assertNull;
60 import static org.junit.Assert.assertTrue;
61 import static org.junit.Assume.assumeFalse;
62 import static org.junit.Assume.assumeTrue;
63 import static org.mockito.ArgumentMatchers.any;
64 import static org.mockito.ArgumentMatchers.anyBoolean;
65 import static org.mockito.ArgumentMatchers.anyInt;
66 import static org.mockito.ArgumentMatchers.anyString;
67 import static org.mockito.ArgumentMatchers.eq;
68 import static org.mockito.Mockito.clearInvocations;
69 import static org.mockito.Mockito.doAnswer;
70 import static org.mockito.Mockito.mock;
71 import static org.mockito.Mockito.never;
72 import static org.mockito.Mockito.spy;
73 import static org.mockito.Mockito.times;
74 import static org.mockito.Mockito.verify;
75 
76 import static java.lang.Integer.MAX_VALUE;
77 
78 import android.app.ActivityManager;
79 import android.content.res.Configuration;
80 import android.content.res.Resources;
81 import android.graphics.Color;
82 import android.graphics.Point;
83 import android.graphics.Rect;
84 import android.os.IBinder;
85 import android.platform.test.annotations.Presubmit;
86 import android.util.ArrayMap;
87 import android.util.ArraySet;
88 import android.view.SurfaceControl;
89 import android.view.WindowManager;
90 import android.window.IDisplayAreaOrganizer;
91 import android.window.IRemoteTransition;
92 import android.window.ITaskFragmentOrganizer;
93 import android.window.ITaskOrganizer;
94 import android.window.ITransitionPlayer;
95 import android.window.RemoteTransition;
96 import android.window.SystemPerformanceHinter;
97 import android.window.TaskFragmentAnimationParams;
98 import android.window.TaskFragmentOrganizer;
99 import android.window.TransitionInfo;
100 
101 import androidx.annotation.NonNull;
102 import androidx.test.filters.SmallTest;
103 
104 import com.android.internal.graphics.ColorUtils;
105 
106 import org.junit.Test;
107 import org.junit.runner.RunWith;
108 import org.mockito.ArgumentCaptor;
109 
110 import java.util.ArrayList;
111 import java.util.Objects;
112 import java.util.concurrent.CountDownLatch;
113 import java.util.concurrent.TimeUnit;
114 import java.util.function.Consumer;
115 import java.util.function.Function;
116 
117 /**
118  * Build/Install/Run:
119  *  atest WmTests:TransitionTests
120  */
121 @SmallTest
122 @Presubmit
123 @RunWith(WindowTestRunner.class)
124 public class TransitionTests extends WindowTestsBase {
125     final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class);
126     private BLASTSyncEngine mSyncEngine;
127     private Transition mTransition;
128     private TransitionInfo mInfo;
129 
createTestTransition(int transitType, TransitionController controller)130     private Transition createTestTransition(int transitType, TransitionController controller) {
131         final Transition transition = new Transition(transitType, 0 /* flags */, controller,
132                 controller.mSyncEngine);
133         spyOn(transition.mLogger);
134         doNothing().when(transition.mLogger).logOnSendAsync(any());
135         return transition;
136     }
137 
createTestTransition(int transitType)138     private Transition createTestTransition(int transitType) {
139         final TransitionController controller = new TestTransitionController(mAtm);
140 
141         mSyncEngine = createTestBLASTSyncEngine();
142         controller.setSyncEngine(mSyncEngine);
143         final Transition out = createTestTransition(transitType, controller);
144         out.startCollecting(0 /* timeoutMs */);
145         return out;
146     }
147 
148     @Test
testCreateInfo_NewTask()149     public void testCreateInfo_NewTask() {
150         final Transition transition = createTestTransition(TRANSIT_OPEN);
151         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
152         ArraySet<WindowContainer> participants = transition.mParticipants;
153 
154         final Task newTask = createTask(mDisplayContent);
155         final Task oldTask = createTask(mDisplayContent);
156         final ActivityRecord closing = createActivityRecord(oldTask);
157         final ActivityRecord opening = createActivityRecord(newTask);
158         // Start states.
159         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
160         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */));
161         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
162         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
163         fillChangeMap(changes, newTask);
164         // End states.
165         closing.setVisibleRequested(false);
166         opening.setVisibleRequested(true);
167 
168         final int transit = transition.mType;
169         int flags = 0;
170 
171         // Check basic both tasks participating
172         participants.add(oldTask);
173         participants.add(newTask);
174         ArrayList<Transition.ChangeInfo> targets =
175                 Transition.calculateTargets(participants, changes);
176         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
177         assertEquals(2, info.getChanges().size());
178         assertEquals(transit, info.getType());
179 
180         // Check that children are pruned
181         participants.add(opening);
182         participants.add(closing);
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         // Check combined prune and promote
190         participants.remove(newTask);
191         targets = Transition.calculateTargets(participants, changes);
192         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
193         assertEquals(2, info.getChanges().size());
194         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
195         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
196 
197         // Check multi promote
198         participants.remove(oldTask);
199         targets = Transition.calculateTargets(participants, changes);
200         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
201         assertEquals(2, info.getChanges().size());
202         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
203         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
204     }
205 
206     @Test
testCreateInfo_Activity()207     public void testCreateInfo_Activity() {
208         final Transition transition = createTestTransition(TRANSIT_OPEN);
209         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
210         ArraySet<WindowContainer> participants = transition.mParticipants;
211 
212         final Task theTask = createTask(mDisplayContent);
213         final ActivityRecord closing = createActivityRecord(theTask);
214         final ActivityRecord opening = createActivityRecord(theTask);
215         // Start states.
216         changes.put(theTask, new Transition.ChangeInfo(theTask, true /* vis */, false /* exChg */));
217         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
218         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
219         fillChangeMap(changes, theTask);
220         // End states.
221         closing.setVisibleRequested(false);
222         opening.setVisibleRequested(true);
223 
224         final int transit = transition.mType;
225         int flags = 0;
226 
227         participants.add(opening);
228         participants.add(closing);
229         ArrayList<Transition.ChangeInfo> targets =
230                 Transition.calculateTargets(participants, changes);
231         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
232         assertEquals(2, info.getChanges().size());
233         assertEquals(info.getChanges().get(1).getActivityComponent(), closing.mActivityComponent);
234     }
235 
236     @Test
testCreateInfo_NestedTasks()237     public void testCreateInfo_NestedTasks() {
238         final Transition transition = createTestTransition(TRANSIT_OPEN);
239         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
240         ArraySet<WindowContainer> participants = transition.mParticipants;
241 
242         final Task newTask = createTask(mDisplayContent);
243         final Task newNestedTask = createTaskInRootTask(newTask, 0);
244         final Task newNestedTask2 = createTaskInRootTask(newTask, 0);
245         final Task oldTask = createTask(mDisplayContent);
246         final ActivityRecord closing = createActivityRecord(oldTask);
247         final ActivityRecord opening = createActivityRecord(newNestedTask);
248         final ActivityRecord opening2 = createActivityRecord(newNestedTask2);
249         // Start states.
250         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
251         changes.put(newNestedTask,
252                 new Transition.ChangeInfo(newNestedTask, false /* vis */, true /* exChg */));
253         changes.put(newNestedTask2,
254                 new Transition.ChangeInfo(newNestedTask2, false /* vis */, true /* exChg */));
255         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */));
256         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
257         changes.put(opening2,
258                 new Transition.ChangeInfo(opening2, false /* vis */, true /* exChg */));
259         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
260         fillChangeMap(changes, newTask);
261         // End states.
262         closing.setVisibleRequested(false);
263         opening.setVisibleRequested(true);
264         opening2.setVisibleRequested(true);
265 
266         final int transit = transition.mType;
267         int flags = 0;
268 
269         // Check full promotion from leaf
270         participants.add(oldTask);
271         participants.add(opening);
272         participants.add(opening2);
273         ArrayList<Transition.ChangeInfo> targets =
274                 Transition.calculateTargets(participants, changes);
275         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
276         assertEquals(2, info.getChanges().size());
277         assertEquals(transit, info.getType());
278         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
279         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
280 
281         // Check that unchanging but visible descendant of sibling prevents promotion
282         participants.remove(opening2);
283         targets = Transition.calculateTargets(participants, changes);
284         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
285         assertEquals(2, info.getChanges().size());
286         assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken()));
287         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
288     }
289 
290     @Test
testCreateInfo_DisplayArea()291     public void testCreateInfo_DisplayArea() {
292         assumeTrue(mDisplayContent.mTransitionController.useShellTransitionsRotation());
293 
294         final Transition transition = createTestTransition(TRANSIT_OPEN);
295         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
296         ArraySet<WindowContainer> participants = transition.mParticipants;
297         final Task showTask = createTask(mDisplayContent);
298         final Task showNestedTask = createTaskInRootTask(showTask, 0);
299         final Task showTask2 = createTask(mDisplayContent);
300         final DisplayArea tda = showTask.getDisplayArea();
301         final ActivityRecord showing = createActivityRecord(showNestedTask);
302         final ActivityRecord showing2 = createActivityRecord(showTask2);
303         // Start states.
304         changes.put(showTask,
305                 new Transition.ChangeInfo(showTask, false /* vis */, true /* exChg */));
306         changes.put(showNestedTask,
307                 new Transition.ChangeInfo(showNestedTask, false /* vis */, true /* exChg */));
308         changes.put(showTask2,
309                 new Transition.ChangeInfo(showTask2, false /* vis */, true /* exChg */));
310         changes.put(tda, new Transition.ChangeInfo(tda, false /* vis */, true /* exChg */));
311         changes.put(showing, new Transition.ChangeInfo(showing, false /* vis */, true /* exChg */));
312         changes.put(showing2,
313                 new Transition.ChangeInfo(showing2, false /* vis */, true /* exChg */));
314         fillChangeMap(changes, tda);
315 
316         // End states.
317         showing.setVisibleRequested(true);
318         showing2.setVisibleRequested(true);
319 
320         final int transit = transition.mType;
321         int flags = 0;
322 
323         // Check promotion to DisplayArea
324         participants.add(showing);
325         participants.add(showing2);
326         ArrayList<Transition.ChangeInfo> targets =
327                 Transition.calculateTargets(participants, changes);
328         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
329         assertEquals(1, info.getChanges().size());
330         assertEquals(transit, info.getType());
331         assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
332 
333         // Check that organized tasks get reported even if not top
334         makeTaskOrganized(showTask);
335         targets = Transition.calculateTargets(participants, changes);
336         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
337         assertEquals(2, info.getChanges().size());
338         assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
339         assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken()));
340         // Even if DisplayArea explicitly participating
341         participants.add(tda);
342         targets = Transition.calculateTargets(participants, changes);
343         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
344         assertEquals(2, info.getChanges().size());
345     }
346 
347     @Test
testCreateInfo_existenceChange()348     public void testCreateInfo_existenceChange() {
349         final Transition transition = createTestTransition(TRANSIT_OPEN);
350 
351         final Task openTask = createTask(mDisplayContent);
352         final ActivityRecord opening = createActivityRecord(openTask);
353         opening.setVisibleRequested(false); // starts invisible
354         final Task closeTask = createTask(mDisplayContent);
355         final ActivityRecord closing = createActivityRecord(closeTask);
356         closing.setVisibleRequested(true); // starts visible
357 
358         transition.collectExistenceChange(openTask);
359         transition.collect(opening);
360         transition.collect(closing);
361         opening.setVisibleRequested(true);
362         closing.setVisibleRequested(false);
363 
364         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
365                 transition.mParticipants, transition.mChanges);
366         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
367         assertEquals(2, info.getChanges().size());
368         // There was an existence change on open, so it should be OPEN rather than SHOW
369         assertEquals(TRANSIT_OPEN,
370                 info.getChange(openTask.mRemoteToken.toWindowContainerToken()).getMode());
371         // No exestence change on closing, so HIDE rather than CLOSE
372         assertEquals(TRANSIT_TO_BACK,
373                 info.getChange(closeTask.mRemoteToken.toWindowContainerToken()).getMode());
374     }
375 
376     @Test
testCreateInfo_ordering()377     public void testCreateInfo_ordering() {
378         final Transition transition = createTestTransition(TRANSIT_OPEN);
379         // pick some number with a high enough chance of being out-of-order when added to set.
380         final int taskCount = 6;
381 
382         final Task[] tasks = new Task[taskCount];
383         for (int i = 0; i < taskCount; ++i) {
384             // Each add goes on top, so at the end of this, task[9] should be on top
385             tasks[i] = createTask(mDisplayContent,
386                     WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
387             final ActivityRecord act = createActivityRecord(tasks[i]);
388             // alternate so that the transition doesn't get promoted to the display area
389             act.setVisibleRequested((i % 2) == 0); // starts invisible
390         }
391 
392         // doesn't matter which order collected since participants is a set
393         for (int i = 0; i < taskCount; ++i) {
394             transition.collectExistenceChange(tasks[i]);
395             final ActivityRecord act = tasks[i].getTopMostActivity();
396             transition.collect(act);
397             tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0);
398         }
399 
400         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
401                 transition.mParticipants, transition.mChanges);
402         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
403         assertEquals(taskCount, info.getChanges().size());
404         // verify order is top-to-bottem
405         for (int i = 0; i < taskCount; ++i) {
406             assertEquals(tasks[taskCount - i - 1].mRemoteToken.toWindowContainerToken(),
407                     info.getChanges().get(i).getContainer());
408         }
409     }
410 
411     @Test
testCreateInfo_wallpaper()412     public void testCreateInfo_wallpaper() {
413         final Transition transition = createTestTransition(TRANSIT_OPEN);
414         // pick some number with a high enough chance of being out-of-order when added to set.
415         final int taskCount = 4;
416         final int showWallpaperTask = 2;
417 
418         final Task[] tasks = new Task[taskCount];
419         for (int i = 0; i < taskCount; ++i) {
420             // Each add goes on top, so at the end of this, task[9] should be on top
421             tasks[i] = createTask(mDisplayContent,
422                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
423             final ActivityRecord act = createActivityRecord(tasks[i]);
424             // alternate so that the transition doesn't get promoted to the display area
425             act.setVisibleRequested((i % 2) == 0); // starts invisible
426             if (i == showWallpaperTask) {
427                 doReturn(true).when(act).showWallpaper();
428             }
429         }
430 
431         final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm,
432                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */));
433         final WindowState wallpaperWindow = newWindowBuilder("wallpaperWindow",
434                 TYPE_WALLPAPER).setWindowToken(wallpaperWindowToken).build();
435         wallpaperWindowToken.setVisibleRequested(false);
436         transition.collect(wallpaperWindowToken);
437         wallpaperWindowToken.setVisibleRequested(true);
438         wallpaperWindow.mHasSurface = true;
439 
440         // doesn't matter which order collected since participants is a set
441         for (int i = 0; i < taskCount; ++i) {
442             transition.collectExistenceChange(tasks[i]);
443             final ActivityRecord act = tasks[i].getTopMostActivity();
444             transition.collect(act);
445             tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0);
446         }
447 
448         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
449                 transition.mParticipants, transition.mChanges);
450         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
451         // verify that wallpaper is at bottom
452         assertEquals(taskCount + 1, info.getChanges().size());
453         // The wallpaper is not organized, so it won't have a token; however, it will be marked
454         // as IS_WALLPAPER
455         assertEquals(FLAG_IS_WALLPAPER,
456                 info.getChanges().get(info.getChanges().size() - 1).getFlags());
457         assertEquals(FLAG_SHOW_WALLPAPER, info.getChange(
458                 tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags());
459     }
460 
461     @Test
testCreateInfo_PromoteSimilarClose()462     public void testCreateInfo_PromoteSimilarClose() {
463         final Transition transition = createTestTransition(TRANSIT_CLOSE);
464         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
465         ArraySet<WindowContainer> participants = transition.mParticipants;
466 
467         final Task topTask = createTask(mDisplayContent);
468         final Task belowTask = createTask(mDisplayContent);
469         final ActivityRecord showing = createActivityRecord(belowTask);
470         final ActivityRecord hiding = createActivityRecord(topTask);
471         final ActivityRecord closing = createActivityRecord(topTask);
472         // Start states.
473         changes.put(topTask, new Transition.ChangeInfo(topTask, true /* vis */, false /* exChg */));
474         changes.put(belowTask,
475                 new Transition.ChangeInfo(belowTask, false /* vis */, false /* exChg */));
476         changes.put(showing,
477                 new Transition.ChangeInfo(showing, false /* vis */, false /* exChg */));
478         changes.put(hiding, new Transition.ChangeInfo(hiding, true /* vis */, false /* exChg */));
479         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
480         fillChangeMap(changes, topTask);
481         // End states.
482         showing.setVisibleRequested(true);
483         closing.setVisibleRequested(false);
484         hiding.setVisibleRequested(false);
485 
486         participants.add(belowTask);
487         participants.add(hiding);
488         participants.add(closing);
489         ArrayList<Transition.ChangeInfo> targets =
490                 Transition.calculateTargets(participants, changes);
491         assertEquals(2, targets.size());
492         assertTrue(Transition.containsChangeFor(belowTask, targets));
493         assertTrue(Transition.containsChangeFor(topTask, targets));
494     }
495 
496     @Test
testCreateInfo_PromoteSimilarOpen()497     public void testCreateInfo_PromoteSimilarOpen() {
498         final Transition transition = createTestTransition(TRANSIT_OPEN);
499         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
500         ArraySet<WindowContainer> participants = transition.mParticipants;
501 
502         final Task topTask = createTask(mDisplayContent);
503         final Task belowTask = createTask(mDisplayContent);
504         final ActivityRecord showing = createActivityRecord(topTask);
505         final ActivityRecord opening = createActivityRecord(topTask);
506         final ActivityRecord closing = createActivityRecord(belowTask);
507         // Start states.
508         changes.put(topTask,
509                 new Transition.ChangeInfo(topTask, false /* vis */, false /* exChg */));
510         changes.put(belowTask,
511                 new Transition.ChangeInfo(belowTask, true /* vis */, false /* exChg */));
512         changes.put(showing,
513                 new Transition.ChangeInfo(showing, false /* vis */, false /* exChg */));
514         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
515         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
516         fillChangeMap(changes, topTask);
517         // End states.
518         showing.setVisibleRequested(true);
519         opening.setVisibleRequested(true);
520         closing.setVisibleRequested(false);
521 
522         participants.add(belowTask);
523         participants.add(showing);
524         participants.add(opening);
525         ArrayList<Transition.ChangeInfo> targets =
526                 Transition.calculateTargets(participants, changes);
527         assertEquals(2, targets.size());
528         assertTrue(Transition.containsChangeFor(belowTask, targets));
529         assertTrue(Transition.containsChangeFor(topTask, targets));
530     }
531 
532     @Test
testCreateInfo_NoAnimation()533     public void testCreateInfo_NoAnimation() {
534         final Transition transition = createTestTransition(TRANSIT_OPEN);
535         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
536         ArraySet<WindowContainer> participants = transition.mParticipants;
537 
538         final Task newTask = createTask(mDisplayContent);
539         final Task oldTask = createTask(mDisplayContent);
540         final ActivityRecord closing = createActivityRecord(oldTask);
541         final ActivityRecord opening = createActivityRecord(newTask);
542         // Start states.
543         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
544         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */));
545         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
546         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */));
547         transition.setNoAnimation(opening);
548         fillChangeMap(changes, newTask);
549         // End states.
550         closing.setVisibleRequested(false);
551         opening.setVisibleRequested(true);
552 
553         final int transit = transition.mType;
554         int flags = 0;
555 
556         // Check that no-animation flag is promoted
557         participants.add(oldTask);
558         participants.add(newTask);
559         participants.add(opening);
560         participants.add(closing);
561         ArrayList<Transition.ChangeInfo> targets =
562                 Transition.calculateTargets(participants, changes);
563         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
564         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
565         assertTrue(info.getChange(newTask.mRemoteToken.toWindowContainerToken())
566                 .hasFlags(TransitionInfo.FLAG_NO_ANIMATION));
567 
568         // Check that no-animation flag is NOT promoted if at-least on child *is* animated
569         final ActivityRecord opening2 = createActivityRecord(newTask);
570         changes.put(opening2,
571                 new Transition.ChangeInfo(opening2, false /* vis */, true /* exChg */));
572         participants.add(opening2);
573         targets = Transition.calculateTargets(participants, changes);
574         info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
575         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
576         assertFalse(info.getChange(newTask.mRemoteToken.toWindowContainerToken())
577                 .hasFlags(TransitionInfo.FLAG_NO_ANIMATION));
578     }
579 
580     @Test
testCreateInfo_MultiDisplay()581     public void testCreateInfo_MultiDisplay() {
582         DisplayContent otherDisplay = createNewDisplay();
583         final Transition transition = createTestTransition(TRANSIT_OPEN);
584         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
585         ArraySet<WindowContainer> participants = transition.mParticipants;
586 
587         final Task display0Task = createTask(mDisplayContent);
588         final Task display1Task = createTask(otherDisplay);
589         // Start states.
590         changes.put(display0Task,
591                 new Transition.ChangeInfo(display0Task, false /* vis */, true /* exChg */));
592         changes.put(display1Task,
593                 new Transition.ChangeInfo(display1Task, false /* vis */, true /* exChg */));
594         fillChangeMap(changes, display0Task);
595         fillChangeMap(changes, display1Task);
596         // End states.
597         display0Task.setVisibleRequested(true);
598         display1Task.setVisibleRequested(true);
599 
600         final int transit = transition.mType;
601         int flags = 0;
602 
603         participants.add(display0Task);
604         participants.add(display1Task);
605         ArrayList<Transition.ChangeInfo> targets =
606                 Transition.calculateTargets(participants, changes);
607         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
608         assertEquals(2, info.getRootCount());
609         // Check that the changes are assigned to the correct display
610         assertEquals(mDisplayContent.getDisplayId(), info.getChange(
611                 display0Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId());
612         assertEquals(otherDisplay.getDisplayId(), info.getChange(
613                 display1Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId());
614         // Check that roots can be found by display and have the correct display
615         assertEquals(mDisplayContent.getDisplayId(),
616                 info.getRoot(info.findRootIndex(mDisplayContent.getDisplayId())).getDisplayId());
617         assertEquals(otherDisplay.getDisplayId(),
618                 info.getRoot(info.findRootIndex(otherDisplay.getDisplayId())).getDisplayId());
619     }
620 
621     @Test
testTargets_noIntermediatesToWallpaper()622     public void testTargets_noIntermediatesToWallpaper() {
623         final Transition transition = createTestTransition(TRANSIT_OPEN);
624 
625         final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
626                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
627         // Make DA organized so we can check that they don't get included.
628         WindowContainer parent = wallpaperWindowToken.getParent();
629         makeDisplayAreaOrganized(parent, mDisplayContent);
630         final WindowState wallpaperWindow = newWindowBuilder("wallpaperWindow",
631                 TYPE_WALLPAPER).setWindowToken(wallpaperWindowToken).build();
632         wallpaperWindowToken.setVisibleRequested(false);
633         transition.collect(wallpaperWindowToken);
634         wallpaperWindowToken.setVisibleRequested(true);
635         wallpaperWindow.mHasSurface = true;
636         doReturn(true).when(mDisplayContent).isAttached();
637         transition.collect(mDisplayContent);
638         assertFalse("The change of non-interesting window container should be skipped",
639                 transition.mChanges.containsKey(mDisplayContent.getParent()));
640         mDisplayContent.getWindowConfiguration().setRotation(
641                 (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
642 
643         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
644                 transition.mParticipants, transition.mChanges);
645         TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT);
646         // The wallpaper is not organized, so it won't have a token; however, it will be marked
647         // as IS_WALLPAPER
648         assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
649         // Make sure no intermediate display areas were pulled in between wallpaper and display.
650         assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(),
651                 info.getChanges().get(0).getParent());
652     }
653 
654     @Test
testRunningRemoteTransition()655     public void testRunningRemoteTransition() {
656         final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
657                 mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
658         final WindowProcessController playerProc = mSystemServicesTestRule.addProcess(
659                 "pkg.player", "proc.player", 5000 /* pid */, 5000 /* uid */);
660         testPlayer.mController.registerTransitionPlayer(testPlayer, playerProc);
661         doReturn(mock(IBinder.class)).when(playerProc.getThread()).asBinder();
662         final WindowProcessController delegateProc = mSystemServicesTestRule.addProcess(
663                 "pkg.delegate", "proc.delegate", 6000 /* pid */, 6000 /* uid */);
664         doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder();
665         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true)
666                 .setVisible(false).build();
667         final Task task = app.getTask();
668         task.setTaskOrganizer(mock(ITaskOrganizer.class), true /* skipTaskAppeared */);
669         app.setVisibleRequested(true);
670         final TransitionController controller = app.mTransitionController;
671         final Transition transition = controller.createTransition(TRANSIT_OPEN);
672         final RemoteTransition remoteTransition = new RemoteTransition(
673                 mock(IRemoteTransition.class));
674         remoteTransition.setAppThread(delegateProc.getThread());
675         transition.collect(app);
676         controller.requestStartTransition(transition, null /* startTask */, remoteTransition,
677                 null /* displayChange */);
678         assertTrue(delegateProc.isRunningRemoteTransition());
679         testPlayer.startTransition();
680         app.onStartingWindowDrawn();
681         // The task appeared event should be deferred until transition ready.
682         assertFalse(task.taskAppearedReady());
683         testPlayer.onTransactionReady();
684         assertTrue(task.taskAppearedReady());
685         assertTrue(playerProc.isRunningRemoteTransition());
686         assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
687         assertTrue(app.isVisible());
688 
689         testPlayer.finish();
690         assertFalse(playerProc.isRunningRemoteTransition());
691         assertFalse(delegateProc.isRunningRemoteTransition());
692         assertFalse(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
693     }
694 
695     @Test
testOpenActivityInTheSameTaskWithDisplayChange()696     public void testOpenActivityInTheSameTaskWithDisplayChange() {
697         final ActivityRecord closing = createActivityRecord(mDisplayContent);
698         closing.setVisibleRequested(true);
699         final Task task = closing.getTask();
700         makeTaskOrganized(task);
701         final ActivityRecord opening = createActivityRecord(task);
702         opening.setVisibleRequested(false);
703         makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent);
704         final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent };
705         final Transition transition = createTestTransition(TRANSIT_OPEN);
706         for (WindowContainer<?> wc : wcs) {
707             transition.collect(wc);
708         }
709         closing.setVisibleRequested(false);
710         opening.setVisibleRequested(true);
711         final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1;
712         for (WindowContainer<?> wc : wcs) {
713             wc.getWindowConfiguration().setRotation(newRotation);
714         }
715 
716         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
717                 transition.mParticipants, transition.mChanges);
718         // Especially the activities must be in the targets.
719         for (WindowContainer<?> wc : wcs) {
720             assertTrue(Transition.containsChangeFor(wc, targets));
721         }
722     }
723 
724     @Test
testIndependent()725     public void testIndependent() {
726         final Transition transition = createTestTransition(TRANSIT_OPEN);
727         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
728         ArraySet<WindowContainer> participants = transition.mParticipants;
729 
730         final Task openTask = createTask(mDisplayContent);
731         final Task openInOpenTask = createTaskInRootTask(openTask, 0);
732         final ActivityRecord openInOpen = createActivityRecord(openInOpenTask);
733 
734         final Task changeTask = createTask(mDisplayContent);
735         final Task changeInChangeTask = createTaskInRootTask(changeTask, 0);
736         final Task openInChangeTask = createTaskInRootTask(changeTask, 0);
737         final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
738         final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
739         // set organizer for everything so that they all get added to transition info
740         makeTaskOrganized(openTask, openInOpenTask, changeTask, changeInChangeTask,
741                 openInChangeTask);
742 
743         // Start states.
744         changes.put(openTask,
745                 new Transition.ChangeInfo(openTask, false /* vis */, true /* exChg */));
746         changes.put(changeTask,
747                 new Transition.ChangeInfo(changeTask, true /* vis */, false /* exChg */));
748         changes.put(openInOpenTask,
749                 new Transition.ChangeInfo(openInOpenTask, false /* vis */, true /* exChg */));
750         changes.put(openInChangeTask,
751                 new Transition.ChangeInfo(openInChangeTask, false /* vis */, true /* exChg */));
752         changes.put(changeInChangeTask,
753                 new Transition.ChangeInfo(changeInChangeTask, true /* vis */, false /* exChg */));
754         changes.put(openInOpen,
755                 new Transition.ChangeInfo(openInOpen, false /* vis */, true /* exChg */));
756         changes.put(openInChange,
757                 new Transition.ChangeInfo(openInChange, false /* vis */, true /* exChg */));
758         changes.put(changeInChange,
759                 new Transition.ChangeInfo(changeInChange, true /* vis */, false /* exChg */));
760         fillChangeMap(changes, openTask);
761         // End states.
762         changeInChange.setVisibleRequested(true);
763         openInOpen.setVisibleRequested(true);
764         openInChange.setVisibleRequested(true);
765         // Force the change-type changes to be "dirty" so they aren't skipped
766         changes.get(changeTask).mKnownConfigChanges = 1;
767         changes.get(changeInChangeTask).mKnownConfigChanges = 1;
768         changes.get(changeInChange).mKnownConfigChanges = 1;
769 
770         final int transit = transition.mType;
771         int flags = 0;
772 
773         // Check full promotion from leaf
774         participants.add(changeInChange);
775         participants.add(openInOpen);
776         participants.add(openInChange);
777         // Explicitly add changeTask (to test independence with parents)
778         participants.add(changeTask);
779         final ArrayList<Transition.ChangeInfo> targets =
780                 Transition.calculateTargets(participants, changes);
781         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
782         // Root changes should always be considered independent
783         assertTrue(isIndependent(
784                 info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info));
785         assertTrue(isIndependent(
786                 info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info));
787 
788         // Children of a open/close change are not independent
789         assertFalse(isIndependent(
790                 info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info));
791 
792         // Non-root changes are not independent
793         assertFalse(isIndependent(
794                 info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info));
795 
796         // open/close within a change are independent
797         assertTrue(isIndependent(
798                 info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
799     }
800 
801     @Test
testOpenOpaqueTask()802     public void testOpenOpaqueTask() {
803         final Transition transition = createTestTransition(TRANSIT_OPEN);
804         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
805         ArraySet<WindowContainer> participants = transition.mParticipants;
806 
807         final Task oldTask = createTask(mDisplayContent);
808         final Task newTask = createTask(mDisplayContent);
809 
810         final ActivityRecord closing = createActivityRecord(oldTask);
811         closing.setOccludesParent(true);
812         final ActivityRecord opening = createActivityRecord(newTask);
813         opening.setOccludesParent(true);
814         // Start states.
815         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
816         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
817         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
818         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
819         fillChangeMap(changes, newTask);
820         // End states.
821         closing.setVisibleRequested(false);
822         opening.setVisibleRequested(true);
823 
824         final int transit = transition.mType;
825         int flags = 0;
826 
827         // Check basic both tasks participating
828         participants.add(oldTask);
829         participants.add(newTask);
830         ArrayList<Transition.ChangeInfo> targets =
831                 Transition.calculateTargets(participants, changes);
832         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
833         assertEquals(2, info.getChanges().size());
834         assertEquals(transit, info.getType());
835 
836         assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
837         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
838     }
839 
840     @Test
testOpenTranslucentTask()841     public void testOpenTranslucentTask() {
842         final Transition transition = createTestTransition(TRANSIT_OPEN);
843         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
844         ArraySet<WindowContainer> participants = transition.mParticipants;
845 
846         final Task oldTask = createTask(mDisplayContent);
847         final Task newTask = createTask(mDisplayContent);
848 
849         final ActivityRecord closing = createActivityRecord(oldTask);
850         closing.setOccludesParent(true);
851         final ActivityRecord opening = createActivityRecord(newTask);
852         opening.setOccludesParent(false);
853         // Start states.
854         changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
855         changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
856         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
857         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
858         fillChangeMap(changes, newTask);
859         // End states.
860         closing.setVisibleRequested(false);
861         opening.setVisibleRequested(true);
862 
863         final int transit = transition.mType;
864         int flags = 0;
865 
866         // Check basic both tasks participating
867         participants.add(oldTask);
868         participants.add(newTask);
869         ArrayList<Transition.ChangeInfo> targets =
870                 Transition.calculateTargets(participants, changes);
871         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
872         assertEquals(2, info.getChanges().size());
873         assertEquals(transit, info.getType());
874 
875         assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
876         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
877     }
878 
879     @Test
testOpenOpaqueTaskFragment()880     public void testOpenOpaqueTaskFragment() {
881         final Transition transition = createTestTransition(TRANSIT_OPEN);
882         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
883         ArraySet<WindowContainer> participants = transition.mParticipants;
884 
885         final Task task = createTask(mDisplayContent);
886         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
887         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
888 
889         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
890         closing.setOccludesParent(true);
891         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
892         opening.setOccludesParent(true);
893         // Start states.
894         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
895                 false /* vis */, true /* exChg */));
896         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
897                 true /* vis */, false /* exChg */));
898         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
899         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
900         fillChangeMap(changes, openingTaskFragment);
901         // End states.
902         closing.setVisibleRequested(false);
903         opening.setVisibleRequested(true);
904 
905         final int transit = transition.mType;
906         int flags = 0;
907 
908         // Check basic both tasks participating
909         participants.add(closingTaskFragment);
910         participants.add(openingTaskFragment);
911         ArrayList<Transition.ChangeInfo> targets =
912                 Transition.calculateTargets(participants, changes);
913         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
914         assertEquals(2, info.getChanges().size());
915         assertEquals(transit, info.getType());
916 
917         assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
918         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
919     }
920 
921     @Test
testOpenTranslucentTaskFragment()922     public void testOpenTranslucentTaskFragment() {
923         final Transition transition = createTestTransition(TRANSIT_OPEN);
924         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
925         ArraySet<WindowContainer> participants = transition.mParticipants;
926 
927         final Task task = createTask(mDisplayContent);
928         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
929         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
930 
931         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
932         closing.setOccludesParent(true);
933         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
934         opening.setOccludesParent(false);
935         // Start states.
936         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
937                 false /* vis */, true /* exChg */));
938         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
939                 true /* vis */, false /* exChg */));
940         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
941         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
942         fillChangeMap(changes, openingTaskFragment);
943         // End states.
944         closing.setVisibleRequested(false);
945         opening.setVisibleRequested(true);
946 
947         final int transit = transition.mType;
948         int flags = 0;
949 
950         // Check basic both tasks participating
951         participants.add(closingTaskFragment);
952         participants.add(openingTaskFragment);
953         ArrayList<Transition.ChangeInfo> targets =
954                 Transition.calculateTargets(participants, changes);
955         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
956         assertEquals(2, info.getChanges().size());
957         assertEquals(transit, info.getType());
958 
959         assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
960         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
961     }
962 
963     @Test
testCloseOpaqueTaskFragment_withFinishingActivity()964     public void testCloseOpaqueTaskFragment_withFinishingActivity() {
965         final Transition transition = createTestTransition(TRANSIT_CLOSE);
966         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
967         ArraySet<WindowContainer> participants = transition.mParticipants;
968 
969         final Task task = createTask(mDisplayContent);
970         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
971         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
972 
973         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
974         opening.setOccludesParent(true);
975         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
976         closing.setOccludesParent(true);
977         closing.finishing = true;
978         // Start states.
979         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
980                 false /* vis */, true /* exChg */));
981         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
982                 true /* vis */, false /* exChg */));
983         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
984         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
985         fillChangeMap(changes, openingTaskFragment);
986         // End states.
987         closing.setVisibleRequested(false);
988         opening.setVisibleRequested(true);
989 
990         final int transit = transition.mType;
991         int flags = 0;
992 
993         // Check basic both tasks participating
994         participants.add(closingTaskFragment);
995         participants.add(openingTaskFragment);
996         ArrayList<Transition.ChangeInfo> targets =
997                 Transition.calculateTargets(participants, changes);
998         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
999         assertEquals(2, info.getChanges().size());
1000         assertEquals(transit, info.getType());
1001 
1002         assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
1003         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
1004     }
1005 
1006     @Test
testCloseTranslucentTaskFragment_withFinishingActivity()1007     public void testCloseTranslucentTaskFragment_withFinishingActivity() {
1008         final Transition transition = createTestTransition(TRANSIT_CLOSE);
1009         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1010         ArraySet<WindowContainer> participants = transition.mParticipants;
1011 
1012         final Task task = createTask(mDisplayContent);
1013         final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task);
1014         final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task);
1015 
1016         final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
1017         opening.setOccludesParent(true);
1018         final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
1019         closing.setOccludesParent(false);
1020         closing.finishing = true;
1021         // Start states.
1022         changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
1023                 false /* vis */, true /* exChg */));
1024         changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment,
1025                 true /* vis */, false /* exChg */));
1026         changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */));
1027         changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */));
1028         fillChangeMap(changes, openingTaskFragment);
1029         // End states.
1030         closing.setVisibleRequested(false);
1031         opening.setVisibleRequested(true);
1032 
1033         final int transit = transition.mType;
1034         int flags = 0;
1035 
1036         // Check basic both tasks participating
1037         participants.add(closingTaskFragment);
1038         participants.add(openingTaskFragment);
1039         ArrayList<Transition.ChangeInfo> targets =
1040                 Transition.calculateTargets(participants, changes);
1041         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT);
1042         assertEquals(2, info.getChanges().size());
1043         assertEquals(transit, info.getType());
1044 
1045         assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT));
1046         assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT));
1047     }
1048 
1049     @Test
testTimeout()1050     public void testTimeout() {
1051         final TransitionController controller = new TestTransitionController(mAtm);
1052         final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
1053         final CountDownLatch latch = new CountDownLatch(1);
1054         // When the timeout is reached, it will finish the sync-group and notify transaction ready.
1055         final Transition t = new Transition(TRANSIT_OPEN, 0 /* flags */, controller, sync) {
1056             @Override
1057             public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
1058                 latch.countDown();
1059             }
1060         };
1061         t.startCollecting(10 /* timeoutMs */);
1062         assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS)));
1063     }
1064 
1065     @Test
testTransitionBounds()1066     public void testTransitionBounds() {
1067         registerTestTransitionPlayer();
1068         final int offset = 10;
1069         final Function<WindowContainer<?>, TransitionInfo.Change> test = wc -> {
1070             final Transition transition = wc.mTransitionController.createTransition(TRANSIT_OPEN);
1071             transition.collect(wc);
1072             final int nextRotation = (wc.getWindowConfiguration().getRotation() + 1) % 4;
1073             wc.getWindowConfiguration().setRotation(nextRotation);
1074             wc.getWindowConfiguration().setDisplayRotation(nextRotation);
1075             final Rect bounds = wc.getWindowConfiguration().getBounds();
1076             // Flip the bounds with offset.
1077             wc.getWindowConfiguration().setBounds(
1078                     new Rect(offset, offset, bounds.height(), bounds.width()));
1079             final int flags = 0;
1080             final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, flags,
1081                     Transition.calculateTargets(transition.mParticipants, transition.mChanges),
1082                     mMockT);
1083             transition.abort();
1084             return info.getChanges().get(0);
1085         };
1086 
1087         final ActivityRecord app = createActivityRecord(mDisplayContent);
1088         final TransitionInfo.Change changeOfActivity = test.apply(app);
1089         // There will be letterbox if the activity bounds don't match parent, so always use its
1090         // parent bounds for animation.
1091         assertEquals(app.getParent().getBounds(), changeOfActivity.getEndAbsBounds());
1092         final int endRotation = app.mTransitionController.useShellTransitionsRotation()
1093                 ? app.getWindowConfiguration().getRotation()
1094                 // Without shell rotation, fixed rotation is done by core so the info should not
1095                 // contain rotation change.
1096                 : app.getParent().getWindowConfiguration().getRotation();
1097         assertEquals(endRotation, changeOfActivity.getEndRotation());
1098 
1099         // Non-activity target always uses its configuration for end info.
1100         final Task task = app.getTask();
1101         final TransitionInfo.Change changeOfTask = test.apply(task);
1102         assertEquals(task.getBounds(), changeOfTask.getEndAbsBounds());
1103         assertEquals(new Point(offset, offset), changeOfTask.getEndRelOffset());
1104         assertEquals(task.getWindowConfiguration().getRotation(), changeOfTask.getEndRotation());
1105     }
1106 
1107     @Test
testDisplayRotationChange()1108     public void testDisplayRotationChange() {
1109         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
1110         spyOn(displayPolicy);
1111         // Simulate gesture navigation (non-movable) so it is not seamless.
1112         doReturn(false).when(displayPolicy).navigationBarCanMove();
1113         final Task task = createActivityRecord(mDisplayContent).getTask();
1114         final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
1115         final WindowState navBar = newWindowBuilder("navBar", TYPE_NAVIGATION_BAR).build();
1116         final WindowState ime = newWindowBuilder("ime", TYPE_INPUT_METHOD).build();
1117         final WindowToken decorToken = new WindowToken.Builder(mWm, mock(IBinder.class),
1118                 TYPE_NAVIGATION_BAR_PANEL).setDisplayContent(mDisplayContent)
1119                 .setRoundedCornerOverlay(true).build();
1120         final WindowState screenDecor = newWindowBuilder("screenDecor",
1121                 decorToken.windowType).setWindowToken(decorToken).build();
1122         final WindowState[] windows = {statusBar, navBar, ime, screenDecor};
1123         makeWindowVisible(windows);
1124         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
1125         mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
1126         mDisplayContent.mTransitionController.setSyncEngine(createTestBLASTSyncEngine());
1127         final TestTransitionPlayer player = registerTestTransitionPlayer();
1128 
1129         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
1130         mDisplayContent.setLastHasContent();
1131         mDisplayContent.requestChangeTransition(1 /* any changes */, null /* displayChange */);
1132         assertEquals(WindowContainer.SYNC_STATE_NONE, statusBar.mSyncState);
1133         assertEquals(WindowContainer.SYNC_STATE_NONE, navBar.mSyncState);
1134         assertEquals(WindowContainer.SYNC_STATE_NONE, screenDecor.mSyncState);
1135         assertEquals(WindowContainer.SYNC_STATE_WAITING_FOR_DRAW, ime.mSyncState);
1136 
1137         final AsyncRotationController asyncRotationController =
1138                 mDisplayContent.getAsyncRotationController();
1139         assertNotNull(asyncRotationController);
1140         player.startTransition();
1141 
1142         assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
1143         assertFalse(mDisplayContent.mTransitionController.isCollecting(decorToken));
1144         assertTrue(ime.mToken.inTransition());
1145         assertTrue(task.inTransition());
1146         assertTrue(asyncRotationController.isTargetToken(decorToken));
1147         assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
1148 
1149         // Only seamless window syncs its draw transaction with transition.
1150         assertTrue(asyncRotationController.handleFinishDrawing(screenDecor, mMockT));
1151         // Status bar finishes drawing before the start transaction. Its fade-in animation will be
1152         // executed until the transaction is committed, so it is still in target tokens.
1153         assertFalse(asyncRotationController.handleFinishDrawing(statusBar, mMockT));
1154         assertTrue(asyncRotationController.isTargetToken(statusBar.mToken));
1155 
1156         // Window surface position is frozen while seamless rotation state is active.
1157         final Point prevPos = new Point(screenDecor.mLastSurfacePosition);
1158         screenDecor.getFrame().left += 1;
1159         screenDecor.updateSurfacePosition(mMockT);
1160         assertEquals(prevPos, screenDecor.mLastSurfacePosition);
1161 
1162         final SurfaceControl.Transaction startTransaction = mTransaction;
1163         clearInvocations(startTransaction);
1164         final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
1165                 onRotationTransactionReady(player, startTransaction);
1166 
1167         // The transaction is committed, so fade-in animation for status bar is consumed.
1168         transactionCommittedListener.onTransactionCommitted();
1169         assertFalse(asyncRotationController.isTargetToken(statusBar.mToken));
1170         assertShouldFreezeInsetsPosition(asyncRotationController, navBar, false);
1171 
1172         // Navigation bar finishes drawing after the start transaction, so its fade-in animation
1173         // can execute directly.
1174         navBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
1175         asyncRotationController.updateTargetWindows();
1176         assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
1177         assertNull(mDisplayContent.getAsyncRotationController());
1178     }
1179 
1180     @Test
testAppTransitionWithRotationChange()1181     public void testAppTransitionWithRotationChange() {
1182         final TestTransitionPlayer player = registerTestTransitionPlayer();
1183         final boolean useFixedRotation = !player.mController.useShellTransitionsRotation();
1184         if (useFixedRotation) {
1185             testFixedRotationOpen(player);
1186         } else {
1187             testShellRotationOpen(player);
1188         }
1189     }
1190 
testShellRotationOpen(TestTransitionPlayer player)1191     private void testShellRotationOpen(TestTransitionPlayer player) {
1192         final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
1193         makeWindowVisible(statusBar);
1194         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
1195         final ActivityRecord app = createActivityRecord(mDisplayContent);
1196         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
1197         app.mTransitionController.requestStartTransition(transition, app.getTask(),
1198                 null /* remoteTransition */, null /* displayChange */);
1199         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
1200         final int anyChanges = 1;
1201         mDisplayContent.setLastHasContent();
1202         mDisplayContent.collectDisplayChange(transition);
1203         transition.setKnownConfigChanges(mDisplayContent, anyChanges);
1204         final AsyncRotationController asyncRotationController =
1205                 mDisplayContent.getAsyncRotationController();
1206         assertNotNull(asyncRotationController);
1207         assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
1208 
1209         player.startTransition();
1210         // Non-app windows should not be collected.
1211         assertFalse(statusBar.mToken.inTransition());
1212         assertTrue(app.getTask().inTransition());
1213 
1214         final SurfaceControl.Transaction startTransaction = mTransaction;
1215         clearInvocations(startTransaction);
1216         final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
1217         doReturn(true).when(leash).isValid();
1218         final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
1219                 onRotationTransactionReady(player, startTransaction);
1220         // The leash should be unrotated.
1221         verify(startTransaction).setMatrix(eq(leash), any(), any());
1222 
1223         // The redrawn window will be faded in when the transition finishes. And because this test
1224         // only use one non-activity window, the fade rotation controller should also be cleared.
1225         statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
1226         final SurfaceControl.Transaction postDrawTransaction =
1227                 mock(SurfaceControl.Transaction.class);
1228         final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction,
1229                 Integer.MAX_VALUE);
1230         assertFalse(layoutNeeded);
1231 
1232         transactionCommittedListener.onTransactionCommitted();
1233         player.finish();
1234         // The controller should capture the draw transaction and merge it when preparing to run
1235         // fade-in animation.
1236         verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction));
1237         assertNull(mDisplayContent.getAsyncRotationController());
1238     }
1239 
testFixedRotationOpen(TestTransitionPlayer player)1240     private void testFixedRotationOpen(TestTransitionPlayer player) {
1241         final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
1242         makeWindowVisible(statusBar);
1243         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
1244         final WindowState navBar = createNavBarWithProvidedInsets(mDisplayContent);
1245         final InsetsSourceProvider navBarInsetsProvider = navBar.getControllableInsetProvider();
1246         assertNotNull(navBarInsetsProvider);
1247         final ActivityRecord app = createActivityRecord(mDisplayContent);
1248         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
1249         app.mTransitionController.requestStartTransition(transition, app.getTask(),
1250                 null /* remoteTransition */, null /* displayChange */);
1251         transition.collectExistenceChange(app.getTask());
1252         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
1253         final AsyncRotationController asyncRotationController =
1254                 mDisplayContent.getAsyncRotationController();
1255         assertNotNull(asyncRotationController);
1256         assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
1257         assertTrue(app.getTask().inTransition());
1258 
1259         player.start();
1260         player.finish();
1261         app.getTask().finishSync(mWm.mTransactionFactory.get(), app.getTask().getSyncGroup(),
1262                 false /* cancel */);
1263 
1264         // The open transition is finished. Continue to play seamless display change transition,
1265         // so the previous async rotation controller should still exist.
1266         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
1267         mDisplayContent.setLastHasContent();
1268         mDisplayContent.requestChangeTransition(1 /* changes */, null /* displayChange */);
1269         assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
1270         assertNotNull(mDisplayContent.getAsyncRotationController());
1271 
1272         // The app is still in transition, so the callback should be no-op.
1273         mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app);
1274         assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
1275 
1276         // The bar was invisible so it is not handled by the controller. But if it becomes visible
1277         // and drawn before the transition starts,
1278         assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
1279         navBar.finishDrawing(null /* postDrawTransaction */, Integer.MAX_VALUE);
1280         assertTrue(asyncRotationController.isTargetToken(navBar.mToken));
1281         assertTrue(asyncRotationController.shouldFreezeInsetsPosition(navBar));
1282 
1283         player.startTransition();
1284         // Non-app windows should not be collected.
1285         assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
1286         // Avoid DeviceStateController disturbing the test by triggering another rotation change.
1287         doReturn(false).when(mDisplayContent).updateRotationUnchecked();
1288 
1289         clearInvocations(mTransaction);
1290         onRotationTransactionReady(player, mTransaction).onTransactionCommitted();
1291         assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
1292                 mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
1293         spyOn(navBarInsetsProvider);
1294         player.finish();
1295 
1296         // The controller should be cleared if the target windows are drawn.
1297         statusBar.finishDrawing(mWm.mTransactionFactory.get(), Integer.MAX_VALUE);
1298         assertNull(mDisplayContent.getAsyncRotationController());
1299         // The shouldFreezeInsetsPosition for navBar was true, so its insets position should be
1300         // updated if the transition is done.
1301         verify(navBarInsetsProvider).updateInsetsControlPosition(navBar);
1302     }
1303 
assertShouldFreezeInsetsPosition(AsyncRotationController controller, WindowState w, boolean freeze)1304     private static void assertShouldFreezeInsetsPosition(AsyncRotationController controller,
1305             WindowState w, boolean freeze) {
1306         if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
1307             // Non blast sync should never freeze insets position.
1308             freeze = false;
1309         }
1310         assertEquals(freeze, controller.shouldFreezeInsetsPosition(w));
1311     }
1312 
1313     @Test
testFinishRotationControllerWithFixedRotation()1314     public void testFinishRotationControllerWithFixedRotation() {
1315         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
1316         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
1317         registerTestTransitionPlayer();
1318         mDisplayContent.setLastHasContent();
1319         mDisplayContent.requestChangeTransition(1 /* changes */, null /* displayChange */);
1320         assertNotNull(mDisplayContent.getAsyncRotationController());
1321         mDisplayContent.setFixedRotationLaunchingAppUnchecked(null);
1322         assertNull("Clear rotation controller if rotation is not changed",
1323                 mDisplayContent.getAsyncRotationController());
1324 
1325         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
1326         assertNotNull(mDisplayContent.getAsyncRotationController());
1327         mDisplayContent.getDisplayRotation().setRotation(
1328                 mDisplayContent.getWindowConfiguration().getRotation() + 1);
1329         mDisplayContent.setFixedRotationLaunchingAppUnchecked(null);
1330         assertNotNull("Keep rotation controller if rotation will be changed",
1331                 mDisplayContent.getAsyncRotationController());
1332     }
1333 
1334     @Test
testDeferRotationForTransientLaunch()1335     public void testDeferRotationForTransientLaunch() {
1336         mDisplayContent.setIgnoreOrientationRequest(false);
1337         final TestTransitionPlayer player = registerTestTransitionPlayer();
1338         assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation());
1339         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
1340         final ActivityRecord home = new ActivityBuilder(mAtm)
1341                 .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask())
1342                 .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build();
1343         final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN);
1344         final int prevRotation = mDisplayContent.getRotation();
1345         transition.setTransientLaunch(home, null /* restoreBelow */);
1346         home.mTransitionController.requestStartTransition(transition, home.getTask(),
1347                 null /* remoteTransition */, null /* displayChange */);
1348         transition.collectExistenceChange(home);
1349         home.setVisibleRequested(true);
1350         mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
1351         doReturn(true).when(home).hasFixedRotationTransform(any());
1352         player.startTransition();
1353         player.onTransactionReady();
1354 
1355         final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
1356         final RemoteDisplayChangeController displayChangeController = mDisplayContent
1357                 .mRemoteDisplayChangeController;
1358         spyOn(displayRotation);
1359         spyOn(displayChangeController);
1360         doReturn(true).when(displayChangeController).isWaitingForRemoteDisplayChange();
1361         doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation(
1362                 anyInt() /* orientation */, anyInt() /* lastRotation */);
1363         // Rotation update is skipped while the recents animation is running.
1364         assertFalse(mDisplayContent.updateRotationUnchecked());
1365         assertEquals(SCREEN_ORIENTATION_UNSET, displayRotation.getLastOrientation());
1366         // Return to the app without fixed orientation from recents.
1367         app.moveFocusableActivityToTop("test");
1368         player.finish();
1369         // The display should be updated to the changed orientation after the animation is finish.
1370         assertNotEquals(mDisplayContent.getRotation(), prevRotation);
1371     }
1372 
1373     @Test
testIntermediateVisibility()1374     public void testIntermediateVisibility() {
1375         final TransitionController controller = new TestTransitionController(mAtm);
1376         controller.setSyncEngine(mWm.mSyncEngine);
1377         final ITransitionPlayer player = new ITransitionPlayer.Default();
1378         controller.registerTransitionPlayer(player, null /* playerProc */);
1379         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
1380 
1381         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1382         final Task task1 = createTask(mDisplayContent);
1383         final ActivityRecord activity1 = createActivityRecord(task1);
1384         activity1.setVisibleRequested(false);
1385         activity1.setVisible(false);
1386         final Task task2 = createTask(mDisplayContent);
1387         makeTaskOrganized(task1, task2);
1388         final ActivityRecord activity2 = createActivityRecord(task1);
1389         activity2.setVisibleRequested(true);
1390         activity2.setVisible(true);
1391 
1392         openTransition.collectExistenceChange(task1);
1393         openTransition.collectExistenceChange(activity1);
1394         openTransition.collectExistenceChange(task2);
1395         openTransition.collectExistenceChange(activity2);
1396 
1397         activity1.setVisibleRequested(true);
1398         activity1.setVisible(true);
1399         activity2.setVisibleRequested(false);
1400 
1401         // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
1402         // We didn't call abort on the transition itself, so it will still run onTransactionReady
1403         // normally.
1404         mWm.mSyncEngine.abort(openTransition.getSyncId());
1405 
1406         // Before finishing openTransition, we are now going to simulate closing task1 to return
1407         // back to (open) task2.
1408         final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
1409 
1410         closeTransition.collectExistenceChange(task1);
1411         closeTransition.collectExistenceChange(activity1);
1412         closeTransition.collectExistenceChange(task2);
1413         closeTransition.collectExistenceChange(activity2);
1414 
1415         activity1.setVisibleRequested(false);
1416         activity2.setVisibleRequested(true);
1417 
1418         final ActionChain chain = ActionChain.testFinish(null);
1419         openTransition.finishTransition(chain);
1420 
1421         // We finished the openTransition. Even though activity1 is visibleRequested=false, since
1422         // the closeTransition animation hasn't played yet, make sure that we didn't commit
1423         // visible=false on activity1 since it needs to remain visible for the animation.
1424         assertTrue(activity1.isVisible());
1425         assertTrue(activity2.isVisible());
1426 
1427         // Using abort to force-finish the sync (since we obviously can't wait for drawing).
1428         // We didn't call abort on the actual transition, so it will still run onTransactionReady
1429         // normally.
1430         mWm.mSyncEngine.abort(closeTransition.getSyncId());
1431 
1432         closeTransition.finishTransition(chain);
1433 
1434         assertFalse(activity1.isVisible());
1435         assertTrue(activity2.isVisible());
1436 
1437         // The abort should still commit visible-requested to visible.
1438         final Transition abortTransition = controller.createTransition(TRANSIT_OPEN);
1439         abortTransition.collect(activity1);
1440         activity1.setVisibleRequested(true);
1441         activity1.setVisible(false);
1442         abortTransition.abort();
1443         assertTrue(activity1.isVisible());
1444 
1445         // The mLaunchTaskBehind flag of an invisible initializing activity should not be cleared.
1446         final Transition noChangeTransition = controller.createTransition(TRANSIT_OPEN);
1447         noChangeTransition.collect(activity1);
1448         activity1.setVisibleRequested(false);
1449         activity1.setState(ActivityRecord.State.INITIALIZING, "test");
1450         activity1.mLaunchTaskBehind = true;
1451         mWm.mSyncEngine.abort(noChangeTransition.getSyncId());
1452         noChangeTransition.finishTransition(chain);
1453         assertTrue(activity1.mLaunchTaskBehind);
1454     }
1455 
1456     @Test
testTransitionEndedListeners()1457     public void testTransitionEndedListeners() {
1458         final TransitionController controller = new TestTransitionController(mAtm);
1459         controller.setSyncEngine(mWm.mSyncEngine);
1460         final ITransitionPlayer player = new ITransitionPlayer.Default();
1461         controller.registerTransitionPlayer(player, null /* playerProc */);
1462         final Runnable transitionEndedListener = mock(Runnable.class);
1463 
1464         final Transition transition1 = controller.createTransition(TRANSIT_OPEN);
1465         transition1.addTransitionEndedListener(transitionEndedListener);
1466 
1467         // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
1468         // We didn't call abort on the transition itself, so it will still run onTransactionReady
1469         // normally.
1470         mWm.mSyncEngine.abort(transition1.getSyncId());
1471         transition1.finishTransition(ActionChain.testFinish(transition1));
1472 
1473         verify(transitionEndedListener).run();
1474 
1475         clearInvocations(transitionEndedListener);
1476 
1477         final Transition transition2 = controller.createTransition(TRANSIT_OPEN);
1478         transition2.addTransitionEndedListener(transitionEndedListener);
1479         transition2.abort();
1480         verify(transitionEndedListener).run();
1481     }
1482 
1483     @Test
testTransientLaunch()1484     public void testTransientLaunch() {
1485         spyOn(mWm.mSnapshotController.mTaskSnapshotController);
1486         final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>();
1487         final TransitionController controller = new TestTransitionController(mAtm) {
1488             @Override
1489             protected void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
1490                 if (ar.mEnteringAnimation) {
1491                     enteringAnimReports.add(ar);
1492                 }
1493                 super.dispatchLegacyAppTransitionFinished(ar);
1494             }
1495         };
1496         controller.setSyncEngine(mWm.mSyncEngine);
1497         controller.mSnapshotController = mWm.mSnapshotController;
1498         final TaskSnapshotController taskSnapshotController = controller.mSnapshotController
1499                 .mTaskSnapshotController;
1500         final ITransitionPlayer player = new ITransitionPlayer.Default();
1501         controller.registerTransitionPlayer(player, null /* playerProc */);
1502         final Transition openTransition = createTestTransition(TRANSIT_OPEN, controller);
1503         controller.moveToCollecting(openTransition);
1504 
1505         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1506         final Task task1 = createTask(mDisplayContent);
1507         final ActivityRecord activity1 = createActivityRecord(task1);
1508         activity1.setVisibleRequested(false);
1509         activity1.setVisible(false);
1510         final Task task2 = createTask(mDisplayContent);
1511         makeTaskOrganized(task1, task2);
1512         final ActivityRecord activity2 = createActivityRecord(task2);
1513         activity2.setVisibleRequested(true);
1514         activity2.setVisible(true);
1515 
1516         openTransition.collectExistenceChange(task1);
1517         openTransition.collectExistenceChange(activity1);
1518         openTransition.collectExistenceChange(task2);
1519         openTransition.collectExistenceChange(activity2);
1520 
1521         activity1.setVisibleRequested(true);
1522         activity1.setVisible(true);
1523         activity2.setVisibleRequested(false);
1524         activity1.setState(ActivityRecord.State.RESUMED, "test");
1525 
1526         // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
1527         // We didn't call abort on the transition itself, so it will still run onTransactionReady
1528         // normally.
1529         mWm.mSyncEngine.abort(openTransition.getSyncId());
1530 
1531         verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2));
1532 
1533         controller.finishTransition(ActionChain.testFinish(openTransition));
1534 
1535         // We are now going to simulate closing task1 to return back to (open) task2.
1536         final Transition closeTransition = createTestTransition(TRANSIT_CLOSE, controller);
1537         controller.moveToCollecting(closeTransition);
1538 
1539         closeTransition.collectExistenceChange(task2);
1540         closeTransition.collectExistenceChange(activity2);
1541         closeTransition.setTransientLaunch(activity2, task1);
1542         final Transition.ChangeInfo task1ChangeInfo = closeTransition.mChanges.get(task1);
1543         assertNotNull(task1ChangeInfo);
1544         assertTrue(task1ChangeInfo.hasChanged());
1545         // Make sure the unrelated activity is NOT collected.
1546         final Transition.ChangeInfo activity1ChangeInfo = closeTransition.mChanges.get(activity1);
1547         assertNull(activity1ChangeInfo);
1548         // No need to wait for the activity in transient hide task.
1549         assertEquals(WindowContainer.SYNC_STATE_NONE, activity1.mSyncState);
1550 
1551         // An active transient launch overrides idle state to avoid clearing power mode before the
1552         // transition is finished.
1553         activity2.idle = true;
1554         assertFalse(mRootWindowContainer.allResumedActivitiesIdle());
1555 
1556         activity1.setVisibleRequested(false);
1557         activity2.setVisibleRequested(true);
1558         activity2.setVisible(true);
1559 
1560         // Using abort to force-finish the sync (since we obviously can't wait for drawing).
1561         // We didn't call abort on the actual transition, so it will still run onTransactionReady
1562         // normally.
1563         mWm.mSyncEngine.abort(closeTransition.getSyncId());
1564 
1565         // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be
1566         // called until finish).
1567         verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1));
1568 
1569         enteringAnimReports.clear();
1570         doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), anyBoolean());
1571         final boolean[] wasInFinishingTransition = { false };
1572         controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
1573                 mDisplayContent.mDisplayId) {
1574             @Override
1575             public void onAppTransitionFinishedLocked(IBinder token) {
1576                 final ActivityRecord r = ActivityRecord.forToken(token);
1577                 if (r != null) {
1578                     wasInFinishingTransition[0] = controller.inFinishingTransition(r);
1579                 }
1580             }
1581         });
1582         final boolean[] calledListenerOnOtherDisplay = { false };
1583         controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
1584                 mDisplayContent.mDisplayId + 1234) {
1585             @Override
1586             public void onAppTransitionFinishedLocked(IBinder token) {
1587                 calledListenerOnOtherDisplay[0] = true;
1588             }
1589         });
1590         assertTrue(activity1.isVisible());
1591         doReturn(false).when(task1).isTranslucent(null);
1592         doReturn(false).when(task1).isTranslucentAndVisible();
1593         assertTrue(controller.canApplyDim(task1));
1594         doReturn(true).when(task1).isTranslucent(null);
1595         doReturn(true).when(task1).isTranslucentAndVisible();
1596         assertFalse(controller.canApplyDim(task1));
1597 
1598         controller.finishTransition(ActionChain.testFinish(closeTransition));
1599         assertTrue(wasInFinishingTransition[0]);
1600         assertFalse(calledListenerOnOtherDisplay[0]);
1601         assertNull(controller.mFinishingTransition);
1602 
1603         assertTrue(activity2.isVisible());
1604         assertEquals(ActivityTaskManagerService.APP_SWITCH_DISALLOW, mAtm.getBalAppSwitchesState());
1605         // Because task1 is occluded by task2, finishTransition should make activity1 invisible.
1606         assertFalse(activity1.isVisibleRequested());
1607         // Make sure activity1 visibility was committed
1608         assertFalse(activity1.isVisible());
1609         assertFalse(activity1.app.hasActivityInVisibleTask());
1610         // Make sure the userLeaving is true and the resuming activity is given,
1611         verify(task1).startPausing(eq(true), anyBoolean(), eq(activity2), any());
1612 
1613         verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1));
1614         assertTrue(enteringAnimReports.contains(activity2));
1615     }
1616 
1617     @Test
testIsTransientVisible()1618     public void testIsTransientVisible() {
1619         final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
1620                 .setVisible(false).build();
1621         final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
1622                 .setVisible(false).build();
1623         final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
1624         final Task taskA = appA.getTask();
1625         final Task taskB = appB.getTask();
1626         final Task taskRecent = recent.getTask();
1627         registerTestTransitionPlayer();
1628         final TransitionController controller = mRootWindowContainer.mTransitionController;
1629         final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
1630         controller.moveToCollecting(transition);
1631         transition.collect(recent);
1632         transition.collect(taskA);
1633         transition.setTransientLaunch(recent, taskA);
1634         taskRecent.moveToFront("move-recent-to-front");
1635         recent.setVisibility(true);
1636         recent.setState(ActivityRecord.State.RESUMED, "test");
1637 
1638         // During collecting and playing, the recent is on top so it is visible naturally.
1639         // While B needs isTransientVisible to keep visibility because it is occluded by recents.
1640         assertFalse(controller.isTransientVisible(taskB));
1641         assertTrue(controller.isTransientVisible(taskA));
1642         assertFalse(controller.isTransientVisible(taskRecent));
1643         // Switch to playing state.
1644         transition.onTransactionReady(transition.getSyncId(), mMockT);
1645         assertTrue(controller.isTransientVisible(taskA));
1646 
1647         // Switch to another task. For example, use gesture navigation to switch tasks.
1648         taskB.moveToFront("move-b-to-front");
1649         appB.setVisibility(true);
1650         // The previous app (taskA) should be paused first so it loses transient visible. Because
1651         // visually it is taskA -> taskB, the pause -> resume order should be the same.
1652         assertFalse(controller.isTransientVisible(taskA));
1653         // The recent is occluded by appB.
1654         assertFalse(controller.isTransientVisible(taskRecent));
1655         // Active transient launch won't be paused if the transition is not finished. It is to
1656         // avoid the latency to resume the current top (appB) by waiting for both recent and appA
1657         // to complete pause.
1658         assertEquals(recent, taskRecent.getResumedActivity());
1659         assertFalse(taskRecent.startPausing(false /* uiSleeping */, appB /* resuming */, "test"));
1660         // ActivityRecord#makeInvisible will add the invisible recent to the stopping list.
1661         // So when the transition finished, the recent can still be notified to pause and stop.
1662         mDisplayContent.ensureActivitiesVisible(null /* starting */, true /* notifyClients */);
1663         assertTrue(mSupervisor.mStoppingActivities.contains(recent));
1664     }
1665 
1666     @Test
testTransientWithParallelLaunch()1667     public void testTransientWithParallelLaunch() {
1668         final Task recentTask = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
1669         final ActivityRecord recent = new ActivityBuilder(mAtm).setTask(recentTask)
1670                 .setVisible(false).build();
1671         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
1672         final Task appTask = app.getTask();
1673         registerTestTransitionPlayer();
1674         final TransitionController controller = mRootWindowContainer.mTransitionController;
1675         final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
1676         transition.mParallelCollectType = Transition.PARALLEL_TYPE_RECENTS;
1677         controller.moveToCollecting(transition);
1678         transition.collect(recentTask);
1679         transition.collect(appTask);
1680         transition.setTransientLaunch(recent, appTask);
1681         recentTask.moveToFront("move-recent-to-front");
1682         transition.setAllReady();
1683         transition.start();
1684         // Assume that the app starts another activity in its task.
1685         final Transition newTransition = controller.createAndStartCollecting(TRANSIT_OPEN);
1686 
1687         assertEquals(newTransition, controller.getCollectingTransition());
1688         assertTrue(controller.mWaitingTransitions.contains(transition));
1689         assertTrue(controller.isTransientHide(appTask));
1690         assertTrue(controller.isTransientVisible(appTask));
1691         assertTrue(controller.isTransientLaunch(recent));
1692     }
1693 
1694     @Test
testNotReadyPushPop()1695     public void testNotReadyPushPop() {
1696         final TransitionController controller = new TestTransitionController(mAtm);
1697         controller.setSyncEngine(mWm.mSyncEngine);
1698         final ITransitionPlayer player = new ITransitionPlayer.Default();
1699         controller.registerTransitionPlayer(player, null /* playerProc */);
1700         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
1701 
1702         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1703         final Task task1 = createTask(mDisplayContent);
1704         openTransition.collectExistenceChange(task1);
1705 
1706         assertFalse(openTransition.allReady());
1707 
1708         openTransition.setAllReady();
1709 
1710         openTransition.deferTransitionReady();
1711         assertFalse(openTransition.allReady());
1712 
1713         openTransition.continueTransitionReady();
1714         assertTrue(openTransition.allReady());
1715     }
1716 
1717     @Test
testIsBehindStartingWindowChange()1718     public void testIsBehindStartingWindowChange() {
1719         final Transition transition = createTestTransition(TRANSIT_OPEN);
1720         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1721         final ArraySet<WindowContainer> participants = transition.mParticipants;
1722 
1723         final Task task = createTask(mDisplayContent);
1724         final ActivityRecord activity0 = createActivityRecord(task);
1725         final ActivityRecord activity1 = createActivityRecord(task);
1726         final WindowManager.LayoutParams attrs =
1727                 new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING);
1728         final TestWindowState startingWindow = createWindowState(attrs, activity1);
1729         activity1.mStartingData = mock(StartingData.class);
1730         activity1.addWindow(startingWindow);
1731 
1732         // Start states.
1733         changes.put(activity0,
1734                 new Transition.ChangeInfo(activity0, true /* vis */, false /* exChg */));
1735         changes.put(activity1,
1736                 new Transition.ChangeInfo(activity1, false /* vis */, false /* exChg */));
1737         // End states.
1738         activity0.setVisibleRequested(false);
1739         activity1.setVisibleRequested(true);
1740 
1741         participants.add(activity0);
1742         participants.add(activity1);
1743         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1744                 participants, changes);
1745         final TransitionInfo info = Transition.calculateTransitionInfo(
1746                 transition.mType, 0 /* flags */, targets, mMockT);
1747 
1748         // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting
1749         // window should cover the whole Task.
1750         assertEquals(2, info.getChanges().size());
1751         assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
1752         assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
1753 
1754     }
1755 
1756     @Test
testFlagInTaskWithEmbeddedActivity()1757     public void testFlagInTaskWithEmbeddedActivity() {
1758         final Transition transition = createTestTransition(TRANSIT_OPEN);
1759         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1760         final ArraySet<WindowContainer> participants = transition.mParticipants;
1761 
1762         final Task task = createTask(mDisplayContent);
1763         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1764         assertFalse(nonEmbeddedActivity.isEmbedded());
1765         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1766         registerTaskFragmentOrganizer(
1767                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1768         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1769                 .setParentTask(task)
1770                 .createActivityCount(2)
1771                 .setOrganizer(organizer)
1772                 .build();
1773         final ActivityRecord closingActivity = embeddedTf.getBottomMostActivity();
1774         final ActivityRecord openingActivity = embeddedTf.getTopMostActivity();
1775         // Start states.
1776         changes.put(embeddedTf,
1777                 new Transition.ChangeInfo(embeddedTf, true /* vis */, false /* exChg */));
1778         changes.put(closingActivity,
1779                 new Transition.ChangeInfo(closingActivity, true /* vis */, false /* exChg */));
1780         changes.put(openingActivity,
1781                 new Transition.ChangeInfo(openingActivity, false /* vis */, true /* exChg */));
1782         changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity,
1783                 true /* vis */, false /* exChg */));
1784         // End states.
1785         closingActivity.setVisibleRequested(false);
1786         openingActivity.setVisibleRequested(true);
1787         nonEmbeddedActivity.setVisibleRequested(false);
1788 
1789         participants.add(closingActivity);
1790         participants.add(openingActivity);
1791         participants.add(nonEmbeddedActivity);
1792         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1793                 participants, changes);
1794         final TransitionInfo info = Transition.calculateTransitionInfo(
1795                 transition.mType, 0 /* flags */, targets, mMockT);
1796 
1797         // All windows in the Task should have FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY because the Task
1798         // contains embedded activity.
1799         assertEquals(3, info.getChanges().size());
1800         assertTrue(info.getChanges().get(0).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1801         assertTrue(info.getChanges().get(1).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1802         assertTrue(info.getChanges().get(2).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1803     }
1804 
1805     @Test
testFlagFillsTask_embeddingNotFillingTask()1806     public void testFlagFillsTask_embeddingNotFillingTask() {
1807         final Transition transition = createTestTransition(TRANSIT_OPEN);
1808         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1809         final ArraySet<WindowContainer> participants = transition.mParticipants;
1810 
1811         final Task task = createTask(mDisplayContent);
1812         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1813         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1814         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1815         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1816         registerTaskFragmentOrganizer(
1817                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1818         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1819                 .setParentTask(task)
1820                 .createActivityCount(1)
1821                 .setOrganizer(organizer)
1822                 .build();
1823         final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
1824         // Start states.
1825         changes.put(task, new Transition.ChangeInfo(task, true /* vis */, false /* exChg */));
1826         changes.put(nonEmbeddedActivity,
1827                 new Transition.ChangeInfo(nonEmbeddedActivity, true /* vis */, false /* exChg */));
1828         changes.put(embeddedTf,
1829                 new Transition.ChangeInfo(embeddedTf, false /* vis */, true /* exChg */));
1830         // End states.
1831         nonEmbeddedActivity.setVisibleRequested(false);
1832         embeddedActivity.setVisibleRequested(true);
1833         embeddedTf.setBounds(new Rect(0, 0, 500, 500));
1834 
1835         participants.add(nonEmbeddedActivity);
1836         participants.add(embeddedTf);
1837         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1838                 participants, changes);
1839         final TransitionInfo info = Transition.calculateTransitionInfo(
1840                 transition.mType, 0 /* flags */, targets, mMockT);
1841 
1842         // The embedded with bounds overridden should not have the flag.
1843         assertEquals(2, info.getChanges().size());
1844         assertFalse(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1845         assertEquals(embeddedTf.getBounds(), info.getChanges().get(0).getEndAbsBounds());
1846         assertTrue(info.getChanges().get(1).hasFlags(FLAG_FILLS_TASK));
1847     }
1848 
1849     @Test
testFlagFillsTask_openActivityFillingTask()1850     public void testFlagFillsTask_openActivityFillingTask() {
1851         final Transition transition = createTestTransition(TRANSIT_OPEN);
1852         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1853         final ArraySet<WindowContainer> participants = transition.mParticipants;
1854 
1855         final Task task = createTask(mDisplayContent);
1856         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1857         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1858         final ActivityRecord activity = createActivityRecord(task);
1859         // Start states: set bounds to make sure the start bounds is ignored if it is not visible.
1860         activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
1861         activity.setVisibleRequested(false);
1862         changes.put(activity, new Transition.ChangeInfo(activity));
1863         // End states: reset bounds to fill Task.
1864         activity.getConfiguration().windowConfiguration.setBounds(taskBounds);
1865         activity.setVisibleRequested(true);
1866 
1867         participants.add(activity);
1868         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1869                 participants, changes);
1870         final TransitionInfo info = Transition.calculateTransitionInfo(
1871                 transition.mType, 0 /* flags */, targets, mMockT);
1872 
1873         // Opening activity that is filling Task after transition should have the flag.
1874         assertEquals(1, info.getChanges().size());
1875         assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1876     }
1877 
1878     @Test
testFlagFillsTask_closeActivityFillingTask()1879     public void testFlagFillsTask_closeActivityFillingTask() {
1880         final Transition transition = createTestTransition(TRANSIT_CLOSE);
1881         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1882         final ArraySet<WindowContainer> participants = transition.mParticipants;
1883 
1884         final Task task = createTask(mDisplayContent);
1885         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1886         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1887         final ActivityRecord activity = createActivityRecord(task);
1888         // Start states: fills Task without override.
1889         activity.setVisibleRequested(true);
1890         changes.put(activity, new Transition.ChangeInfo(activity));
1891         // End states: set bounds to make sure the start bounds is ignored if it is not visible.
1892         activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
1893         activity.setVisibleRequested(false);
1894 
1895         participants.add(activity);
1896         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1897                 participants, changes);
1898         final TransitionInfo info = Transition.calculateTransitionInfo(
1899                 transition.mType, 0 /* flags */, targets, mMockT);
1900 
1901         // Closing activity that is filling Task before transition should have the flag.
1902         assertEquals(1, info.getChanges().size());
1903         assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1904     }
1905 
1906     @Test
testReparentChangeLastParent()1907     public void testReparentChangeLastParent() {
1908         final Transition transition = createTestTransition(TRANSIT_CHANGE);
1909         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1910         final ArraySet<WindowContainer> participants = transition.mParticipants;
1911 
1912         // Reparent activity in transition.
1913         final Task lastParent = createTask(mDisplayContent);
1914         final Task newParent = createTask(mDisplayContent);
1915         final ActivityRecord activity = createActivityRecord(lastParent);
1916         activity.setVisibleRequested(true);
1917         // Skip manipulate the SurfaceControl.
1918         doNothing().when(activity).setDropInputMode(anyInt());
1919         changes.put(activity, new Transition.ChangeInfo(activity));
1920         activity.reparent(newParent, POSITION_TOP);
1921         activity.setVisibleRequested(false);
1922 
1923         participants.add(activity);
1924         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1925                 participants, changes);
1926         final TransitionInfo info = Transition.calculateTransitionInfo(
1927                 transition.mType, 0 /* flags */, targets, mMockT);
1928 
1929         // Change contains last parent info.
1930         assertEquals(1, info.getChanges().size());
1931         assertEquals(lastParent.mRemoteToken.toWindowContainerToken(),
1932                 info.getChanges().get(0).getLastParent());
1933     }
1934 
1935     @Test
testIncludeEmbeddedActivityReparent()1936     public void testIncludeEmbeddedActivityReparent() {
1937         final Transition transition = createTestTransition(TRANSIT_OPEN);
1938         final Task task = createTask(mDisplayContent);
1939         task.setBounds(new Rect(0, 0, 2000, 1000));
1940         final ActivityRecord activity = createActivityRecord(task);
1941         activity.setVisibleRequested(true);
1942         // Skip manipulate the SurfaceControl.
1943         doNothing().when(activity).setDropInputMode(anyInt());
1944         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1945         registerTaskFragmentOrganizer(
1946                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1947         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1948                 .setParentTask(task)
1949                 .setOrganizer(organizer)
1950                 .build();
1951         // TaskFragment with different bounds from Task.
1952         embeddedTf.setBounds(new Rect(0, 0, 1000, 1000));
1953 
1954         // Start states.
1955         transition.collect(activity);
1956         transition.collectExistenceChange(embeddedTf);
1957 
1958         // End states.
1959         activity.reparent(embeddedTf, POSITION_TOP);
1960 
1961         // Verify that both activity and TaskFragment are included.
1962         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
1963                 transition.mParticipants, transition.mChanges);
1964         assertTrue(Transition.containsChangeFor(embeddedTf, targets));
1965         assertTrue(Transition.containsChangeFor(activity, targets));
1966     }
1967 
1968     @Test
testChangeSetBackgroundColor()1969     public void testChangeSetBackgroundColor() {
1970         final Transition transition = createTestTransition(TRANSIT_CHANGE);
1971         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1972         final ArraySet<WindowContainer> participants = transition.mParticipants;
1973 
1974         // Test background color for Activity and embedded TaskFragment.
1975         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1976         registerTaskFragmentOrganizer(
1977                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1978         final Task task = createTask(mDisplayContent);
1979         final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
1980         final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
1981         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1982         final ActivityManager.TaskDescription taskDescription =
1983                 new ActivityManager.TaskDescription.Builder()
1984                         .setBackgroundColor(Color.YELLOW)
1985                         .build();
1986         task.setTaskDescription(taskDescription);
1987 
1988         // Start states:
1989         embeddedActivity.setVisibleRequested(true);
1990         nonEmbeddedActivity.setVisibleRequested(false);
1991         changes.put(embeddedTf, new Transition.ChangeInfo(embeddedTf));
1992         changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity));
1993         // End states:
1994         embeddedActivity.setVisibleRequested(false);
1995         nonEmbeddedActivity.setVisibleRequested(true);
1996 
1997         participants.add(embeddedTf);
1998         participants.add(nonEmbeddedActivity);
1999         final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
2000                 participants, changes);
2001         final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType,
2002                 0 /* flags */, targets, mMockT);
2003 
2004         // Background color should be set on both Activity and embedded TaskFragment.
2005         final int expectedBackgroundColor = ColorUtils.setAlphaComponent(
2006                 taskDescription.getBackgroundColor(), 255);
2007         assertEquals(2, info.getChanges().size());
2008         assertEquals(expectedBackgroundColor, info.getChanges().get(0).getBackgroundColor());
2009         assertEquals(expectedBackgroundColor, info.getChanges().get(1).getBackgroundColor());
2010     }
2011 
2012     @Test
testOverrideAnimationOptionsToInfoIfNecessary_fromStyleAnimOptions()2013     public void testOverrideAnimationOptionsToInfoIfNecessary_fromStyleAnimOptions() {
2014         ActivityRecord r = initializeOverrideAnimationOptionsTest();
2015         TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
2016                 .makeCommonAnimOptions("testPackage");
2017         mTransition.setOverrideAnimation(options, r, null /* startCallback */,
2018                 null /* finishCallback */);
2019 
2020         mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
2021 
2022         final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
2023         final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
2024         final TransitionInfo.Change embeddedTfChange = mInfo.getChanges().get(2);
2025         final TransitionInfo.Change activityChange = mInfo.getChanges().get(3);
2026 
2027         assertNull("Display change's AnimationOptions must not be overridden.",
2028                 displayChange.getAnimationOptions());
2029         assertNull("Task change's AnimationOptions must not be overridden.",
2030                 taskChange.getAnimationOptions());
2031         assertNull("Embedded TF change's AnimationOptions must not be overridden.",
2032                 embeddedTfChange.getAnimationOptions());
2033         assertEquals("Activity change's AnimationOptions must be overridden.",
2034                 options, activityChange.getAnimationOptions());
2035     }
2036 
2037     @Test
testOverrideAnimationOptionsToInfoIfNecessary_sceneAnimOptions()2038     public void testOverrideAnimationOptionsToInfoIfNecessary_sceneAnimOptions() {
2039         ActivityRecord r = initializeOverrideAnimationOptionsTest();
2040         TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
2041                 .makeSceneTransitionAnimOptions();
2042         mTransition.setOverrideAnimation(options, r, null /* startCallback */,
2043                 null /* finishCallback */);
2044 
2045         mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
2046 
2047         final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
2048         final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
2049         final TransitionInfo.Change embeddedTfChange = mInfo.getChanges().get(2);
2050         final TransitionInfo.Change activityChange = mInfo.getChanges().get(3);
2051 
2052         assertNull("Display change's AnimationOptions must not be overridden.",
2053                 displayChange.getAnimationOptions());
2054         assertEquals("Task change's AnimationOptions must be overridden.",
2055                 options, taskChange.getAnimationOptions());
2056         assertNull("Embedded TF change's AnimationOptions must not be overridden.",
2057                 embeddedTfChange.getAnimationOptions());
2058         assertEquals("Activity change's AnimationOptions must be overridden.",
2059                 options, activityChange.getAnimationOptions());
2060     }
2061 
2062     @Test
testOverrideAnimationOptionsToInfoIfNecessary_crossProfileAnimOptions()2063     public void testOverrideAnimationOptionsToInfoIfNecessary_crossProfileAnimOptions() {
2064         ActivityRecord r = initializeOverrideAnimationOptionsTest();
2065         TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
2066                 .makeCrossProfileAnimOptions();
2067         mTransition.setOverrideAnimation(options, r, null /* startCallback */,
2068                 null /* finishCallback */);
2069 
2070         final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
2071         final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
2072         final TransitionInfo.Change embeddedTfChange = mInfo.getChanges().get(2);
2073         final TransitionInfo.Change activityChange = mInfo.getChanges().get(3);
2074         activityChange.setMode(TRANSIT_OPEN);
2075 
2076         mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
2077 
2078         assertNull("Display change's AnimationOptions must not be overridden.",
2079                 displayChange.getAnimationOptions());
2080         assertNull("Task change's AnimationOptions must not be overridden.",
2081                 taskChange.getAnimationOptions());
2082         assertNull("Embedded TF change's AnimationOptions must not be overridden.",
2083                 embeddedTfChange.getAnimationOptions());
2084         assertEquals("Activity change's AnimationOptions must be overridden.",
2085                 options, activityChange.getAnimationOptions());
2086         assertTrue(activityChange.hasFlags(FLAG_CROSS_PROFILE_OWNER_THUMBNAIL));
2087     }
2088 
2089     @Test
testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptions()2090     public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptions() {
2091         final ActivityRecord r = initializeOverrideAnimationOptionsTest();
2092         final TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
2093                 .makeCustomAnimOptions("testPackage", Resources.ID_NULL,
2094                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2095                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2096                         false /* overrideTaskTransition */);
2097         mTransition.setOverrideAnimation(options, r, null /* startCallback */,
2098                 null /* finishCallback */);
2099         final int expectedBackgroundColor = Color.GREEN;
2100         mTransition.setOverrideBackgroundColor(expectedBackgroundColor);
2101 
2102         mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
2103 
2104         final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
2105         final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
2106         final TransitionInfo.Change embeddedTfChange = mInfo.getChanges().get(2);
2107         final TransitionInfo.Change activityChange = mInfo.getChanges().get(3);
2108 
2109         assertNull("Display change's AnimationOptions must not be overridden.",
2110                 displayChange.getAnimationOptions());
2111         assertNull("Task change's AnimationOptions must not be overridden.",
2112                 taskChange.getAnimationOptions());
2113         assertEquals("Embedded TF change's AnimationOptions must be overridden.",
2114                 options, embeddedTfChange.getAnimationOptions());
2115         assertEquals("Embedded TF change's background color must not be overridden.",
2116                 0, embeddedTfChange.getBackgroundColor());
2117         assertEquals("Activity change's AnimationOptions must be overridden.",
2118                 options, activityChange.getAnimationOptions());
2119         assertEquals("Activity change's background color must be overridden.",
2120                 expectedBackgroundColor, activityChange.getBackgroundColor());
2121 
2122     }
2123 
2124     @Test
testOverrideAnimationOptionsToInfoIfNecessary_haveTaskFragmentAnimParams()2125     public void testOverrideAnimationOptionsToInfoIfNecessary_haveTaskFragmentAnimParams() {
2126         final ActivityRecord r = initializeOverrideAnimationOptionsTest();
2127 
2128         final TaskFragment embeddedTf = mTransition.mTargets.get(2).mContainer.asTaskFragment();
2129         embeddedTf.setAnimationParams(new TaskFragmentAnimationParams.Builder()
2130                 .setAnimationBackgroundColor(Color.RED)
2131                 .setOpenAnimationResId(0x12345678)
2132                 .build());
2133 
2134         final TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
2135                 .makeCustomAnimOptions("testPackage", Resources.ID_NULL,
2136                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2137                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2138                         false /* overrideTaskTransition */);
2139         mTransition.setOverrideAnimation(options, r, null /* startCallback */,
2140                 null /* finishCallback */);
2141         final int expectedBackgroundColor = Color.GREEN;
2142         mTransition.setOverrideBackgroundColor(expectedBackgroundColor);
2143 
2144         final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
2145         final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
2146         final TransitionInfo.Change embeddedTfChange = mInfo.getChanges().get(2);
2147         final TransitionInfo.Change activityChange = mInfo.getChanges().get(3);
2148 
2149         final int expectedColor = embeddedTf.getAnimationParams().getAnimationBackgroundColor();
2150         embeddedTfChange.setBackgroundColor(expectedColor);
2151         final TransitionInfo.AnimationOptions expectedOptions = TransitionInfo.AnimationOptions
2152                 .makeCustomAnimOptions("testPackage", 0x12345678,
2153                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2154                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2155                         false /* overrideTaskTransition */);
2156         embeddedTfChange.setAnimationOptions(expectedOptions);
2157 
2158         mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
2159 
2160         assertNull("Display change's AnimationOptions must not be overridden.",
2161                 displayChange.getAnimationOptions());
2162         assertNull("Task change's AnimationOptions must not be overridden.",
2163                 taskChange.getAnimationOptions());
2164         assertEquals("Embedded TF change's AnimationOptions must be overridden.",
2165                 expectedOptions, embeddedTfChange.getAnimationOptions());
2166         assertEquals("Embedded TF change's background color must not be overridden.",
2167                 expectedColor, embeddedTfChange.getBackgroundColor());
2168         assertEquals("Activity change's AnimationOptions must be overridden.",
2169                 options, activityChange.getAnimationOptions());
2170         assertEquals("Activity change's background color must be overridden.",
2171                 expectedBackgroundColor, activityChange.getBackgroundColor());
2172     }
2173 
2174     @Test
testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptionsWithTaskOverride()2175     public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptionsWithTaskOverride() {
2176         final ActivityRecord r = initializeOverrideAnimationOptionsTest();
2177         final TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
2178                 .makeCustomAnimOptions("testPackage", Resources.ID_NULL,
2179                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2180                         TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID,
2181                         true /* overrideTaskTransition */);
2182         mTransition.setOverrideAnimation(options, r, null /* startCallback */,
2183                 null /* finishCallback */);
2184         final int expectedBackgroundColor = Color.GREEN;
2185         mTransition.setOverrideBackgroundColor(expectedBackgroundColor);
2186 
2187         mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
2188 
2189         final TransitionInfo.Change displayChange = mInfo.getChanges().get(0);
2190         final TransitionInfo.Change taskChange = mInfo.getChanges().get(1);
2191         final TransitionInfo.Change embeddedTfChange = mInfo.getChanges().get(2);
2192         final TransitionInfo.Change activityChange = mInfo.getChanges().get(3);
2193 
2194         assertNull("Display change's AnimationOptions must not be overridden.",
2195                 displayChange.getAnimationOptions());
2196         assertEquals("Task change's AnimationOptions must be overridden.",
2197                 options, taskChange.getAnimationOptions());
2198         assertEquals("Task change's background color must be overridden.",
2199                 expectedBackgroundColor, taskChange.getBackgroundColor());
2200         assertEquals("Embedded TF change's AnimationOptions must be overridden.",
2201                 options, embeddedTfChange.getAnimationOptions());
2202         assertEquals("Embedded TF change's background color must be overridden.",
2203                 0, embeddedTfChange.getBackgroundColor());
2204         assertEquals("Activity change's AnimationOptions must be overridden.",
2205                 options, activityChange.getAnimationOptions());
2206         assertEquals("Activity change's background color must be overridden.",
2207                 expectedBackgroundColor, activityChange.getBackgroundColor());
2208     }
2209 
initializeOverrideAnimationOptionsTest()2210     private ActivityRecord initializeOverrideAnimationOptionsTest() {
2211         mTransition = createTestTransition(TRANSIT_OPEN);
2212 
2213         // Test set AnimationOptions for Activity and Task.
2214         final Task task = createTask(mDisplayContent);
2215         // Create an embedded TaskFragment.
2216         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
2217         registerTaskFragmentOrganizer(
2218                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
2219         final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
2220         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
2221         mWm.mCurrentUserId = nonEmbeddedActivity.mUserId;
2222 
2223         mTransition.mTargets = new ArrayList<>();
2224         mTransition.mTargets.add(new Transition.ChangeInfo(mDisplayContent));
2225         mTransition.mTargets.add(new Transition.ChangeInfo(task));
2226         mTransition.mTargets.add(new Transition.ChangeInfo(embeddedTf));
2227         mTransition.mTargets.add(new Transition.ChangeInfo(nonEmbeddedActivity));
2228 
2229         mInfo = new TransitionInfo(TRANSIT_OPEN, 0 /* flags */);
2230         mInfo.addChange(new TransitionInfo.Change(mDisplayContent.mRemoteToken
2231                 .toWindowContainerToken(), mDisplayContent.getAnimationLeash()));
2232         mInfo.addChange(new TransitionInfo.Change(task.mRemoteToken.toWindowContainerToken(),
2233                 task.getAnimationLeash()));
2234         mInfo.addChange(new TransitionInfo.Change(embeddedTf.mRemoteToken.toWindowContainerToken(),
2235                 embeddedTf.getAnimationLeash()));
2236         mInfo.addChange(new TransitionInfo.Change(null /* container */,
2237                 nonEmbeddedActivity.getAnimationLeash()));
2238         return nonEmbeddedActivity;
2239     }
2240 
2241     @Test
testTransitionVisibleChange()2242     public void testTransitionVisibleChange() {
2243         registerTestTransitionPlayer();
2244         final ActivityRecord app = createActivityRecord(
2245                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
2246         final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */,
2247                 app.mTransitionController, mWm.mSyncEngine);
2248         app.mTransitionController.moveToCollecting(transition);
2249         mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE);
2250         final ArrayList<WindowContainer> freezeCalls = new ArrayList<>();
2251         transition.setContainerFreezer(new Transition.IContainerFreezer() {
2252             @Override
2253             public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
2254                 freezeCalls.add(wc);
2255                 return true;
2256             }
2257 
2258             @Override
2259             public void cleanUp(SurfaceControl.Transaction t) {
2260             }
2261         });
2262         assertEquals(WindowAnimator.PENDING_STATE_NONE, mWm.mAnimator.mPendingState);
2263         app.startAnimation(app.getPendingTransaction(), mock(AnimationAdapter.class),
2264                 false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
2265         assertEquals(WindowAnimator.PENDING_STATE_HAS_CHANGES, mWm.mAnimator.mPendingState);
2266 
2267         final Task task = app.getTask();
2268         transition.collect(task);
2269         assertEquals(WindowAnimator.PENDING_STATE_NEED_APPLY, mWm.mAnimator.mPendingState);
2270         final Rect bounds = new Rect(task.getBounds());
2271         Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
2272         bounds.inset(10, 10);
2273         c.windowConfiguration.setBounds(bounds);
2274         task.onRequestedOverrideConfigurationChanged(c);
2275         assertTrue(freezeCalls.contains(task));
2276 
2277         transition.start();
2278         mWm.mSyncEngine.abort(transition.getSyncId());
2279         assertEquals(WindowAnimator.PENDING_STATE_NONE, mWm.mAnimator.mPendingState);
2280     }
2281 
2282     @Test
testDeferTransitionReady_deferStartedTransition()2283     public void testDeferTransitionReady_deferStartedTransition() {
2284         final Transition transition = createTestTransition(TRANSIT_OPEN);
2285         transition.setAllReady();
2286         transition.start();
2287 
2288         assertTrue(mSyncEngine.isReady(transition.getSyncId()));
2289 
2290         transition.deferTransitionReady();
2291 
2292         // Both transition ready tracker and sync engine should be deferred.
2293         assertFalse(transition.allReady());
2294         assertFalse(mSyncEngine.isReady(transition.getSyncId()));
2295 
2296         transition.continueTransitionReady();
2297 
2298         assertTrue(transition.allReady());
2299         assertTrue(mSyncEngine.isReady(transition.getSyncId()));
2300     }
2301 
2302     @Test
testVisibleChange_snapshot()2303     public void testVisibleChange_snapshot() {
2304         registerTestTransitionPlayer();
2305         final ActivityRecord app = createActivityRecord(
2306                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
2307         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
2308                 app.mTransitionController, mWm.mSyncEngine);
2309         app.mTransitionController.moveToCollecting(transition);
2310         mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE);
2311         final SurfaceControl mockSnapshot = mock(SurfaceControl.class);
2312         transition.setContainerFreezer(new Transition.IContainerFreezer() {
2313             @Override
2314             public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
2315                 Objects.requireNonNull(transition.mChanges.get(wc)).mSnapshot = mockSnapshot;
2316                 return true;
2317             }
2318 
2319             @Override
2320             public void cleanUp(SurfaceControl.Transaction t) {
2321             }
2322         });
2323         final Task task = app.getTask();
2324         transition.collect(task);
2325         final Rect bounds = new Rect(task.getBounds());
2326         Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
2327         bounds.inset(10, 10);
2328         c.windowConfiguration.setBounds(bounds);
2329         task.onRequestedOverrideConfigurationChanged(c);
2330 
2331         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
2332                 transition.mParticipants, transition.mChanges);
2333         TransitionInfo info = Transition.calculateTransitionInfo(
2334                 TRANSIT_CHANGE, 0, targets, mMockT);
2335         assertEquals(mockSnapshot,
2336                 info.getChange(task.mRemoteToken.toWindowContainerToken()).getSnapshot());
2337         transition.abort();
2338     }
2339 
2340     @Test
testCollectReparentChange()2341     public void testCollectReparentChange() {
2342         registerTestTransitionPlayer();
2343 
2344         // Reparent activity in transition.
2345         final Task lastParent = createTask(mDisplayContent);
2346         final Task newParent = createTask(mDisplayContent);
2347         final ActivityRecord activity = createActivityRecord(lastParent);
2348         doReturn(true).when(lastParent).shouldRemoveSelfOnLastChildRemoval();
2349         doNothing().when(activity).setDropInputMode(anyInt());
2350         activity.setVisibleRequested(true);
2351 
2352         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
2353                 activity.mTransitionController, mWm.mSyncEngine);
2354         activity.mTransitionController.moveToCollecting(transition);
2355         transition.collect(activity);
2356         activity.reparent(newParent, POSITION_TOP);
2357 
2358         // ChangeInfo#mCommonAncestor should be set after reparent.
2359         final Transition.ChangeInfo change = transition.mChanges.get(activity);
2360         assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
2361 
2362         // WindowContainer#onDisplayChanged should collect the moved task.
2363         final DisplayContent newDisplay = createNewDisplay();
2364         newParent.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
2365         assertTrue(transition.mParticipants.contains(newParent));
2366     }
2367 
2368     @Test
testMoveTaskToTopWhileVisible()2369     public void testMoveTaskToTopWhileVisible() {
2370         final Transition transition = createTestTransition(TRANSIT_OPEN);
2371         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
2372         final ArraySet<WindowContainer> participants = transition.mParticipants;
2373 
2374         // Start with taskB on top and taskA on bottom but both visible.
2375         final Task rootTaskA = createTask(mDisplayContent);
2376         final Task leafTaskA = createTaskInRootTask(rootTaskA, 0 /* userId */);
2377         final Task taskB = createTask(mDisplayContent);
2378         leafTaskA.setVisibleRequested(true);
2379         taskB.setVisibleRequested(true);
2380         // manually collect since this is a test transition and not known by transitionController.
2381         transition.collect(leafTaskA);
2382         rootTaskA.moveToFront("test", leafTaskA);
2383 
2384         // Test has order changes, a shallow check of order changes
2385         assertTrue(transition.hasOrderChanges());
2386 
2387         // All the tasks were already visible, so there shouldn't be any changes
2388         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
2389                 participants, changes);
2390         assertTrue(targets.isEmpty());
2391 
2392         // After collecting order changes, it should recognize that a task moved to top.
2393         transition.collectOrderChanges(true);
2394         targets = Transition.calculateTargets(participants, changes);
2395         assertEquals(1, targets.size());
2396 
2397         // Make sure the flag is set
2398         final TransitionInfo info = Transition.calculateTransitionInfo(
2399                 transition.mType, 0 /* flags */, targets, mMockT);
2400         assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
2401         assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
2402     }
2403 
2404     @Test
testMoveDisplayToTop()2405     public void testMoveDisplayToTop() {
2406         // Set up two displays, each of which has a task.
2407         DisplayContent otherDisplay = createNewDisplay();
2408         final Consumer<DisplayContent> setUpTask = (DisplayContent dc) -> {
2409             final Task task = createTask(dc);
2410             final ActivityRecord act = createActivityRecord(task);
2411             final TestWindowState win = createWindowState(
2412                     new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), act);
2413             act.addWindow(win);
2414             act.setVisibleRequested(true);
2415         };
2416         setUpTask.accept(mDisplayContent);
2417         setUpTask.accept(otherDisplay);
2418         mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /* updateImWindows */);
2419 
2420         final Transition transition = createTestTransition(TRANSIT_OPEN);
2421         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
2422         final ArraySet<WindowContainer> participants = transition.mParticipants;
2423 
2424         // Emulate WindowManagerService#moveDisplayToTopInternal().
2425         transition.recordTaskOrder(mDefaultDisplay);
2426         mDefaultDisplay.getParent().positionChildAt(WindowContainer.POSITION_TOP,
2427                 mDefaultDisplay, true /* includingParents */);
2428         mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /* updateImWindows */);
2429         transition.setReady(mDefaultDisplay, true /* ready */);
2430 
2431         // Test has order changes, a shallow check of order changes.
2432         assertTrue(transition.hasOrderChanges());
2433 
2434         // We just moved a display to top, so there shouldn't be any changes.
2435         ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
2436                 participants, changes);
2437         assertTrue(targets.isEmpty());
2438 
2439         // After collecting order changes, the task on the newly focused display should be
2440         // considered to get moved to top.
2441         transition.collectOrderChanges(true);
2442         targets = Transition.calculateTargets(participants, changes);
2443         assertEquals(1, targets.size());
2444 
2445         // Make sure the flag is set
2446         final TransitionInfo info = Transition.calculateTransitionInfo(
2447                 transition.mType, 0 /* flags */, targets, mMockT);
2448         assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
2449         assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
2450     }
2451 
2452     private class OrderChangeTestSetup {
2453         final TransitionController mController;
2454         final TestTransitionPlayer mPlayer;
2455         final Transition mTransitA;
2456         final Transition mTransitB;
2457 
OrderChangeTestSetup()2458         OrderChangeTestSetup() {
2459             mController = mAtm.getTransitionController();
2460             mPlayer = registerTestTransitionPlayer();
2461             mController.setSyncEngine(mWm.mSyncEngine);
2462 
2463             mTransitA = createTestTransition(TRANSIT_OPEN, mController);
2464             mTransitA.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2465             mTransitB = createTestTransition(TRANSIT_OPEN, mController);
2466             mTransitB.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2467         }
2468 
startParallelCollect(boolean activityLevelFirst)2469         void startParallelCollect(boolean activityLevelFirst) {
2470             // Start with taskB on top and taskA on bottom but both visible.
2471             final Task taskA = createTask(mDisplayContent);
2472             taskA.setVisibleRequested(true);
2473             final ActivityRecord actA = createActivityRecord(taskA);
2474             final TestWindowState winA = createWindowState(
2475                     new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), actA);
2476             actA.addWindow(winA);
2477             final ActivityRecord actB = createActivityRecord(taskA);
2478             final TestWindowState winB = createWindowState(
2479                     new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), actB);
2480             actB.addWindow(winB);
2481 
2482             final Task taskB = createTask(mDisplayContent);
2483             actA.setVisibleRequested(true);
2484             actB.setVisibleRequested(false);
2485             taskB.setVisibleRequested(true);
2486             assertTrue(actA.isAttached());
2487 
2488             final Consumer<Boolean> startAndCollectA = (doReady) -> {
2489                 mController.startCollectOrQueue(mTransitA, (deferred) -> {
2490                 });
2491 
2492                 // Collect activity-level change into A
2493                 mTransitA.collect(actA);
2494                 actA.setVisibleRequested(false);
2495                 winA.onSyncFinishedDrawing();
2496                 mTransitA.collect(actB);
2497                 actB.setVisibleRequested(true);
2498                 winB.onSyncFinishedDrawing();
2499                 mTransitA.start();
2500                 if (doReady) {
2501                     mTransitA.setReady(mDisplayContent, true);
2502                 }
2503             };
2504             final Consumer<Boolean> startAndCollectB = (doReady) -> {
2505                 mController.startCollectOrQueue(mTransitB, (deferred) -> {
2506                 });
2507                 mTransitB.collect(taskA);
2508                 taskA.moveToFront("test");
2509                 mTransitB.start();
2510                 if (doReady) {
2511                     mTransitB.setReady(mDisplayContent, true);
2512                 }
2513             };
2514 
2515             if (activityLevelFirst) {
2516                 startAndCollectA.accept(true);
2517                 startAndCollectB.accept(false);
2518             } else {
2519                 startAndCollectB.accept(true);
2520                 startAndCollectA.accept(false);
2521             }
2522         }
2523     }
2524 
2525     @Test
testMoveToTopStartAfterReadyAfterParallel()2526     public void testMoveToTopStartAfterReadyAfterParallel() {
2527         // Start collect activity-only transit A
2528         // Start collect task transit B in parallel
2529         // finish A first -> should not include order change from B.
2530         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2531         setup.startParallelCollect(true /* activity first */);
2532 
2533         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2534         waitUntilHandlersIdle();
2535         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2536             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2537         }
2538 
2539         setup.mTransitB.setAllReady();
2540         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2541         waitUntilHandlersIdle();
2542         boolean hasOrderChange = false;
2543         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2544             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2545             if (chg.getTaskInfo() == null) continue;
2546             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2547         }
2548         assertTrue(hasOrderChange);
2549     }
2550 
2551     @Test
testMoveToTopStartAfterReadyBeforeParallel()2552     public void testMoveToTopStartAfterReadyBeforeParallel() {
2553         // Start collect activity-only transit A
2554         // Start collect task transit B in parallel
2555         // finish B first -> should include order change
2556         // then finish A -> should NOT include order change.
2557         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2558         setup.startParallelCollect(true /* activity first */);
2559         // Make it unready now so that it doesn't get dequeued automatically.
2560         setup.mTransitA.setReady(mDisplayContent, false);
2561 
2562         // Make task change ready first
2563         setup.mTransitB.setAllReady();
2564         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2565         waitUntilHandlersIdle();
2566         boolean hasOrderChange = false;
2567         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2568             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2569             if (chg.getTaskInfo() == null) continue;
2570             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2571         }
2572         assertTrue(hasOrderChange);
2573 
2574         setup.mTransitA.setAllReady();
2575         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2576         waitUntilHandlersIdle();
2577         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2578             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2579         }
2580     }
2581 
2582     @Test
testMoveToTopStartBeforeReadyAfterParallel()2583     public void testMoveToTopStartBeforeReadyAfterParallel() {
2584         // Start collect task transit B
2585         // Start collect activity-only transit A in parallel
2586         // finish A first -> should not include order change from B.
2587         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2588         setup.startParallelCollect(false /* activity first */);
2589         // Make B unready now so that it doesn't get dequeued automatically.
2590         setup.mTransitB.setReady(mDisplayContent, false);
2591 
2592         setup.mTransitA.setAllReady();
2593         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2594         waitUntilHandlersIdle();
2595         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2596             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2597         }
2598 
2599         setup.mTransitB.setAllReady();
2600         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2601         waitUntilHandlersIdle();
2602         boolean hasOrderChange = false;
2603         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2604             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2605             if (chg.getTaskInfo() == null) continue;
2606             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2607         }
2608         assertTrue(hasOrderChange);
2609     }
2610 
2611     @Test
testMoveToTopStartBeforeReadyBeforeParallel()2612     public void testMoveToTopStartBeforeReadyBeforeParallel() {
2613         // Start collect task transit B
2614         // Start collect activity-only transit A in parallel
2615         // finish B first -> should include order change
2616         // then finish A -> should NOT include order change.
2617         final OrderChangeTestSetup setup = new OrderChangeTestSetup();
2618         setup.startParallelCollect(false /* activity first */);
2619 
2620         mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId());
2621         waitUntilHandlersIdle();
2622         boolean hasOrderChange = false;
2623         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2624             final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i);
2625             if (chg.getTaskInfo() == null) continue;
2626             hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0;
2627         }
2628         assertTrue(hasOrderChange);
2629 
2630         setup.mTransitA.setAllReady();
2631         mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId());
2632         waitUntilHandlersIdle();
2633         for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) {
2634             assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo());
2635         }
2636     }
2637 
2638     @Test
testQueueStartCollect()2639     public void testQueueStartCollect() {
2640         final TransitionController controller = mAtm.getTransitionController();
2641         final TestTransitionPlayer player = registerTestTransitionPlayer();
2642 
2643         mSyncEngine = createTestBLASTSyncEngine();
2644         controller.setSyncEngine(mSyncEngine);
2645 
2646         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2647         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2648         final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
2649 
2650         final boolean[] onStartA = new boolean[]{false, false};
2651         final boolean[] onStartB = new boolean[]{false, false};
2652         controller.startCollectOrQueue(transitA, (deferred) -> {
2653             onStartA[0] = true;
2654             onStartA[1] = deferred;
2655         });
2656         controller.startCollectOrQueue(transitB, (deferred) -> {
2657             onStartB[0] = true;
2658             onStartB[1] = deferred;
2659         });
2660         waitUntilHandlersIdle();
2661 
2662         assertTrue(onStartA[0]);
2663         assertFalse(onStartA[1]);
2664         assertTrue(transitA.isCollecting());
2665 
2666         // B should be queued, so no calls yet
2667         assertFalse(onStartB[0]);
2668         assertTrue(transitB.isPending());
2669 
2670         // finish collecting A
2671         transitA.start();
2672         transitA.setAllReady();
2673         mSyncEngine.tryFinishForTest(transitA.getSyncId());
2674         waitUntilHandlersIdle();
2675 
2676         assertTrue(transitA.isPlaying());
2677         assertTrue(transitB.isCollecting());
2678         assertTrue(onStartB[0]);
2679         // Should receive deferred = true
2680         assertTrue(onStartB[1]);
2681 
2682         // finish collecting B
2683         transitB.start();
2684         transitB.setAllReady();
2685         mSyncEngine.tryFinishForTest(transitB.getSyncId());
2686         assertTrue(transitB.isPlaying());
2687 
2688         // Now we should be able to start collecting directly a new transition
2689         final boolean[] onStartC = new boolean[]{false, false};
2690         controller.startCollectOrQueue(transitC, (deferred) -> {
2691             onStartC[0] = true;
2692             onStartC[1] = deferred;
2693         });
2694         waitUntilHandlersIdle();
2695         assertTrue(onStartC[0]);
2696         assertFalse(onStartC[1]);
2697         assertTrue(transitC.isCollecting());
2698     }
2699 
2700     @Test
testQueueWithLegacy()2701     public void testQueueWithLegacy() {
2702         final TransitionController controller = mAtm.getTransitionController();
2703         final TestTransitionPlayer player = registerTestTransitionPlayer();
2704 
2705         mSyncEngine = createTestBLASTSyncEngine();
2706         controller.setSyncEngine(mSyncEngine);
2707 
2708         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2709         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2710 
2711         controller.startCollectOrQueue(transitA, (deferred) -> {});
2712 
2713         BLASTSyncEngine.SyncGroup legacySync = mSyncEngine.prepareSyncSet(
2714                 mock(BLASTSyncEngine.TransactionReadyListener.class), "test");
2715         final boolean[] applyLegacy = new boolean[2];
2716         controller.startLegacySyncOrQueue(legacySync, (deferred) -> {
2717             applyLegacy[0] = true;
2718             applyLegacy[1] = deferred;
2719         });
2720         assertFalse(applyLegacy[0]);
2721         waitUntilHandlersIdle();
2722 
2723         controller.startCollectOrQueue(transitB, (deferred) -> {});
2724         assertTrue(transitA.isCollecting());
2725 
2726         // finish collecting A
2727         tryFinishTransitionSyncSet(transitA);
2728         waitUntilHandlersIdle();
2729 
2730         assertTrue(transitA.isPlaying());
2731         // legacy sync should start now
2732         assertTrue(applyLegacy[0]);
2733         assertTrue(applyLegacy[1]);
2734         // transitB must wait
2735         assertTrue(transitB.isPending());
2736 
2737         // finish legacy sync
2738         mSyncEngine.setReady(legacySync.mSyncId);
2739         mSyncEngine.tryFinishForTest(legacySync.mSyncId);
2740         // transitioncontroller should be notified so it can start collecting B
2741         assertTrue(transitB.isCollecting());
2742     }
2743 
2744     @Test
testQueueParallel()2745     public void testQueueParallel() {
2746         final TransitionController controller = mAtm.getTransitionController();
2747         final TestTransitionPlayer player = registerTestTransitionPlayer();
2748 
2749         mSyncEngine = createTestBLASTSyncEngine();
2750         controller.setSyncEngine(mSyncEngine);
2751 
2752         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2753         transitA.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2754         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2755         transitB.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2756         final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
2757         transitC.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL;
2758         final Transition transitSync = createTestTransition(TRANSIT_OPEN, controller);
2759         final Transition transitD = createTestTransition(TRANSIT_OPEN, controller);
2760 
2761         controller.startCollectOrQueue(transitA, (deferred) -> {});
2762         controller.startCollectOrQueue(transitB, (deferred) -> {});
2763         controller.startCollectOrQueue(transitC, (deferred) -> {});
2764         controller.startCollectOrQueue(transitSync, (deferred) -> {});
2765         controller.startCollectOrQueue(transitD, (deferred) -> {});
2766 
2767         assertTrue(transitA.isCollecting() && !transitA.isStarted());
2768         // We still serialize on readiness
2769         assertTrue(transitB.isPending());
2770         assertTrue(transitC.isPending());
2771 
2772         transitA.start();
2773         transitA.setAllReady();
2774         transitB.start();
2775         transitB.setAllReady();
2776 
2777         // A, B, and C should be collecting in parallel now.
2778         assertTrue(transitA.isStarted());
2779         assertTrue(transitB.isStarted());
2780         assertTrue(transitC.isCollecting() && !transitC.isStarted());
2781 
2782         transitC.start();
2783         transitC.setAllReady();
2784 
2785         assertTrue(transitA.isStarted());
2786         assertTrue(transitB.isStarted());
2787         assertTrue(transitC.isStarted());
2788         // Not parallel so should remain pending
2789         assertTrue(transitSync.isPending());
2790         // After Sync, so should also remain pending.
2791         assertTrue(transitD.isPending());
2792         // There should always be a collector, since Sync can't collect yet, C should remain.
2793         assertEquals(transitC, controller.getCollectingTransition());
2794 
2795         mSyncEngine.tryFinishForTest(transitB.getSyncId());
2796 
2797         // The other transitions should remain waiting.
2798         assertTrue(transitA.isStarted());
2799         assertTrue(transitB.isPlaying());
2800         assertTrue(transitC.isStarted());
2801         assertEquals(transitC, controller.getCollectingTransition());
2802 
2803         mSyncEngine.tryFinishForTest(transitC.getSyncId());
2804         assertTrue(transitA.isStarted());
2805         assertTrue(transitC.isPlaying());
2806         // The "collecting" one became ready, so the first "waiting" should move back to collecting.
2807         assertEquals(transitA, controller.getCollectingTransition());
2808 
2809         assertTrue(transitSync.isPending());
2810         assertTrue(transitD.isPending());
2811         mSyncEngine.tryFinishForTest(transitA.getSyncId());
2812 
2813         // Now all collectors are done, so sync can be pulled-off the queue.
2814         assertTrue(transitSync.isCollecting() && !transitSync.isStarted());
2815         transitSync.start();
2816         transitSync.setAllReady();
2817         // Since D can run in parallel, it should be pulled-off the queue.
2818         assertTrue(transitSync.isStarted());
2819         assertTrue(transitD.isPending());
2820 
2821         mSyncEngine.tryFinishForTest(transitSync.getSyncId());
2822         assertTrue(transitD.isCollecting());
2823 
2824         transitD.start();
2825         transitD.setAllReady();
2826         mSyncEngine.tryFinishForTest(transitD.getSyncId());
2827 
2828         // Now nothing should be collecting
2829         assertFalse(controller.isCollecting());
2830     }
2831 
2832     @Test
testDeferredMoveTaskToBack()2833     public void testDeferredMoveTaskToBack() {
2834         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
2835         final Task task = activity.getTask();
2836         registerTestTransitionPlayer();
2837         final TransitionController controller = mWm.mRoot.mTransitionController;
2838         mSyncEngine = createTestBLASTSyncEngine();
2839         controller.setSyncEngine(mSyncEngine);
2840         final Transition transition = createTestTransition(TRANSIT_CHANGE, controller);
2841         controller.moveToCollecting(transition);
2842         task.moveTaskToBack(task);
2843         // Actual action will be deferred by current transition.
2844         verify(task, never()).moveToBack(any(), any());
2845 
2846         tryFinishTransitionSyncSet(transition);
2847         waitUntilHandlersIdle();
2848         // Continue to move task to back after the transition is done.
2849         verify(task).moveToBack(any(), any());
2850         final Transition moveBackTransition = controller.getCollectingTransition();
2851         assertNotNull(moveBackTransition);
2852         moveBackTransition.abort();
2853 
2854         // The move-to-back can be collected in to a collecting OPEN transition.
2855         clearInvocations(task);
2856         final Transition transition2 = createTestTransition(TRANSIT_OPEN, controller);
2857         controller.moveToCollecting(transition2);
2858         task.moveTaskToBack(task);
2859         verify(task).moveToBack(any(), any());
2860     }
2861 
2862     @Test
testNoSyncFlagIfOneTrack()2863     public void testNoSyncFlagIfOneTrack() {
2864         final TransitionController controller = mAtm.getTransitionController();
2865         final TestTransitionPlayer player = registerTestTransitionPlayer();
2866 
2867         mSyncEngine = createTestBLASTSyncEngine();
2868         controller.setSyncEngine(mSyncEngine);
2869 
2870         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2871         final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
2872         final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
2873 
2874         controller.startCollectOrQueue(transitA, (deferred) -> {});
2875         controller.startCollectOrQueue(transitB, (deferred) -> {});
2876         controller.startCollectOrQueue(transitC, (deferred) -> {});
2877 
2878         // Verify that, as-long as there is <= 1 track, we won't get a SYNC flag
2879         tryFinishTransitionSyncSet(transitA);
2880         assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
2881         tryFinishTransitionSyncSet(transitB);
2882         assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
2883         tryFinishTransitionSyncSet(transitC);
2884         assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
2885     }
2886 
2887     @Test
testTransitionsTriggerPerformanceHints()2888     public void testTransitionsTriggerPerformanceHints() {
2889         final var session = new SystemPerformanceHinter.HighPerfSession[1];
2890         final SystemPerformanceHinter perfHinter = mWm.mSystemPerformanceHinter;
2891         spyOn(perfHinter);
2892         doAnswer(invocation -> {
2893             session[0] = (SystemPerformanceHinter.HighPerfSession) invocation.callRealMethod();
2894             spyOn(session[0]);
2895             return session[0];
2896         }).when(perfHinter).createSession(anyInt(), anyInt(), anyString());
2897         final TransitionController controller = mDisplayContent.mTransitionController;
2898         final TestTransitionPlayer player = registerTestTransitionPlayer();
2899         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
2900         final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
2901         controller.moveToCollecting(transitA);
2902         transitA.collectExistenceChange(app);
2903         controller.requestStartTransition(transitA, app.getTask(),
2904                 null /* remoteTransition */, null /* displayChange */);
2905         player.start();
2906 
2907         verify(mDisplayContent).enableHighPerfTransition(true);
2908         verify(session[0]).start();
2909 
2910         player.finish();
2911         verify(mDisplayContent).enableHighPerfTransition(false);
2912         verify(session[0]).close();
2913     }
2914 
2915     @Test
testConfigAtEnd()2916     public void testConfigAtEnd() {
2917         final TransitionController controller = mDisplayContent.mTransitionController;
2918         Transition transit = createTestTransition(TRANSIT_CHANGE, controller);
2919         final TestTransitionPlayer player = registerTestTransitionPlayer();
2920 
2921         final Task task = createTask(mDisplayContent);
2922         final Rect taskBounds = new Rect(0, 0, 200, 300);
2923         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
2924         final ActivityRecord activity = createActivityRecord(task);
2925         activity.setVisibleRequested(true);
2926         activity.setVisible(true);
2927 
2928         controller.moveToCollecting(transit);
2929         transit.collect(task);
2930         transit.setConfigAtEnd(task);
2931         task.getRequestedOverrideConfiguration().windowConfiguration.setBounds(
2932                 new Rect(10, 10, 200, 300));
2933         task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration());
2934 
2935         controller.requestStartTransition(transit, task, null, null);
2936         player.start();
2937         // always include config-at-end activity since it is considered "independent" due to
2938         // changing at a different time.
2939         assertTrue(player.mLastReady.getChanges().stream()
2940                 .anyMatch((change -> change.getActivityComponent() != null
2941                         && (change.getFlags() & TransitionInfo.FLAG_CONFIG_AT_END) != 0)));
2942         assertTrue(activity.isConfigurationDispatchPaused());
2943         player.finish();
2944         assertFalse(activity.isConfigurationDispatchPaused());
2945     }
2946 
2947     @Test
testConfigAtEndReparent()2948     public void testConfigAtEndReparent() {
2949         final TransitionController controller = mDisplayContent.mTransitionController;
2950         Transition transit = createTestTransition(TRANSIT_CHANGE, controller);
2951         final TestTransitionPlayer player = registerTestTransitionPlayer();
2952 
2953         final Task taskOrig = createTask(mDisplayContent);
2954         taskOrig.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 200, 300));
2955         final Task task = createTask(mDisplayContent);
2956         task.getConfiguration().windowConfiguration.setBounds(new Rect(10, 10, 200, 300));
2957         final ActivityRecord activity = createActivityRecord(taskOrig);
2958         activity.setVisibleRequested(true);
2959         activity.setVisible(true);
2960 
2961         controller.moveToCollecting(transit);
2962         transit.collect(taskOrig);
2963         transit.collect(task);
2964         transit.collect(activity);
2965         transit.setConfigAtEnd(taskOrig);
2966         activity.reparent(task, MAX_VALUE);
2967         task.moveToFront("test");
2968 
2969         controller.requestStartTransition(transit, task, null, null);
2970         player.start();
2971         // always include config-at-end activity since it is considered "independent" due to
2972         // changing at a different time.
2973         assertTrue(player.mLastReady.getChanges().stream()
2974                 .anyMatch((change -> change.getActivityComponent() != null
2975                         && (change.getFlags() & TransitionInfo.FLAG_CONFIG_AT_END) != 0)));
2976         assertTrue(activity.isConfigurationDispatchPaused());
2977         player.finish();
2978         assertFalse(activity.isConfigurationDispatchPaused());
2979     }
2980 
2981     @Test
testReadyTrackerBasics()2982     public void testReadyTrackerBasics() {
2983         final TransitionController controller = new TestTransitionController(
2984                 mock(ActivityTaskManagerService.class));
2985         controller.setFullReadyTrackingForTest(true);
2986         Transition transit = createTestTransition(TRANSIT_OPEN, controller);
2987         // Not ready if nothing has happened yet
2988         assertFalse(transit.mReadyTracker.isReady());
2989 
2990         Transition.ReadyCondition condition1 = new Transition.ReadyCondition("c1");
2991         transit.mReadyTracker.add(condition1);
2992         assertFalse(transit.mReadyTracker.isReady());
2993 
2994         Transition.ReadyCondition condition2 = new Transition.ReadyCondition("c2");
2995         transit.mReadyTracker.add(condition2);
2996         assertFalse(transit.mReadyTracker.isReady());
2997 
2998         condition2.meet();
2999         assertFalse(transit.mReadyTracker.isReady());
3000 
3001         condition1.meet();
3002         assertTrue(transit.mReadyTracker.isReady());
3003     }
3004 
3005     @Test
testReadyTrackerAlternate()3006     public void testReadyTrackerAlternate() {
3007         final TransitionController controller = new TestTransitionController(
3008                 mock(ActivityTaskManagerService.class));
3009         controller.setFullReadyTrackingForTest(true);
3010         Transition transit = createTestTransition(TRANSIT_OPEN, controller);
3011 
3012         Transition.ReadyCondition condition1 = new Transition.ReadyCondition("c1");
3013         transit.mReadyTracker.add(condition1);
3014         assertFalse(transit.mReadyTracker.isReady());
3015 
3016         condition1.meetAlternate("reason1");
3017         assertTrue(transit.mReadyTracker.isReady());
3018         assertEquals("reason1", condition1.mAlternate);
3019     }
3020 
tryFinishTransitionSyncSet(Transition transition)3021     private void tryFinishTransitionSyncSet(Transition transition) {
3022         transition.setAllReady();
3023         transition.start();
3024         mSyncEngine.tryFinishForTest(transition.getSyncId());
3025     }
3026 
makeTaskOrganized(Task... tasks)3027     private static void makeTaskOrganized(Task... tasks) {
3028         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
3029         for (Task t : tasks) {
3030             t.mTaskOrganizer = organizer;
3031         }
3032     }
3033 
makeDisplayAreaOrganized(WindowContainer<?> from, WindowContainer<?> end)3034     private static void makeDisplayAreaOrganized(WindowContainer<?> from,
3035             WindowContainer<?> end) {
3036         final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
3037         while (from != null && from != end) {
3038             if (from.asDisplayArea() != null) {
3039                 from.asDisplayArea().mOrganizer = organizer;
3040             }
3041             from = from.getParent();
3042         }
3043         if (end.asDisplayArea() != null) {
3044             end.asDisplayArea().mOrganizer = organizer;
3045         }
3046     }
3047 
3048     /** Fill the change map with all the parents of top. Change maps are usually fully populated */
fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, WindowContainer top)3049     private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
3050             WindowContainer top) {
3051         for (WindowContainer curr = top.getParent(); curr != null; curr = curr.getParent()) {
3052             changes.put(curr, new Transition.ChangeInfo(curr, true /* vis */, false /* exChg */));
3053         }
3054     }
3055 
onRotationTransactionReady( TestTransitionPlayer player, SurfaceControl.Transaction startTransaction)3056     private static SurfaceControl.TransactionCommittedListener onRotationTransactionReady(
3057             TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) {
3058         final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
3059                 ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
3060         player.onTransactionReady();
3061         // The startTransaction is from mWm.mTransactionFactory.get() in SyncGroup#finishNow.
3062         // 2 times are from SyncGroup#finishNow and AsyncRotationController#setupStartTransaction.
3063         verify(startTransaction, times(2)).addTransactionCommittedListener(
3064                 any(), listenerCaptor.capture());
3065         return listenerCaptor.getValue();
3066     }
3067 }
3068