• 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
24 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
25 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
26 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
27 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
28 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
29 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
30 import static android.view.WindowManager.TRANSIT_CHANGE;
31 import static android.view.WindowManager.TRANSIT_CLOSE;
32 import static android.view.WindowManager.TRANSIT_OPEN;
33 import static android.view.WindowManager.TRANSIT_TO_BACK;
34 import static android.window.TransitionInfo.FLAG_FILLS_TASK;
35 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
36 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
37 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
38 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
39 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
40 import static android.window.TransitionInfo.isIndependent;
41 
42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
45 import static com.android.server.wm.WindowContainer.POSITION_TOP;
46 
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertFalse;
49 import static org.junit.Assert.assertNotEquals;
50 import static org.junit.Assert.assertNotNull;
51 import static org.junit.Assert.assertNull;
52 import static org.junit.Assert.assertTrue;
53 import static org.junit.Assume.assumeFalse;
54 import static org.mockito.ArgumentMatchers.any;
55 import static org.mockito.ArgumentMatchers.anyInt;
56 import static org.mockito.ArgumentMatchers.eq;
57 import static org.mockito.Mockito.mock;
58 import static org.mockito.Mockito.spy;
59 import static org.mockito.Mockito.times;
60 import static org.mockito.Mockito.verify;
61 
62 import android.app.ActivityManager;
63 import android.content.res.Configuration;
64 import android.graphics.Color;
65 import android.graphics.Point;
66 import android.graphics.Rect;
67 import android.os.IBinder;
68 import android.platform.test.annotations.Presubmit;
69 import android.util.ArrayMap;
70 import android.util.ArraySet;
71 import android.view.SurfaceControl;
72 import android.window.IDisplayAreaOrganizer;
73 import android.window.IRemoteTransition;
74 import android.window.ITaskFragmentOrganizer;
75 import android.window.ITaskOrganizer;
76 import android.window.ITransitionPlayer;
77 import android.window.RemoteTransition;
78 import android.window.TaskFragmentOrganizer;
79 import android.window.TransitionInfo;
80 
81 import androidx.annotation.NonNull;
82 import androidx.test.filters.SmallTest;
83 
84 import com.android.internal.graphics.ColorUtils;
85 
86 import org.junit.Test;
87 import org.junit.runner.RunWith;
88 import org.mockito.ArgumentCaptor;
89 
90 import java.util.ArrayList;
91 import java.util.Arrays;
92 import java.util.Objects;
93 import java.util.concurrent.CountDownLatch;
94 import java.util.concurrent.TimeUnit;
95 import java.util.function.Function;
96 
97 /**
98  * Build/Install/Run:
99  *  atest WmTests:TransitionTests
100  */
101 @SmallTest
102 @Presubmit
103 @RunWith(WindowTestRunner.class)
104 public class TransitionTests extends WindowTestsBase {
105     final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class);
106     private BLASTSyncEngine mSyncEngine;
107 
createTestTransition(int transitType)108     private Transition createTestTransition(int transitType) {
109         TransitionTracer tracer = mock(TransitionTracer.class);
110         final TransitionController controller = new TransitionController(
111                 mock(ActivityTaskManagerService.class), mock(TaskSnapshotController.class),
112                 mock(TransitionTracer.class));
113 
114         mSyncEngine = createTestBLASTSyncEngine();
115         final Transition t = new Transition(transitType, 0 /* flags */, controller, mSyncEngine);
116         t.startCollecting(0 /* timeoutMs */);
117         return t;
118     }
119 
120     @Test
testCreateInfo_NewTask()121     public void testCreateInfo_NewTask() {
122         final Transition transition = createTestTransition(TRANSIT_OPEN);
123         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
124         ArraySet<WindowContainer> participants = transition.mParticipants;
125 
126         final Task newTask = createTask(mDisplayContent);
127         final Task oldTask = createTask(mDisplayContent);
128         final ActivityRecord closing = createActivityRecord(oldTask);
129         final ActivityRecord opening = createActivityRecord(newTask);
130         // Start states.
131         changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
132         changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, true /* exChg */));
133         changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
134         changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */));
135         fillChangeMap(changes, newTask);
136         // End states.
137         closing.setVisibleRequested(false);
138         opening.setVisibleRequested(true);
139 
140         final int transit = transition.mType;
141         int flags = 0;
142 
143         // Check basic both tasks participating
144         participants.add(oldTask);
145         participants.add(newTask);
146         ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
147         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
148                 mMockT);
149         assertEquals(2, info.getChanges().size());
150         assertEquals(transit, info.getType());
151 
152         // Check that children are pruned
153         participants.add(opening);
154         participants.add(closing);
155         targets = Transition.calculateTargets(participants, changes);
156         info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
157         assertEquals(2, info.getChanges().size());
158         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
159         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
160 
161         // Check combined prune and promote
162         participants.remove(newTask);
163         targets = Transition.calculateTargets(participants, changes);
164         info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
165         assertEquals(2, info.getChanges().size());
166         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
167         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
168 
169         // Check multi promote
170         participants.remove(oldTask);
171         targets = Transition.calculateTargets(participants, changes);
172         info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
173         assertEquals(2, info.getChanges().size());
174         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
175         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
176     }
177 
178     @Test
testCreateInfo_NestedTasks()179     public void testCreateInfo_NestedTasks() {
180         final Transition transition = createTestTransition(TRANSIT_OPEN);
181         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
182         ArraySet<WindowContainer> participants = transition.mParticipants;
183 
184         final Task newTask = createTask(mDisplayContent);
185         final Task newNestedTask = createTaskInRootTask(newTask, 0);
186         final Task newNestedTask2 = createTaskInRootTask(newTask, 0);
187         final Task oldTask = createTask(mDisplayContent);
188         final ActivityRecord closing = createActivityRecord(oldTask);
189         final ActivityRecord opening = createActivityRecord(newNestedTask);
190         final ActivityRecord opening2 = createActivityRecord(newNestedTask2);
191         // Start states.
192         changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
193         changes.put(newNestedTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
194         changes.put(newNestedTask2, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
195         changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, true /* exChg */));
196         changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
197         changes.put(opening2, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
198         changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */));
199         fillChangeMap(changes, newTask);
200         // End states.
201         closing.setVisibleRequested(false);
202         opening.setVisibleRequested(true);
203         opening2.setVisibleRequested(true);
204 
205         final int transit = transition.mType;
206         int flags = 0;
207 
208         // Check full promotion from leaf
209         participants.add(oldTask);
210         participants.add(opening);
211         participants.add(opening2);
212         ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
213         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
214                 mMockT);
215         assertEquals(2, info.getChanges().size());
216         assertEquals(transit, info.getType());
217         assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
218         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
219 
220         // Check that unchanging but visible descendant of sibling prevents promotion
221         participants.remove(opening2);
222         targets = Transition.calculateTargets(participants, changes);
223         info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
224         assertEquals(2, info.getChanges().size());
225         assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken()));
226         assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
227     }
228 
229     @Test
testCreateInfo_DisplayArea()230     public void testCreateInfo_DisplayArea() {
231         final Transition transition = createTestTransition(TRANSIT_OPEN);
232         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
233         ArraySet<WindowContainer> participants = transition.mParticipants;
234         final Task showTask = createTask(mDisplayContent);
235         final Task showNestedTask = createTaskInRootTask(showTask, 0);
236         final Task showTask2 = createTask(mDisplayContent);
237         final DisplayArea tda = showTask.getDisplayArea();
238         final ActivityRecord showing = createActivityRecord(showNestedTask);
239         final ActivityRecord showing2 = createActivityRecord(showTask2);
240         // Start states.
241         changes.put(showTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
242         changes.put(showNestedTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
243         changes.put(showTask2, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
244         changes.put(tda, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
245         changes.put(showing, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
246         changes.put(showing2, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
247         fillChangeMap(changes, tda);
248 
249         // End states.
250         showing.setVisibleRequested(true);
251         showing2.setVisibleRequested(true);
252 
253         final int transit = transition.mType;
254         int flags = 0;
255 
256         // Check promotion to DisplayArea
257         participants.add(showing);
258         participants.add(showing2);
259         ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
260         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
261                 mMockT);
262         assertEquals(1, info.getChanges().size());
263         assertEquals(transit, info.getType());
264         assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
265 
266         // Check that organized tasks get reported even if not top
267         makeTaskOrganized(showTask);
268         targets = Transition.calculateTargets(participants, changes);
269         info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
270         assertEquals(2, info.getChanges().size());
271         assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
272         assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken()));
273         // Even if DisplayArea explicitly participating
274         participants.add(tda);
275         targets = Transition.calculateTargets(participants, changes);
276         info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
277         assertEquals(2, info.getChanges().size());
278     }
279 
280     @Test
testCreateInfo_existenceChange()281     public void testCreateInfo_existenceChange() {
282         final Transition transition = createTestTransition(TRANSIT_OPEN);
283 
284         final Task openTask = createTask(mDisplayContent);
285         final ActivityRecord opening = createActivityRecord(openTask);
286         opening.setVisibleRequested(false); // starts invisible
287         final Task closeTask = createTask(mDisplayContent);
288         final ActivityRecord closing = createActivityRecord(closeTask);
289         closing.setVisibleRequested(true); // starts visible
290 
291         transition.collectExistenceChange(openTask);
292         transition.collect(opening);
293         transition.collect(closing);
294         opening.setVisibleRequested(true);
295         closing.setVisibleRequested(false);
296 
297         ArrayList<WindowContainer> targets = Transition.calculateTargets(
298                 transition.mParticipants, transition.mChanges);
299         TransitionInfo info = Transition.calculateTransitionInfo(
300                 0, 0, targets, transition.mChanges, mMockT);
301         assertEquals(2, info.getChanges().size());
302         // There was an existence change on open, so it should be OPEN rather than SHOW
303         assertEquals(TRANSIT_OPEN,
304                 info.getChange(openTask.mRemoteToken.toWindowContainerToken()).getMode());
305         // No exestence change on closing, so HIDE rather than CLOSE
306         assertEquals(TRANSIT_TO_BACK,
307                 info.getChange(closeTask.mRemoteToken.toWindowContainerToken()).getMode());
308     }
309 
310     @Test
testCreateInfo_ordering()311     public void testCreateInfo_ordering() {
312         final Transition transition = createTestTransition(TRANSIT_OPEN);
313         // pick some number with a high enough chance of being out-of-order when added to set.
314         final int taskCount = 6;
315 
316         final Task[] tasks = new Task[taskCount];
317         for (int i = 0; i < taskCount; ++i) {
318             // Each add goes on top, so at the end of this, task[9] should be on top
319             tasks[i] = createTask(mDisplayContent,
320                     WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
321             final ActivityRecord act = createActivityRecord(tasks[i]);
322             // alternate so that the transition doesn't get promoted to the display area
323             act.setVisibleRequested((i % 2) == 0); // starts invisible
324         }
325 
326         // doesn't matter which order collected since participants is a set
327         for (int i = 0; i < taskCount; ++i) {
328             transition.collectExistenceChange(tasks[i]);
329             final ActivityRecord act = tasks[i].getTopMostActivity();
330             transition.collect(act);
331             tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0);
332         }
333 
334         ArrayList<WindowContainer> targets = Transition.calculateTargets(
335                 transition.mParticipants, transition.mChanges);
336         TransitionInfo info = Transition.calculateTransitionInfo(
337                 0, 0, targets, transition.mChanges, mMockT);
338         assertEquals(taskCount, info.getChanges().size());
339         // verify order is top-to-bottem
340         for (int i = 0; i < taskCount; ++i) {
341             assertEquals(tasks[taskCount - i - 1].mRemoteToken.toWindowContainerToken(),
342                     info.getChanges().get(i).getContainer());
343         }
344     }
345 
346     @Test
testCreateInfo_wallpaper()347     public void testCreateInfo_wallpaper() {
348         final Transition transition = createTestTransition(TRANSIT_OPEN);
349         // pick some number with a high enough chance of being out-of-order when added to set.
350         final int taskCount = 4;
351         final int showWallpaperTask = 2;
352 
353         final Task[] tasks = new Task[taskCount];
354         for (int i = 0; i < taskCount; ++i) {
355             // Each add goes on top, so at the end of this, task[9] should be on top
356             tasks[i] = createTask(mDisplayContent,
357                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
358             final ActivityRecord act = createActivityRecord(tasks[i]);
359             // alternate so that the transition doesn't get promoted to the display area
360             act.setVisibleRequested((i % 2) == 0); // starts invisible
361             act.visibleIgnoringKeyguard = (i % 2) == 0;
362             if (i == showWallpaperTask) {
363                 doReturn(true).when(act).showWallpaper();
364             }
365         }
366 
367         final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm,
368                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */));
369         final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
370                 "wallpaperWindow");
371         wallpaperWindowToken.setVisibleRequested(false);
372         transition.collect(wallpaperWindowToken);
373         wallpaperWindowToken.setVisibleRequested(true);
374         wallpaperWindow.mHasSurface = true;
375 
376         // doesn't matter which order collected since participants is a set
377         for (int i = 0; i < taskCount; ++i) {
378             transition.collectExistenceChange(tasks[i]);
379             final ActivityRecord act = tasks[i].getTopMostActivity();
380             transition.collect(act);
381             tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0);
382         }
383 
384         ArrayList<WindowContainer> targets = Transition.calculateTargets(
385                 transition.mParticipants, transition.mChanges);
386         TransitionInfo info = Transition.calculateTransitionInfo(
387                 0, 0, targets, transition.mChanges, mMockT);
388         // verify that wallpaper is at bottom
389         assertEquals(taskCount + 1, info.getChanges().size());
390         // The wallpaper is not organized, so it won't have a token; however, it will be marked
391         // as IS_WALLPAPER
392         assertEquals(FLAG_IS_WALLPAPER,
393                 info.getChanges().get(info.getChanges().size() - 1).getFlags());
394         assertEquals(FLAG_SHOW_WALLPAPER, info.getChange(
395                 tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags());
396     }
397 
398     @Test
testCreateInfo_PromoteSimilarClose()399     public void testCreateInfo_PromoteSimilarClose() {
400         final Transition transition = createTestTransition(TRANSIT_CLOSE);
401         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
402         ArraySet<WindowContainer> participants = transition.mParticipants;
403 
404         final Task topTask = createTask(mDisplayContent);
405         final Task belowTask = createTask(mDisplayContent);
406         final ActivityRecord showing = createActivityRecord(belowTask);
407         final ActivityRecord hiding = createActivityRecord(topTask);
408         final ActivityRecord closing = createActivityRecord(topTask);
409         // Start states.
410         changes.put(topTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
411         changes.put(belowTask, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
412         changes.put(showing, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
413         changes.put(hiding, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
414         changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */));
415         fillChangeMap(changes, topTask);
416         // End states.
417         showing.setVisibleRequested(true);
418         closing.setVisibleRequested(false);
419         hiding.setVisibleRequested(false);
420 
421         participants.add(belowTask);
422         participants.add(hiding);
423         participants.add(closing);
424         ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
425         assertEquals(2, targets.size());
426         assertTrue(targets.contains(belowTask));
427         assertTrue(targets.contains(topTask));
428     }
429 
430     @Test
testCreateInfo_PromoteSimilarOpen()431     public void testCreateInfo_PromoteSimilarOpen() {
432         final Transition transition = createTestTransition(TRANSIT_OPEN);
433         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
434         ArraySet<WindowContainer> participants = transition.mParticipants;
435 
436         final Task topTask = createTask(mDisplayContent);
437         final Task belowTask = createTask(mDisplayContent);
438         final ActivityRecord showing = createActivityRecord(topTask);
439         final ActivityRecord opening = createActivityRecord(topTask);
440         final ActivityRecord closing = createActivityRecord(belowTask);
441         // Start states.
442         changes.put(topTask, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
443         changes.put(belowTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
444         changes.put(showing, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
445         changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
446         changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
447         fillChangeMap(changes, topTask);
448         // End states.
449         showing.setVisibleRequested(true);
450         opening.setVisibleRequested(true);
451         closing.setVisibleRequested(false);
452 
453         participants.add(belowTask);
454         participants.add(showing);
455         participants.add(opening);
456         ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
457         assertEquals(2, targets.size());
458         assertTrue(targets.contains(belowTask));
459         assertTrue(targets.contains(topTask));
460     }
461 
462     @Test
testTargets_noIntermediatesToWallpaper()463     public void testTargets_noIntermediatesToWallpaper() {
464         final Transition transition = createTestTransition(TRANSIT_OPEN);
465 
466         final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
467                 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
468         // Make DA organized so we can check that they don't get included.
469         WindowContainer parent = wallpaperWindowToken.getParent();
470         makeDisplayAreaOrganized(parent, mDisplayContent);
471         final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
472                 "wallpaperWindow");
473         wallpaperWindowToken.setVisibleRequested(false);
474         transition.collect(wallpaperWindowToken);
475         wallpaperWindowToken.setVisibleRequested(true);
476         wallpaperWindow.mHasSurface = true;
477         doReturn(true).when(mDisplayContent).isAttached();
478         transition.collect(mDisplayContent);
479         assertFalse("The change of non-interesting window container should be skipped",
480                 transition.mChanges.containsKey(mDisplayContent.getParent()));
481         mDisplayContent.getWindowConfiguration().setRotation(
482                 (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
483 
484         ArrayList<WindowContainer> targets = Transition.calculateTargets(
485                 transition.mParticipants, transition.mChanges);
486         TransitionInfo info = Transition.calculateTransitionInfo(
487                 0, 0, targets, transition.mChanges, mMockT);
488         // The wallpaper is not organized, so it won't have a token; however, it will be marked
489         // as IS_WALLPAPER
490         assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
491         // Make sure no intermediate display areas were pulled in between wallpaper and display.
492         assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(),
493                 info.getChanges().get(0).getParent());
494     }
495 
496     @Test
testRunningRemoteTransition()497     public void testRunningRemoteTransition() {
498         final TestTransitionPlayer testPlayer = new TestTransitionPlayer(
499                 mAtm.getTransitionController(), mAtm.mWindowOrganizerController);
500         final WindowProcessController playerProc = mSystemServicesTestRule.addProcess(
501                 "pkg.player", "proc.player", 5000 /* pid */, 5000 /* uid */);
502         testPlayer.mController.registerTransitionPlayer(testPlayer, playerProc);
503         doReturn(mock(IBinder.class)).when(playerProc.getThread()).asBinder();
504         final WindowProcessController delegateProc = mSystemServicesTestRule.addProcess(
505                 "pkg.delegate", "proc.delegate", 6000 /* pid */, 6000 /* uid */);
506         doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder();
507         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
508         final TransitionController controller = app.mTransitionController;
509         final Transition transition = controller.createTransition(TRANSIT_OPEN);
510         final RemoteTransition remoteTransition = new RemoteTransition(
511                 mock(IRemoteTransition.class));
512         remoteTransition.setAppThread(delegateProc.getThread());
513         transition.collectExistenceChange(app.getTask());
514         controller.requestStartTransition(transition, app.getTask(), remoteTransition,
515                 null /* displayChange */);
516         testPlayer.startTransition();
517         testPlayer.onTransactionReady(app.getSyncTransaction());
518         assertTrue(playerProc.isRunningRemoteTransition());
519         assertTrue(delegateProc.isRunningRemoteTransition());
520         assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
521 
522         testPlayer.finish();
523         assertFalse(playerProc.isRunningRemoteTransition());
524         assertFalse(delegateProc.isRunningRemoteTransition());
525         assertFalse(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
526     }
527 
528     @Test
testOpenActivityInTheSameTaskWithDisplayChange()529     public void testOpenActivityInTheSameTaskWithDisplayChange() {
530         final ActivityRecord closing = createActivityRecord(mDisplayContent);
531         closing.setVisibleRequested(true);
532         final Task task = closing.getTask();
533         makeTaskOrganized(task);
534         final ActivityRecord opening = createActivityRecord(task);
535         opening.setVisibleRequested(false);
536         makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent);
537         final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent };
538         final Transition transition = createTestTransition(TRANSIT_OPEN);
539         for (WindowContainer<?> wc : wcs) {
540             transition.collect(wc);
541         }
542         closing.setVisibleRequested(false);
543         opening.setVisibleRequested(true);
544         final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1;
545         for (WindowContainer<?> wc : wcs) {
546             wc.getWindowConfiguration().setRotation(newRotation);
547         }
548 
549         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
550                 transition.mParticipants, transition.mChanges);
551         // Especially the activities must be in the targets.
552         assertTrue(targets.containsAll(Arrays.asList(wcs)));
553     }
554 
555     @Test
testIndependent()556     public void testIndependent() {
557         final Transition transition = createTestTransition(TRANSIT_OPEN);
558         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
559         ArraySet<WindowContainer> participants = transition.mParticipants;
560 
561         final Task openTask = createTask(mDisplayContent);
562         final Task openInOpenTask = createTaskInRootTask(openTask, 0);
563         final ActivityRecord openInOpen = createActivityRecord(openInOpenTask);
564 
565         final Task changeTask = createTask(mDisplayContent);
566         final Task changeInChangeTask = createTaskInRootTask(changeTask, 0);
567         final Task openInChangeTask = createTaskInRootTask(changeTask, 0);
568         final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask);
569         final ActivityRecord openInChange = createActivityRecord(openInChangeTask);
570         // set organizer for everything so that they all get added to transition info
571         makeTaskOrganized(openTask, openInOpenTask, changeTask, changeInChangeTask,
572                 openInChangeTask);
573 
574         // Start states.
575         changes.put(openTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
576         changes.put(changeTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
577         changes.put(openInOpenTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
578         changes.put(openInChangeTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
579         changes.put(changeInChangeTask,
580                 new Transition.ChangeInfo(true /* vis */, false /* exChg */));
581         changes.put(openInOpen, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
582         changes.put(openInChange, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
583         changes.put(changeInChange, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
584         fillChangeMap(changes, openTask);
585         // End states.
586         changeInChange.setVisibleRequested(true);
587         openInOpen.setVisibleRequested(true);
588         openInChange.setVisibleRequested(true);
589 
590         final int transit = transition.mType;
591         int flags = 0;
592 
593         // Check full promotion from leaf
594         participants.add(changeInChange);
595         participants.add(openInOpen);
596         participants.add(openInChange);
597         // Explicitly add changeTask (to test independence with parents)
598         participants.add(changeTask);
599         final ArrayList<WindowContainer> targets =
600                 Transition.calculateTargets(participants, changes);
601         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
602                 mMockT);
603         // Root changes should always be considered independent
604         assertTrue(isIndependent(
605                 info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info));
606         assertTrue(isIndependent(
607                 info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info));
608 
609         // Children of a open/close change are not independent
610         assertFalse(isIndependent(
611                 info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info));
612 
613         // Non-root changes are not independent
614         assertFalse(isIndependent(
615                 info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info));
616 
617         // open/close within a change are independent
618         assertTrue(isIndependent(
619                 info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
620     }
621 
622     @Test
testOpenOpaqueTask()623     public void testOpenOpaqueTask() {
624         final Transition transition = createTestTransition(TRANSIT_OPEN);
625         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
626         ArraySet<WindowContainer> participants = transition.mParticipants;
627 
628         final Task newTask = createTask(mDisplayContent);
629         doReturn(false).when(newTask).isTranslucent(any());
630         final Task oldTask = createTask(mDisplayContent);
631         doReturn(false).when(oldTask).isTranslucent(any());
632 
633         final ActivityRecord closing = createActivityRecord(oldTask);
634         closing.setOccludesParent(true);
635         final ActivityRecord opening = createActivityRecord(newTask);
636         opening.setOccludesParent(false);
637         // Start states.
638         changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
639         changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
640         changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
641         changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
642         fillChangeMap(changes, newTask);
643         // End states.
644         closing.setVisibleRequested(true);
645         opening.setVisibleRequested(true);
646 
647         final int transit = transition.mType;
648         int flags = 0;
649 
650         // Check basic both tasks participating
651         participants.add(oldTask);
652         participants.add(newTask);
653         ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
654         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
655                 mMockT);
656         assertEquals(2, info.getChanges().size());
657         assertEquals(transit, info.getType());
658 
659         assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) == 0);
660         assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0);
661     }
662 
663     @Test
testOpenTranslucentTask()664     public void testOpenTranslucentTask() {
665         final Transition transition = createTestTransition(TRANSIT_OPEN);
666         ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
667         ArraySet<WindowContainer> participants = transition.mParticipants;
668 
669         final Task newTask = createTask(mDisplayContent);
670         doReturn(true).when(newTask).isTranslucent(any());
671         final Task oldTask = createTask(mDisplayContent);
672         doReturn(false).when(oldTask).isTranslucent(any());
673 
674         final ActivityRecord closing = createActivityRecord(oldTask);
675         closing.setOccludesParent(true);
676         final ActivityRecord opening = createActivityRecord(newTask);
677         opening.setOccludesParent(false);
678         // Start states.
679         changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
680         changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
681         changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
682         changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
683         fillChangeMap(changes, newTask);
684         // End states.
685         closing.setVisibleRequested(true);
686         opening.setVisibleRequested(true);
687 
688         final int transit = transition.mType;
689         int flags = 0;
690 
691         // Check basic both tasks participating
692         participants.add(oldTask);
693         participants.add(newTask);
694         ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
695         TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
696                 mMockT);
697         assertEquals(2, info.getChanges().size());
698         assertEquals(transit, info.getType());
699 
700         assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) != 0);
701         assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0);
702     }
703 
704     @Test
testTimeout()705     public void testTimeout() {
706         final TransitionController controller = new TransitionController(mAtm,
707                 mock(TaskSnapshotController.class), mock(TransitionTracer.class));
708         final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
709         final CountDownLatch latch = new CountDownLatch(1);
710         // When the timeout is reached, it will finish the sync-group and notify transaction ready.
711         final Transition t = new Transition(TRANSIT_OPEN, 0 /* flags */, controller, sync) {
712             @Override
713             public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
714                 latch.countDown();
715             }
716         };
717         t.startCollecting(10 /* timeoutMs */);
718         assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS)));
719     }
720 
721     @Test
testTransitionBounds()722     public void testTransitionBounds() {
723         registerTestTransitionPlayer();
724         final int offset = 10;
725         final Function<WindowContainer<?>, TransitionInfo.Change> test = wc -> {
726             final Transition transition = wc.mTransitionController.createTransition(TRANSIT_OPEN);
727             transition.collect(wc);
728             final int nextRotation = (wc.getWindowConfiguration().getRotation() + 1) % 4;
729             wc.getWindowConfiguration().setRotation(nextRotation);
730             wc.getWindowConfiguration().setDisplayRotation(nextRotation);
731             final Rect bounds = wc.getWindowConfiguration().getBounds();
732             // Flip the bounds with offset.
733             wc.getWindowConfiguration().setBounds(
734                     new Rect(offset, offset, bounds.height(), bounds.width()));
735             final int flags = 0;
736             final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, flags,
737                     Transition.calculateTargets(transition.mParticipants, transition.mChanges),
738                     transition.mChanges, mMockT);
739             transition.abort();
740             return info.getChanges().get(0);
741         };
742 
743         final ActivityRecord app = createActivityRecord(mDisplayContent);
744         final TransitionInfo.Change changeOfActivity = test.apply(app);
745         // There will be letterbox if the activity bounds don't match parent, so always use its
746         // parent bounds for animation.
747         assertEquals(app.getParent().getBounds(), changeOfActivity.getEndAbsBounds());
748         final int endRotation = app.mTransitionController.useShellTransitionsRotation()
749                 ? app.getWindowConfiguration().getRotation()
750                 // Without shell rotation, fixed rotation is done by core so the info should not
751                 // contain rotation change.
752                 : app.getParent().getWindowConfiguration().getRotation();
753         assertEquals(endRotation, changeOfActivity.getEndRotation());
754 
755         // Non-activity target always uses its configuration for end info.
756         final Task task = app.getTask();
757         final TransitionInfo.Change changeOfTask = test.apply(task);
758         assertEquals(task.getBounds(), changeOfTask.getEndAbsBounds());
759         assertEquals(new Point(offset, offset), changeOfTask.getEndRelOffset());
760         assertEquals(task.getWindowConfiguration().getRotation(), changeOfTask.getEndRotation());
761     }
762 
763     @Test
testDisplayRotationChange()764     public void testDisplayRotationChange() {
765         final Task task = createActivityRecord(mDisplayContent).getTask();
766         final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
767         final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
768         final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
769         final WindowToken decorToken = new WindowToken.Builder(mWm, mock(IBinder.class),
770                 TYPE_NAVIGATION_BAR_PANEL).setDisplayContent(mDisplayContent)
771                 .setRoundedCornerOverlay(true).build();
772         final WindowState screenDecor =
773                 createWindow(null, decorToken.windowType, decorToken, "screenDecor");
774         final WindowState[] windows = { statusBar, navBar, ime, screenDecor };
775         makeWindowVisible(windows);
776         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
777         mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
778         final TestTransitionPlayer player = registerTestTransitionPlayer();
779 
780         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
781         mDisplayContent.setLastHasContent();
782         mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */,
783                 null /* displayChange */);
784         assertEquals(WindowContainer.SYNC_STATE_NONE, statusBar.mSyncState);
785         assertEquals(WindowContainer.SYNC_STATE_NONE, navBar.mSyncState);
786         assertEquals(WindowContainer.SYNC_STATE_NONE, screenDecor.mSyncState);
787         assertEquals(WindowContainer.SYNC_STATE_WAITING_FOR_DRAW, ime.mSyncState);
788 
789         final AsyncRotationController asyncRotationController =
790                 mDisplayContent.getAsyncRotationController();
791         assertNotNull(asyncRotationController);
792         for (WindowState w : windows) {
793             w.setOrientationChanging(true);
794         }
795         player.startTransition();
796 
797         assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
798         assertFalse(mDisplayContent.mTransitionController.isCollecting(decorToken));
799         assertTrue(ime.mToken.inTransition());
800         assertTrue(task.inTransition());
801         assertTrue(asyncRotationController.isTargetToken(decorToken));
802         assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
803 
804         if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
805             // Only seamless window syncs its draw transaction with transition.
806             assertFalse(asyncRotationController.handleFinishDrawing(statusBar, mMockT));
807             assertTrue(asyncRotationController.handleFinishDrawing(screenDecor, mMockT));
808         }
809         screenDecor.setOrientationChanging(false);
810         // Status bar finishes drawing before the start transaction. Its fade-in animation will be
811         // executed until the transaction is committed, so it is still in target tokens.
812         statusBar.setOrientationChanging(false);
813         assertTrue(asyncRotationController.isTargetToken(statusBar.mToken));
814 
815         final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
816         final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
817                 onRotationTransactionReady(player, startTransaction);
818 
819         // The transaction is committed, so fade-in animation for status bar is consumed.
820         transactionCommittedListener.onTransactionCommitted();
821         assertFalse(asyncRotationController.isTargetToken(statusBar.mToken));
822         assertShouldFreezeInsetsPosition(asyncRotationController, navBar, false);
823 
824         // Navigation bar finishes drawing after the start transaction, so its fade-in animation
825         // can execute directly.
826         navBar.setOrientationChanging(false);
827         assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
828         assertNull(mDisplayContent.getAsyncRotationController());
829     }
830 
831     @Test
testAppTransitionWithRotationChange()832     public void testAppTransitionWithRotationChange() {
833         final TestTransitionPlayer player = registerTestTransitionPlayer();
834         final boolean useFixedRotation = !player.mController.useShellTransitionsRotation();
835         if (useFixedRotation) {
836             testFixedRotationOpen(player);
837         } else {
838             testShellRotationOpen(player);
839         }
840     }
841 
testShellRotationOpen(TestTransitionPlayer player)842     private void testShellRotationOpen(TestTransitionPlayer player) {
843         final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
844         makeWindowVisible(statusBar);
845         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
846         final ActivityRecord app = createActivityRecord(mDisplayContent);
847         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
848         app.mTransitionController.requestStartTransition(transition, app.getTask(),
849                 null /* remoteTransition */, null /* displayChange */);
850         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
851         final int anyChanges = 1;
852         mDisplayContent.setLastHasContent();
853         mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
854         transition.setKnownConfigChanges(mDisplayContent, anyChanges);
855         final AsyncRotationController asyncRotationController =
856                 mDisplayContent.getAsyncRotationController();
857         assertNotNull(asyncRotationController);
858         assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
859 
860         statusBar.setOrientationChanging(true);
861         player.startTransition();
862         // Non-app windows should not be collected.
863         assertFalse(statusBar.mToken.inTransition());
864         assertTrue(app.getTask().inTransition());
865 
866         final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
867         final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
868         doReturn(true).when(leash).isValid();
869         final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
870                 onRotationTransactionReady(player, startTransaction);
871         // The leash should be unrotated.
872         verify(startTransaction).setMatrix(eq(leash), any(), any());
873 
874         // The redrawn window will be faded in when the transition finishes. And because this test
875         // only use one non-activity window, the fade rotation controller should also be cleared.
876         statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
877         final SurfaceControl.Transaction postDrawTransaction =
878                 mock(SurfaceControl.Transaction.class);
879         final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction,
880                 Integer.MAX_VALUE);
881         assertFalse(layoutNeeded);
882 
883         transactionCommittedListener.onTransactionCommitted();
884         player.finish();
885         // The controller should capture the draw transaction and merge it when preparing to run
886         // fade-in animation.
887         verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction));
888         assertNull(mDisplayContent.getAsyncRotationController());
889     }
890 
testFixedRotationOpen(TestTransitionPlayer player)891     private void testFixedRotationOpen(TestTransitionPlayer player) {
892         final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
893         makeWindowVisible(statusBar);
894         mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
895         final ActivityRecord app = createActivityRecord(mDisplayContent);
896         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
897         app.mTransitionController.requestStartTransition(transition, app.getTask(),
898                 null /* remoteTransition */, null /* displayChange */);
899         app.mTransitionController.collectExistenceChange(app.getTask());
900         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
901         final AsyncRotationController asyncRotationController =
902                 mDisplayContent.getAsyncRotationController();
903         assertNotNull(asyncRotationController);
904         assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
905         assertTrue(app.getTask().inTransition());
906 
907         player.start();
908         player.finish();
909         app.getTask().finishSync(mWm.mTransactionFactory.get(), false /* cancel */);
910 
911         // The open transition is finished. Continue to play seamless display change transition,
912         // so the previous async rotation controller should still exist.
913         mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
914         mDisplayContent.setLastHasContent();
915         mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */);
916         assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
917         assertNotNull(mDisplayContent.getAsyncRotationController());
918 
919         // The app is still in transition, so the callback should be no-op.
920         mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app);
921         assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
922 
923         statusBar.setOrientationChanging(true);
924         player.startTransition();
925         // Non-app windows should not be collected.
926         assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
927 
928         onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
929         assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
930                 mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
931         player.finish();
932 
933         // The controller should be cleared if the target windows are drawn.
934         statusBar.finishDrawing(mWm.mTransactionFactory.get(), Integer.MAX_VALUE);
935         statusBar.setOrientationChanging(false);
936         assertNull(mDisplayContent.getAsyncRotationController());
937     }
938 
assertShouldFreezeInsetsPosition(AsyncRotationController controller, WindowState w, boolean freeze)939     private static void assertShouldFreezeInsetsPosition(AsyncRotationController controller,
940             WindowState w, boolean freeze) {
941         if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
942             // Non blast sync should never freeze insets position.
943             freeze = false;
944         }
945         assertEquals(freeze, controller.shouldFreezeInsetsPosition(w));
946     }
947 
948     @Test
testDeferRotationForTransientLaunch()949     public void testDeferRotationForTransientLaunch() {
950         final TestTransitionPlayer player = registerTestTransitionPlayer();
951         assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation());
952         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
953         final ActivityRecord home = new ActivityBuilder(mAtm)
954                 .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask())
955                 .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build();
956         final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN);
957         final int prevRotation = mDisplayContent.getRotation();
958         transition.setTransientLaunch(home, null /* restoreBelow */);
959         home.mTransitionController.requestStartTransition(transition, home.getTask(),
960                 null /* remoteTransition */, null /* displayChange */);
961         transition.collectExistenceChange(home);
962         home.setVisibleRequested(true);
963         mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
964         doReturn(true).when(home).hasFixedRotationTransform(any());
965         player.startTransition();
966         player.onTransactionReady(mDisplayContent.getSyncTransaction());
967 
968         final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
969         final RemoteDisplayChangeController displayChangeController = mDisplayContent
970                 .mRemoteDisplayChangeController;
971         spyOn(displayRotation);
972         spyOn(displayChangeController);
973         doReturn(true).when(displayChangeController).isWaitingForRemoteDisplayChange();
974         doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation(
975                 anyInt() /* orientation */, anyInt() /* lastRotation */);
976         // Rotation update is skipped while the recents animation is running.
977         assertFalse(mDisplayContent.updateRotationUnchecked());
978         assertEquals(SCREEN_ORIENTATION_UNSET, displayRotation.getLastOrientation());
979         // Return to the app without fixed orientation from recents.
980         app.moveFocusableActivityToTop("test");
981         player.finish();
982         // The display should be updated to the changed orientation after the animation is finish.
983         assertNotEquals(mDisplayContent.getRotation(), prevRotation);
984     }
985 
986     @Test
testIntermediateVisibility()987     public void testIntermediateVisibility() {
988         final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
989         final TransitionController controller = new TransitionController(mAtm, snapshotController,
990                 mock(TransitionTracer.class));
991         final ITransitionPlayer player = new ITransitionPlayer.Default();
992         controller.registerTransitionPlayer(player, null /* playerProc */);
993         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
994 
995         // Start out with task2 visible and set up a transition that closes task2 and opens task1
996         final Task task1 = createTask(mDisplayContent);
997         final ActivityRecord activity1 = createActivityRecord(task1);
998         activity1.setVisibleRequested(false);
999         activity1.setVisible(false);
1000         final Task task2 = createTask(mDisplayContent);
1001         makeTaskOrganized(task1, task2);
1002         final ActivityRecord activity2 = createActivityRecord(task1);
1003         activity2.setVisibleRequested(true);
1004         activity2.setVisible(true);
1005 
1006         openTransition.collectExistenceChange(task1);
1007         openTransition.collectExistenceChange(activity1);
1008         openTransition.collectExistenceChange(task2);
1009         openTransition.collectExistenceChange(activity2);
1010 
1011         activity1.setVisibleRequested(true);
1012         activity1.setVisible(true);
1013         activity2.setVisibleRequested(false);
1014 
1015         // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
1016         // We didn't call abort on the transition itself, so it will still run onTransactionReady
1017         // normally.
1018         mWm.mSyncEngine.abort(openTransition.getSyncId());
1019 
1020         // Before finishing openTransition, we are now going to simulate closing task1 to return
1021         // back to (open) task2.
1022         final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
1023 
1024         closeTransition.collectExistenceChange(task1);
1025         closeTransition.collectExistenceChange(activity1);
1026         closeTransition.collectExistenceChange(task2);
1027         closeTransition.collectExistenceChange(activity2);
1028 
1029         activity1.setVisibleRequested(false);
1030         activity2.setVisibleRequested(true);
1031 
1032         openTransition.finishTransition();
1033 
1034         // We finished the openTransition. Even though activity1 is visibleRequested=false, since
1035         // the closeTransition animation hasn't played yet, make sure that we didn't commit
1036         // visible=false on activity1 since it needs to remain visible for the animation.
1037         assertTrue(activity1.isVisible());
1038         assertTrue(activity2.isVisible());
1039 
1040         // Using abort to force-finish the sync (since we obviously can't wait for drawing).
1041         // We didn't call abort on the actual transition, so it will still run onTransactionReady
1042         // normally.
1043         mWm.mSyncEngine.abort(closeTransition.getSyncId());
1044 
1045         closeTransition.finishTransition();
1046 
1047         assertFalse(activity1.isVisible());
1048         assertTrue(activity2.isVisible());
1049     }
1050 
1051     @Test
testTransientLaunch()1052     public void testTransientLaunch() {
1053         final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
1054         final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>();
1055         final TransitionController controller = new TransitionController(mAtm, snapshotController,
1056                 mock(TransitionTracer.class)) {
1057             @Override
1058             protected void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
1059                 if (ar.mEnteringAnimation) {
1060                     enteringAnimReports.add(ar);
1061                 }
1062                 super.dispatchLegacyAppTransitionFinished(ar);
1063             }
1064         };
1065         final ITransitionPlayer player = new ITransitionPlayer.Default();
1066         controller.registerTransitionPlayer(player, null /* playerProc */);
1067         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
1068 
1069         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1070         final Task task1 = createTask(mDisplayContent);
1071         final ActivityRecord activity1 = createActivityRecord(task1);
1072         activity1.setVisibleRequested(false);
1073         activity1.setVisible(false);
1074         final Task task2 = createTask(mDisplayContent);
1075         makeTaskOrganized(task1, task2);
1076         final ActivityRecord activity2 = createActivityRecord(task2);
1077         activity2.setVisibleRequested(true);
1078         activity2.setVisible(true);
1079 
1080         openTransition.collectExistenceChange(task1);
1081         openTransition.collectExistenceChange(activity1);
1082         openTransition.collectExistenceChange(task2);
1083         openTransition.collectExistenceChange(activity2);
1084 
1085         activity1.setVisibleRequested(true);
1086         activity1.setVisible(true);
1087         activity2.setVisibleRequested(false);
1088 
1089         // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
1090         // We didn't call abort on the transition itself, so it will still run onTransactionReady
1091         // normally.
1092         mWm.mSyncEngine.abort(openTransition.getSyncId());
1093 
1094         verify(snapshotController, times(1)).recordTaskSnapshot(eq(task2), eq(false));
1095 
1096         openTransition.finishTransition();
1097 
1098         // We are now going to simulate closing task1 to return back to (open) task2.
1099         final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
1100 
1101         closeTransition.collectExistenceChange(task1);
1102         closeTransition.collectExistenceChange(activity1);
1103         closeTransition.collectExistenceChange(task2);
1104         closeTransition.collectExistenceChange(activity2);
1105         closeTransition.setTransientLaunch(activity2, null /* restoreBelow */);
1106 
1107         activity1.setVisibleRequested(false);
1108         activity2.setVisibleRequested(true);
1109         activity2.setVisible(true);
1110 
1111         // Using abort to force-finish the sync (since we obviously can't wait for drawing).
1112         // We didn't call abort on the actual transition, so it will still run onTransactionReady
1113         // normally.
1114         mWm.mSyncEngine.abort(closeTransition.getSyncId());
1115 
1116         // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be
1117         // called until finish).
1118         verify(snapshotController, times(0)).recordTaskSnapshot(eq(task1), eq(false));
1119 
1120         enteringAnimReports.clear();
1121         closeTransition.finishTransition();
1122 
1123         verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false));
1124         assertTrue(enteringAnimReports.contains(activity2));
1125     }
1126 
1127     @Test
testNotReadyPushPop()1128     public void testNotReadyPushPop() {
1129         final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
1130         final TransitionController controller = new TransitionController(mAtm, snapshotController,
1131                 mock(TransitionTracer.class));
1132         final ITransitionPlayer player = new ITransitionPlayer.Default();
1133         controller.registerTransitionPlayer(player, null /* playerProc */);
1134         final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
1135 
1136         // Start out with task2 visible and set up a transition that closes task2 and opens task1
1137         final Task task1 = createTask(mDisplayContent);
1138         openTransition.collectExistenceChange(task1);
1139 
1140         assertFalse(openTransition.allReady());
1141 
1142         openTransition.setAllReady();
1143 
1144         openTransition.deferTransitionReady();
1145         assertFalse(openTransition.allReady());
1146 
1147         openTransition.continueTransitionReady();
1148         assertTrue(openTransition.allReady());
1149     }
1150 
1151     @Test
testIsBehindStartingWindowChange()1152     public void testIsBehindStartingWindowChange() {
1153         final Transition transition = createTestTransition(TRANSIT_OPEN);
1154         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1155         final ArraySet<WindowContainer> participants = transition.mParticipants;
1156 
1157         final Task task = createTask(mDisplayContent);
1158         final ActivityRecord activity0 = createActivityRecord(task);
1159         final ActivityRecord activity1 = createActivityRecord(task);
1160         doReturn(true).when(activity1).hasStartingWindow();
1161 
1162         // Start states.
1163         changes.put(activity0, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
1164         changes.put(activity1, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
1165         // End states.
1166         activity0.setVisibleRequested(false);
1167         activity1.setVisibleRequested(true);
1168 
1169         participants.add(activity0);
1170         participants.add(activity1);
1171         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1172                 participants, changes);
1173         final TransitionInfo info = Transition.calculateTransitionInfo(
1174                 transition.mType, 0 /* flags */, targets, changes, mMockT);
1175 
1176         // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting
1177         // window should cover the whole Task.
1178         assertEquals(2, info.getChanges().size());
1179         assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
1180         assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
1181 
1182     }
1183 
1184     @Test
testFlagInTaskWithEmbeddedActivity()1185     public void testFlagInTaskWithEmbeddedActivity() {
1186         final Transition transition = createTestTransition(TRANSIT_OPEN);
1187         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1188         final ArraySet<WindowContainer> participants = transition.mParticipants;
1189 
1190         final Task task = createTask(mDisplayContent);
1191         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1192         assertFalse(nonEmbeddedActivity.isEmbedded());
1193         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1194         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1195                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1196         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1197                 .setParentTask(task)
1198                 .createActivityCount(2)
1199                 .setOrganizer(organizer)
1200                 .build();
1201         final ActivityRecord closingActivity = embeddedTf.getBottomMostActivity();
1202         final ActivityRecord openingActivity = embeddedTf.getTopMostActivity();
1203         // Start states.
1204         changes.put(embeddedTf, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
1205         changes.put(closingActivity, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
1206         changes.put(openingActivity, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
1207         changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */,
1208                 false /* exChg */));
1209         // End states.
1210         closingActivity.setVisibleRequested(false);
1211         openingActivity.setVisibleRequested(true);
1212         nonEmbeddedActivity.setVisibleRequested(false);
1213 
1214         participants.add(closingActivity);
1215         participants.add(openingActivity);
1216         participants.add(nonEmbeddedActivity);
1217         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1218                 participants, changes);
1219         final TransitionInfo info = Transition.calculateTransitionInfo(
1220                 transition.mType, 0 /* flags */, targets, changes, mMockT);
1221 
1222         // All windows in the Task should have FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY because the Task
1223         // contains embedded activity.
1224         assertEquals(3, info.getChanges().size());
1225         assertTrue(info.getChanges().get(0).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1226         assertTrue(info.getChanges().get(1).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1227         assertTrue(info.getChanges().get(2).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
1228     }
1229 
1230     @Test
testFlagFillsTask_embeddingNotFillingTask()1231     public void testFlagFillsTask_embeddingNotFillingTask() {
1232         final Transition transition = createTestTransition(TRANSIT_OPEN);
1233         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1234         final ArraySet<WindowContainer> participants = transition.mParticipants;
1235 
1236         final Task task = createTask(mDisplayContent);
1237         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1238         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1239         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1240         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1241         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1242                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1243         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1244                 .setParentTask(task)
1245                 .createActivityCount(1)
1246                 .setOrganizer(organizer)
1247                 .build();
1248         final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
1249         // Start states.
1250         changes.put(task, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
1251         changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */,
1252                 false /* exChg */));
1253         changes.put(embeddedTf, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
1254         // End states.
1255         nonEmbeddedActivity.setVisibleRequested(false);
1256         embeddedActivity.setVisibleRequested(true);
1257         embeddedTf.setBounds(new Rect(0, 0, 500, 500));
1258 
1259         participants.add(nonEmbeddedActivity);
1260         participants.add(embeddedTf);
1261         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1262                 participants, changes);
1263         final TransitionInfo info = Transition.calculateTransitionInfo(
1264                 transition.mType, 0 /* flags */, targets, changes, mMockT);
1265 
1266         // The embedded with bounds overridden should not have the flag.
1267         assertEquals(2, info.getChanges().size());
1268         assertFalse(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1269         assertEquals(embeddedTf.getBounds(), info.getChanges().get(0).getEndAbsBounds());
1270         assertFalse(info.getChanges().get(1).hasFlags(FLAG_FILLS_TASK));
1271     }
1272 
1273     @Test
testFlagFillsTask_openActivityFillingTask()1274     public void testFlagFillsTask_openActivityFillingTask() {
1275         final Transition transition = createTestTransition(TRANSIT_OPEN);
1276         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1277         final ArraySet<WindowContainer> participants = transition.mParticipants;
1278 
1279         final Task task = createTask(mDisplayContent);
1280         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1281         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1282         final ActivityRecord activity = createActivityRecord(task);
1283         // Start states: set bounds to make sure the start bounds is ignored if it is not visible.
1284         activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
1285         activity.setVisibleRequested(false);
1286         changes.put(activity, new Transition.ChangeInfo(activity));
1287         // End states: reset bounds to fill Task.
1288         activity.getConfiguration().windowConfiguration.setBounds(taskBounds);
1289         activity.setVisibleRequested(true);
1290 
1291         participants.add(activity);
1292         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1293                 participants, changes);
1294         final TransitionInfo info = Transition.calculateTransitionInfo(
1295                 transition.mType, 0 /* flags */, targets, changes, mMockT);
1296 
1297         // Opening activity that is filling Task after transition should have the flag.
1298         assertEquals(1, info.getChanges().size());
1299         assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1300     }
1301 
1302     @Test
testFlagFillsTask_closeActivityFillingTask()1303     public void testFlagFillsTask_closeActivityFillingTask() {
1304         final Transition transition = createTestTransition(TRANSIT_CLOSE);
1305         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1306         final ArraySet<WindowContainer> participants = transition.mParticipants;
1307 
1308         final Task task = createTask(mDisplayContent);
1309         final Rect taskBounds = new Rect(0, 0, 500, 1000);
1310         task.getConfiguration().windowConfiguration.setBounds(taskBounds);
1311         final ActivityRecord activity = createActivityRecord(task);
1312         // Start states: fills Task without override.
1313         activity.setVisibleRequested(true);
1314         changes.put(activity, new Transition.ChangeInfo(activity));
1315         // End states: set bounds to make sure the start bounds is ignored if it is not visible.
1316         activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
1317         activity.setVisibleRequested(false);
1318 
1319         participants.add(activity);
1320         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1321                 participants, changes);
1322         final TransitionInfo info = Transition.calculateTransitionInfo(
1323                 transition.mType, 0 /* flags */, targets, changes, mMockT);
1324 
1325         // Closing activity that is filling Task before transition should have the flag.
1326         assertEquals(1, info.getChanges().size());
1327         assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
1328     }
1329 
1330     @Test
testReparentChangeLastParent()1331     public void testReparentChangeLastParent() {
1332         final Transition transition = createTestTransition(TRANSIT_CHANGE);
1333         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1334         final ArraySet<WindowContainer> participants = transition.mParticipants;
1335 
1336         // Reparent activity in transition.
1337         final Task lastParent = createTask(mDisplayContent);
1338         final Task newParent = createTask(mDisplayContent);
1339         final ActivityRecord activity = createActivityRecord(lastParent);
1340         activity.setVisibleRequested(true);
1341         // Skip manipulate the SurfaceControl.
1342         doNothing().when(activity).setDropInputMode(anyInt());
1343         changes.put(activity, new Transition.ChangeInfo(activity));
1344         activity.reparent(newParent, POSITION_TOP);
1345         activity.setVisibleRequested(false);
1346 
1347         participants.add(activity);
1348         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1349                 participants, changes);
1350         final TransitionInfo info = Transition.calculateTransitionInfo(
1351                 transition.mType, 0 /* flags */, targets, changes, mMockT);
1352 
1353         // Change contains last parent info.
1354         assertEquals(1, info.getChanges().size());
1355         assertEquals(lastParent.mRemoteToken.toWindowContainerToken(),
1356                 info.getChanges().get(0).getLastParent());
1357     }
1358 
1359     @Test
testIncludeEmbeddedActivityReparent()1360     public void testIncludeEmbeddedActivityReparent() {
1361         final Transition transition = createTestTransition(TRANSIT_OPEN);
1362         final Task task = createTask(mDisplayContent);
1363         task.setBounds(new Rect(0, 0, 2000, 1000));
1364         final ActivityRecord activity = createActivityRecord(task);
1365         activity.setVisibleRequested(true);
1366         // Skip manipulate the SurfaceControl.
1367         doNothing().when(activity).setDropInputMode(anyInt());
1368         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1369         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1370                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1371         final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
1372                 .setParentTask(task)
1373                 .setOrganizer(organizer)
1374                 .build();
1375         // TaskFragment with different bounds from Task.
1376         embeddedTf.setBounds(new Rect(0, 0, 1000, 1000));
1377 
1378         // Start states.
1379         transition.collect(activity);
1380         transition.collectExistenceChange(embeddedTf);
1381 
1382         // End states.
1383         activity.reparent(embeddedTf, POSITION_TOP);
1384 
1385         // Verify that both activity and TaskFragment are included.
1386         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1387                 transition.mParticipants, transition.mChanges);
1388         assertTrue(targets.contains(embeddedTf));
1389         assertTrue(targets.contains(activity));
1390     }
1391 
1392     @Test
testChangeSetBackgroundColor()1393     public void testChangeSetBackgroundColor() {
1394         final Transition transition = createTestTransition(TRANSIT_CHANGE);
1395         final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
1396         final ArraySet<WindowContainer> participants = transition.mParticipants;
1397 
1398         // Test background color for Activity and embedded TaskFragment.
1399         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1400         mAtm.mTaskFragmentOrganizerController.registerOrganizer(
1401                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
1402         final Task task = createTask(mDisplayContent);
1403         final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
1404         final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
1405         final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
1406         final ActivityManager.TaskDescription taskDescription =
1407                 new ActivityManager.TaskDescription.Builder()
1408                         .setBackgroundColor(Color.YELLOW)
1409                         .build();
1410         task.setTaskDescription(taskDescription);
1411 
1412         // Start states:
1413         embeddedActivity.setVisibleRequested(true);
1414         nonEmbeddedActivity.setVisibleRequested(false);
1415         changes.put(embeddedTf, new Transition.ChangeInfo(embeddedTf));
1416         changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity));
1417         // End states:
1418         embeddedActivity.setVisibleRequested(false);
1419         nonEmbeddedActivity.setVisibleRequested(true);
1420 
1421         participants.add(embeddedTf);
1422         participants.add(nonEmbeddedActivity);
1423         final ArrayList<WindowContainer> targets = Transition.calculateTargets(
1424                 participants, changes);
1425         final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType,
1426                 0 /* flags */, targets, changes, mMockT);
1427 
1428         // Background color should be set on both Activity and embedded TaskFragment.
1429         final int expectedBackgroundColor = ColorUtils.setAlphaComponent(
1430                 taskDescription.getBackgroundColor(), 255);
1431         assertEquals(2, info.getChanges().size());
1432         assertEquals(expectedBackgroundColor, info.getChanges().get(0).getBackgroundColor());
1433         assertEquals(expectedBackgroundColor, info.getChanges().get(1).getBackgroundColor());
1434     }
1435 
1436     @Test
testTransitionVisibleChange()1437     public void testTransitionVisibleChange() {
1438         registerTestTransitionPlayer();
1439         final ActivityRecord app = createActivityRecord(mDisplayContent);
1440         final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */,
1441                 app.mTransitionController, mWm.mSyncEngine);
1442         app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE);
1443         final ArrayList<WindowContainer> freezeCalls = new ArrayList<>();
1444         transition.setContainerFreezer(new Transition.IContainerFreezer() {
1445             @Override
1446             public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
1447                 freezeCalls.add(wc);
1448                 return true;
1449             }
1450 
1451             @Override
1452             public void cleanUp(SurfaceControl.Transaction t) {
1453             }
1454         });
1455         final Task task = app.getTask();
1456         transition.collect(task);
1457         final Rect bounds = new Rect(task.getBounds());
1458         Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
1459         bounds.inset(10, 10);
1460         c.windowConfiguration.setBounds(bounds);
1461         task.onRequestedOverrideConfigurationChanged(c);
1462         assertTrue(freezeCalls.contains(task));
1463         transition.abort();
1464     }
1465 
1466     @Test
testDeferTransitionReady_deferStartedTransition()1467     public void testDeferTransitionReady_deferStartedTransition() {
1468         final Transition transition = createTestTransition(TRANSIT_OPEN);
1469         transition.setAllReady();
1470         transition.start();
1471 
1472         assertTrue(mSyncEngine.isReady(transition.getSyncId()));
1473 
1474         transition.deferTransitionReady();
1475 
1476         // Both transition ready tracker and sync engine should be deferred.
1477         assertFalse(transition.allReady());
1478         assertFalse(mSyncEngine.isReady(transition.getSyncId()));
1479 
1480         transition.continueTransitionReady();
1481 
1482         assertTrue(transition.allReady());
1483         assertTrue(mSyncEngine.isReady(transition.getSyncId()));
1484     }
1485 
1486     @Test
testVisibleChange_snapshot()1487     public void testVisibleChange_snapshot() {
1488         registerTestTransitionPlayer();
1489         final ActivityRecord app = createActivityRecord(mDisplayContent);
1490         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
1491                 app.mTransitionController, mWm.mSyncEngine);
1492         app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE);
1493         final SurfaceControl mockSnapshot = mock(SurfaceControl.class);
1494         transition.setContainerFreezer(new Transition.IContainerFreezer() {
1495             @Override
1496             public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
1497                 Objects.requireNonNull(transition.mChanges.get(wc)).mSnapshot = mockSnapshot;
1498                 return true;
1499             }
1500 
1501             @Override
1502             public void cleanUp(SurfaceControl.Transaction t) {
1503             }
1504         });
1505         final Task task = app.getTask();
1506         transition.collect(task);
1507         final Rect bounds = new Rect(task.getBounds());
1508         Configuration c = new Configuration(task.getRequestedOverrideConfiguration());
1509         bounds.inset(10, 10);
1510         c.windowConfiguration.setBounds(bounds);
1511         task.onRequestedOverrideConfigurationChanged(c);
1512 
1513         ArrayList<WindowContainer> targets = Transition.calculateTargets(
1514                 transition.mParticipants, transition.mChanges);
1515         TransitionInfo info = Transition.calculateTransitionInfo(
1516                 TRANSIT_CHANGE, 0, targets, transition.mChanges, mMockT);
1517         assertEquals(mockSnapshot,
1518                 info.getChange(task.mRemoteToken.toWindowContainerToken()).getSnapshot());
1519         transition.abort();
1520     }
1521 
1522     @Test
testCollectReparentChange()1523     public void testCollectReparentChange() {
1524         registerTestTransitionPlayer();
1525 
1526         // Reparent activity in transition.
1527         final Task lastParent = createTask(mDisplayContent);
1528         final Task newParent = createTask(mDisplayContent);
1529         final ActivityRecord activity = createActivityRecord(lastParent);
1530         doReturn(true).when(lastParent).shouldRemoveSelfOnLastChildRemoval();
1531         doNothing().when(activity).setDropInputMode(anyInt());
1532         activity.setVisibleRequested(true);
1533 
1534         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
1535                 activity.mTransitionController, mWm.mSyncEngine);
1536         activity.mTransitionController.moveToCollecting(transition);
1537         transition.collect(activity);
1538         activity.reparent(newParent, POSITION_TOP);
1539 
1540         // ChangeInfo#mCommonAncestor should be set after reparent.
1541         final Transition.ChangeInfo change = transition.mChanges.get(activity);
1542         assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
1543     }
1544 
makeTaskOrganized(Task... tasks)1545     private static void makeTaskOrganized(Task... tasks) {
1546         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
1547         for (Task t : tasks) {
1548             t.mTaskOrganizer = organizer;
1549         }
1550     }
1551 
makeDisplayAreaOrganized(WindowContainer<?> from, WindowContainer<?> end)1552     private static void makeDisplayAreaOrganized(WindowContainer<?> from,
1553             WindowContainer<?> end) {
1554         final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
1555         while (from != null && from != end) {
1556             if (from.asDisplayArea() != null) {
1557                 from.asDisplayArea().mOrganizer = organizer;
1558             }
1559             from = from.getParent();
1560         }
1561         if (end.asDisplayArea() != null) {
1562             end.asDisplayArea().mOrganizer = organizer;
1563         }
1564     }
1565 
1566     /** Fill the change map with all the parents of top. Change maps are usually fully populated */
fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, WindowContainer top)1567     private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
1568             WindowContainer top) {
1569         for (WindowContainer curr = top.getParent(); curr != null; curr = curr.getParent()) {
1570             changes.put(curr, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
1571         }
1572     }
1573 
onRotationTransactionReady( TestTransitionPlayer player, SurfaceControl.Transaction startTransaction)1574     private static SurfaceControl.TransactionCommittedListener onRotationTransactionReady(
1575             TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) {
1576         final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
1577                 ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
1578         player.onTransactionReady(startTransaction);
1579         verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
1580         return listenerCaptor.getValue();
1581     }
1582 }
1583