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.ActivityManager.START_CANCELED;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
24 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
25 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
26 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
27 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
29 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
30 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
31 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
32 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
33 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
34 
35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
38 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
43 import static com.android.server.wm.ActivityRecord.State.RESUMED;
44 import static com.android.server.wm.WindowContainer.POSITION_TOP;
45 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
46 import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
47 
48 import static com.google.common.truth.Truth.assertThat;
49 
50 import static org.junit.Assert.assertEquals;
51 import static org.junit.Assert.assertFalse;
52 import static org.junit.Assert.assertNotNull;
53 import static org.junit.Assert.assertTrue;
54 import static org.mockito.ArgumentMatchers.any;
55 import static org.mockito.ArgumentMatchers.anyBoolean;
56 import static org.mockito.ArgumentMatchers.anyInt;
57 import static org.mockito.ArgumentMatchers.eq;
58 import static org.mockito.Mockito.atLeastOnce;
59 import static org.mockito.Mockito.clearInvocations;
60 
61 import android.app.ActivityManager;
62 import android.app.ActivityManager.RunningTaskInfo;
63 import android.app.ActivityOptions;
64 import android.app.ActivityTaskManager.RootTaskInfo;
65 import android.app.IRequestFinishCallback;
66 import android.app.PictureInPictureParams;
67 import android.content.pm.ActivityInfo;
68 import android.content.pm.ParceledListSlice;
69 import android.content.res.Configuration;
70 import android.graphics.Rect;
71 import android.os.Binder;
72 import android.os.IBinder;
73 import android.os.RemoteException;
74 import android.platform.test.annotations.Presubmit;
75 import android.util.ArrayMap;
76 import android.util.Rational;
77 import android.view.Display;
78 import android.view.SurfaceControl;
79 import android.view.WindowInsets;
80 import android.window.ITaskOrganizer;
81 import android.window.IWindowContainerTransactionCallback;
82 import android.window.StartingWindowInfo;
83 import android.window.StartingWindowRemovalInfo;
84 import android.window.TaskAppearedInfo;
85 import android.window.WindowContainerToken;
86 import android.window.WindowContainerTransaction;
87 
88 import androidx.test.filters.SmallTest;
89 
90 import com.android.server.wm.TaskOrganizerController.PendingTaskEvent;
91 
92 import org.junit.Before;
93 import org.junit.Test;
94 import org.junit.runner.RunWith;
95 import org.mockito.ArgumentCaptor;
96 
97 import java.util.ArrayList;
98 import java.util.HashSet;
99 import java.util.List;
100 
101 /**
102  * Test class for {@link ITaskOrganizer} and {@link android.window.ITaskOrganizerController}.
103  *
104  * Build/Install/Run:
105  *  atest WmTests:WindowOrganizerTests
106  */
107 @SmallTest
108 @Presubmit
109 @RunWith(WindowTestRunner.class)
110 public class WindowOrganizerTests extends WindowTestsBase {
111 
createMockOrganizer()112     private ITaskOrganizer createMockOrganizer() {
113         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
114         when(organizer.asBinder()).thenReturn(new Binder());
115         return organizer;
116     }
117 
registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks)118     private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) {
119         final ITaskOrganizer organizer = createMockOrganizer();
120         ParceledListSlice<TaskAppearedInfo> tasks =
121                 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
122         if (existingTasks != null) {
123             existingTasks.addAll(tasks.getList());
124         }
125         return organizer;
126     }
127 
registerMockOrganizer()128     private ITaskOrganizer registerMockOrganizer() {
129         return registerMockOrganizer(null);
130     }
131 
createTask(Task rootTask, boolean fakeDraw)132     Task createTask(Task rootTask, boolean fakeDraw) {
133         final Task task = createTaskInRootTask(rootTask, 0);
134 
135         if (fakeDraw) {
136             task.setHasBeenVisible(true);
137         }
138         return task;
139     }
140 
createTask(Task rootTask)141     Task createTask(Task rootTask) {
142         // Fake draw notifications for most of our tests.
143         return createTask(rootTask, true);
144     }
145 
createRootTask()146     Task createRootTask() {
147         return createTask(mDisplayContent);
148     }
149 
150     @Before
setUp()151     public void setUp() {
152         // We defer callbacks since we need to adjust task surface visibility, but for these tests,
153         // just run the callbacks synchronously
154         mWm.mAtmService.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer((r) -> r.run());
155     }
156 
157     @Test
testAppearVanish()158     public void testAppearVanish() throws RemoteException {
159         final ITaskOrganizer organizer = registerMockOrganizer();
160         final Task rootTask = createRootTask();
161         final Task task = createTask(rootTask);
162         // Ensure events dispatch to organizer.
163         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
164 
165         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
166 
167         rootTask.removeImmediately();
168         // Ensure events dispatch to organizer.
169         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
170         verify(organizer).onTaskVanished(any());
171     }
172 
173     @Test
testAppearWaitsForVisibility()174     public void testAppearWaitsForVisibility() throws RemoteException {
175         final ITaskOrganizer organizer = registerMockOrganizer();
176         final Task rootTask = createRootTask();
177         final Task task = createTask(rootTask, false);
178         // Ensure events dispatch to organizer.
179         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
180 
181         verify(organizer, never())
182                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
183         rootTask.setHasBeenVisible(true);
184         // Ensure events dispatch to organizer.
185         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
186         assertTrue(rootTask.getHasBeenVisible());
187 
188         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
189 
190         rootTask.removeImmediately();
191         // Ensure events dispatch to organizer.
192         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
193         verify(organizer).onTaskVanished(any());
194     }
195 
196     @Test
testNoVanishedIfNoAppear()197     public void testNoVanishedIfNoAppear() throws RemoteException {
198         final ITaskOrganizer organizer = registerMockOrganizer();
199         final Task rootTask = createRootTask();
200         final Task task = createTask(rootTask, false /* hasBeenVisible */);
201 
202         // In this test we skip making the Task visible, and verify
203         // that even though a TaskOrganizer is set remove doesn't emit
204         // a vanish callback, because we never emitted appear.
205         rootTask.setTaskOrganizer(organizer);
206         verify(organizer, never())
207                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
208         rootTask.removeImmediately();
209         verify(organizer, never()).onTaskVanished(any());
210     }
211 
212     @Test
testTaskNoDraw()213     public void testTaskNoDraw() throws RemoteException {
214         final ITaskOrganizer organizer = registerMockOrganizer();
215         final Task rootTask = createRootTask();
216         final Task task = createTask(rootTask, false /* fakeDraw */);
217         // Ensure events dispatch to organizer.
218         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
219 
220         verify(organizer, never())
221                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
222         assertTrue(rootTask.isOrganized());
223 
224         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
225         // Ensure events dispatch to organizer.
226         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
227         verify(organizer, times(0)).onTaskVanished(any());
228         assertFalse(rootTask.isOrganized());
229     }
230 
231     @Test
testClearOrganizer()232     public void testClearOrganizer() throws RemoteException {
233         final ITaskOrganizer organizer = registerMockOrganizer();
234         final Task rootTask = createRootTask();
235         final Task task = createTask(rootTask);
236         // Ensure events dispatch to organizer.
237         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
238 
239         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
240         assertTrue(rootTask.isOrganized());
241 
242         rootTask.setTaskOrganizer(null);
243         // Ensure events dispatch to organizer.
244         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
245 
246         verify(organizer).onTaskVanished(any());
247         assertFalse(rootTask.isOrganized());
248     }
249 
250     @Test
testRemoveWithOrganizerRemovesTask()251     public void testRemoveWithOrganizerRemovesTask() throws RemoteException {
252         final ITaskOrganizer organizer = registerMockOrganizer();
253         final Task rootTask = createRootTask();
254         final Task task = createTask(rootTask);
255         rootTask.mRemoveWithTaskOrganizer = true;
256 
257         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
258         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
259         assertTrue(rootTask.isOrganized());
260 
261         spyOn(mWm.mAtmService);
262         rootTask.setTaskOrganizer(null);
263         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
264 
265         verify(mWm.mAtmService).removeTask(eq(rootTask.mTaskId));
266     }
267 
268     @Test
testNoRemoveWithOrganizerNoRemoveTask()269     public void testNoRemoveWithOrganizerNoRemoveTask() throws RemoteException {
270         final ITaskOrganizer organizer = registerMockOrganizer();
271         final Task rootTask = createRootTask();
272         final Task task = createTask(rootTask);
273         rootTask.mRemoveWithTaskOrganizer = false;
274 
275         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
276         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
277         assertTrue(rootTask.isOrganized());
278 
279         spyOn(mWm.mAtmService);
280         rootTask.setTaskOrganizer(null);
281         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
282 
283         verify(mWm.mAtmService, never()).removeTask(eq(rootTask.mTaskId));
284     }
285 
286     @Test
testUnregisterOrganizer()287     public void testUnregisterOrganizer() throws RemoteException {
288         final ITaskOrganizer organizer = registerMockOrganizer();
289         final Task rootTask = createRootTask();
290         final Task task = createTask(rootTask);
291         // Ensure events dispatch to organizer.
292         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
293 
294         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
295         assertTrue(rootTask.isOrganized());
296 
297         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
298         // Ensure events dispatch to organizer.
299         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
300 
301         verify(organizer, times(0)).onTaskVanished(any());
302         assertFalse(rootTask.isOrganized());
303     }
304 
305     @Test
testUnregisterOrganizerReturnsRegistrationToPrevious()306     public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException {
307         final Task rootTask = createRootTask();
308         final Task task = createTask(rootTask);
309         final Task rootTask2 = createRootTask();
310         final Task task2 = createTask(rootTask2);
311         final Task rootTask3 = createRootTask();
312         final Task task3 = createTask(rootTask3);
313         final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
314         final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
315         // Ensure events dispatch to organizer.
316         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
317 
318         // verify that tasks are returned and taskAppeared is not called
319         assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3);
320         verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
321                 any(SurfaceControl.class));
322         verify(organizer, times(0)).onTaskVanished(any());
323         assertTrue(rootTask.isOrganized());
324 
325         // Now we replace the registration and verify the new organizer receives existing tasks
326         final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
327         final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
328         // Ensure events dispatch to organizer.
329         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
330         assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3);
331         verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
332                 any(SurfaceControl.class));
333         verify(organizer2, times(0)).onTaskVanished(any());
334         // Removed tasks from the original organizer
335         assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3);
336         assertTrue(rootTask2.isOrganized());
337 
338         // Now we unregister the second one, the first one should automatically be reregistered
339         // so we verify that it's now seeing changes.
340         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
341         // Ensure events dispatch to organizer.
342         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
343         verify(organizer, times(3))
344                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
345         verify(organizer2, times(0)).onTaskVanished(any());
346     }
347 
348     @Test
testUnregisterOrganizer_removesTasksCreatedByIt()349     public void testUnregisterOrganizer_removesTasksCreatedByIt() throws RemoteException {
350         final Task rootTask = createRootTask();
351         final Task task = createTask(rootTask);
352         final Task rootTask2 = createRootTask();
353         rootTask2.mCreatedByOrganizer = true;
354         final Task task2 = createTask(rootTask2);
355         final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
356         final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
357         // Ensure events dispatch to organizer.
358         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
359 
360         // verify that tasks are returned and taskAppeared is called only for rootTask2 since it
361         // is the one created by this organizer.
362         assertContainsTasks(existingTasks, rootTask);
363         verify(organizer, times(1)).onTaskAppeared(any(RunningTaskInfo.class),
364                 any(SurfaceControl.class));
365         verify(organizer, times(0)).onTaskVanished(any());
366         assertTrue(rootTask.isOrganized());
367 
368         // Now we replace the registration and verify the new organizer receives existing tasks
369         final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
370         final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
371         // Ensure events dispatch to organizer.
372         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
373         assertContainsTasks(existingTasks2, rootTask);
374         verify(organizer2, never()).onTaskAppeared(any(RunningTaskInfo.class),
375                 any(SurfaceControl.class));
376         verify(organizer2, times(0)).onTaskVanished(any());
377         // The non-CreatedByOrganizer task is removed from the original organizer.
378         assertTaskVanished(organizer, true /* expectVanished */, rootTask);
379         assertEquals(organizer2, rootTask.mTaskOrganizer);
380         // The CreatedByOrganizer task should be still organized by the original organizer.
381         assertEquals(organizer, rootTask2.mTaskOrganizer);
382 
383         clearInvocations(organizer);
384         // Now we unregister the second one, the first one should automatically be reregistered
385         // so we verify that it's now seeing changes.
386         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
387         // Ensure events dispatch to organizer.
388         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
389 
390         verify(organizer, times(2))
391                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
392 
393         // Unregister the first one. The CreatedByOrganizer task created by it must be removed.
394         mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
395         assertFalse(rootTask2.isAttached());
396         assertFalse(task2.isAttached());
397         // Normal task should keep.
398         assertTrue(task.isAttached());
399         verify(organizer2, times(0)).onTaskVanished(any());
400     }
401 
402     @Test
testOrganizerDeathReturnsRegistrationToPrevious()403     public void testOrganizerDeathReturnsRegistrationToPrevious() throws RemoteException {
404         final Task rootTask = createRootTask();
405         final Task task = createTask(rootTask);
406         final Task rootTask2 = createRootTask();
407         final Task task2 = createTask(rootTask2);
408         final Task rootTask3 = createRootTask();
409         final Task task3 = createTask(rootTask3);
410         final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
411         final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
412         // Ensure events dispatch to organizer.
413         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
414 
415         // verify that tasks are returned and taskAppeared is not called
416         assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3);
417         verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
418                 any(SurfaceControl.class));
419         verify(organizer, times(0)).onTaskVanished(any());
420         assertTrue(rootTask.isOrganized());
421 
422         // Now we replace the registration and verify the new organizer receives existing tasks
423         final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
424         final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
425         // Ensure events dispatch to organizer.
426         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
427         assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3);
428         verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
429                 any(SurfaceControl.class));
430         verify(organizer2, times(0)).onTaskVanished(any());
431         // Removed tasks from the original organizer
432         assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3);
433         assertTrue(rootTask2.isOrganized());
434 
435         // Trigger binderDied for second one, the first one should automatically be reregistered
436         // so we verify that it's now seeing changes.
437         mWm.mAtmService.mTaskOrganizerController.getTaskOrganizerState(organizer2.asBinder())
438                 .getDeathRecipient().binderDied();
439 
440         // Ensure events dispatch to organizer.
441         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
442         verify(organizer, times(3))
443                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
444         verify(organizer2, times(0)).onTaskVanished(any());
445     }
446 
447     @Test
testRegisterTaskOrganizerWithExistingTasks()448     public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
449         final Task rootTask = createRootTask();
450         final Task task = createTask(rootTask);
451         final Task rootTask2 = createRootTask();
452         final Task task2 = createTask(rootTask2);
453         ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
454         final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
455         assertContainsTasks(existingTasks, rootTask, rootTask2);
456 
457         // Verify we don't get onTaskAppeared if we are returned the tasks
458         verify(organizer, never())
459                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
460     }
461 
462     @Test
testRegisterTaskOrganizerWithExistingTasks_noSurfaceControl()463     public void testRegisterTaskOrganizerWithExistingTasks_noSurfaceControl()
464             throws RemoteException {
465         final Task rootTask = createRootTask();
466         final Task task = createTask(rootTask);
467         final Task rootTask2 = createRootTask();
468         final Task task2 = createTask(rootTask2);
469         rootTask2.setSurfaceControl(null);
470         ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
471         final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
472         assertContainsTasks(existingTasks, rootTask);
473 
474         // Verify we don't get onTaskAppeared if we are returned the tasks
475         verify(organizer, never())
476                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
477     }
478 
479     @Test
testTaskTransaction()480     public void testTaskTransaction() {
481         removeGlobalMinSizeRestriction();
482         final Task rootTask = new TaskBuilder(mSupervisor)
483                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
484         final Task task = rootTask.getTopMostTask();
485         testTransaction(task);
486     }
487 
488     @Test
testRootTaskTransaction()489     public void testRootTaskTransaction() {
490         removeGlobalMinSizeRestriction();
491         final Task rootTask = new TaskBuilder(mSupervisor)
492                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
493         RootTaskInfo info =
494                 mWm.mAtmService.getRootTaskInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
495         assertEquals(rootTask.mRemoteToken.toWindowContainerToken(), info.token);
496         testTransaction(rootTask);
497     }
498 
499     @Test
testDisplayAreaTransaction()500     public void testDisplayAreaTransaction() {
501         removeGlobalMinSizeRestriction();
502         final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
503         testTransaction(displayArea);
504     }
505 
testTransaction(WindowContainer wc)506     private void testTransaction(WindowContainer wc) {
507         WindowContainerTransaction t = new WindowContainerTransaction();
508         Rect newBounds = new Rect(10, 10, 100, 100);
509         t.setBounds(wc.mRemoteToken.toWindowContainerToken(), new Rect(10, 10, 100, 100));
510         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
511         assertEquals(newBounds, wc.getBounds());
512     }
513 
514     @Test
testSetWindowingMode()515     public void testSetWindowingMode() {
516         final Task rootTask = new TaskBuilder(mSupervisor)
517                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
518         testSetWindowingMode(rootTask);
519 
520         final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
521         displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
522         testSetWindowingMode(displayArea);
523     }
524 
testSetWindowingMode(WindowContainer wc)525     private void testSetWindowingMode(WindowContainer wc) {
526         final WindowContainerTransaction t = new WindowContainerTransaction();
527         t.setWindowingMode(wc.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
528         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
529         assertEquals(WINDOWING_MODE_FULLSCREEN, wc.getWindowingMode());
530     }
531 
532     @Test
testSetActivityWindowingMode()533     public void testSetActivityWindowingMode() {
534         final ActivityRecord record = makePipableActivity();
535         final Task rootTask = record.getRootTask();
536         final WindowContainerTransaction t = new WindowContainerTransaction();
537 
538         t.setWindowingMode(rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED);
539         t.setActivityWindowingMode(
540                 rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN);
541         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
542 
543         assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
544         // Get the root task from the PIP activity record again, since the PIP root task may have
545         // changed when the activity entered PIP mode.
546         final Task pipRootTask = record.getRootTask();
547         assertEquals(WINDOWING_MODE_PINNED, pipRootTask.getWindowingMode());
548     }
549 
550     @Test
testContainerFocusableChanges()551     public void testContainerFocusableChanges() {
552         removeGlobalMinSizeRestriction();
553         final Task rootTask = new TaskBuilder(mSupervisor)
554                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
555         final Task task = rootTask.getTopMostTask();
556         WindowContainerTransaction t = new WindowContainerTransaction();
557         assertTrue(task.isFocusable());
558         t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), false);
559         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
560         assertFalse(task.isFocusable());
561         t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), true);
562         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
563         assertTrue(task.isFocusable());
564     }
565 
566     @Test
testContainerHiddenChanges()567     public void testContainerHiddenChanges() {
568         removeGlobalMinSizeRestriction();
569         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
570                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
571         WindowContainerTransaction t = new WindowContainerTransaction();
572         assertTrue(rootTask.shouldBeVisible(null));
573         t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), true);
574         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
575         assertFalse(rootTask.shouldBeVisible(null));
576         t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), false);
577         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
578         assertTrue(rootTask.shouldBeVisible(null));
579     }
580 
581     @Test
testContainerTranslucentChanges()582     public void testContainerTranslucentChanges() {
583         removeGlobalMinSizeRestriction();
584         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
585                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
586         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
587         WindowContainerTransaction t = new WindowContainerTransaction();
588         assertFalse(rootTask.isTranslucent(activity));
589         t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), true);
590         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
591         assertTrue(rootTask.isTranslucent(activity));
592         t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), false);
593         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
594         assertFalse(rootTask.isTranslucent(activity));
595     }
596 
597     @Test
testSetIgnoreOrientationRequest_taskDisplayArea()598     public void testSetIgnoreOrientationRequest_taskDisplayArea() {
599         removeGlobalMinSizeRestriction();
600         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
601         final Task rootTask = taskDisplayArea.createRootTask(
602                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
603         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
604         taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
605         mDisplayContent.setFocusedApp(activity);
606         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
607 
608         // TDA returns UNSET when ignoreOrientationRequest == true
609         // DC is UNSPECIFIED when child returns UNSET
610         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
611         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
612 
613         WindowContainerTransaction t = new WindowContainerTransaction();
614         t.setIgnoreOrientationRequest(
615                 taskDisplayArea.mRemoteToken.toWindowContainerToken(),
616                 false /* ignoreOrientationRequest */);
617         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
618 
619         // TDA returns app request orientation when ignoreOrientationRequest == false
620         // DC uses the same as TDA returns when it is not UNSET.
621         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
622         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
623 
624         t.setIgnoreOrientationRequest(
625                 taskDisplayArea.mRemoteToken.toWindowContainerToken(),
626                 true /* ignoreOrientationRequest */);
627         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
628 
629         // TDA returns UNSET when ignoreOrientationRequest == true
630         // DC is UNSPECIFIED when child returns UNSET
631         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
632         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
633     }
634 
635     @Test
testSetIgnoreOrientationRequest_displayContent()636     public void testSetIgnoreOrientationRequest_displayContent() {
637         removeGlobalMinSizeRestriction();
638         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
639         final Task rootTask = taskDisplayArea.createRootTask(
640                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
641         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
642         mDisplayContent.setFocusedApp(activity);
643         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
644 
645         // DC uses the orientation request from app
646         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
647 
648         WindowContainerTransaction t = new WindowContainerTransaction();
649         t.setIgnoreOrientationRequest(
650                 mDisplayContent.mRemoteToken.toWindowContainerToken(),
651                 true /* ignoreOrientationRequest */);
652         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
653 
654         // DC returns UNSPECIFIED when ignoreOrientationRequest == true
655         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
656 
657         t.setIgnoreOrientationRequest(
658                 mDisplayContent.mRemoteToken.toWindowContainerToken(),
659                 false /* ignoreOrientationRequest */);
660         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
661 
662         // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false
663         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
664     }
665 
666     @Test
testOverrideConfigSize()667     public void testOverrideConfigSize() {
668         removeGlobalMinSizeRestriction();
669         final Task rootTask = new TaskBuilder(mSupervisor)
670                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
671         final Task task = rootTask.getTopMostTask();
672         WindowContainerTransaction t = new WindowContainerTransaction();
673         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
674         final int origScreenWDp = task.getConfiguration().screenHeightDp;
675         final int origScreenHDp = task.getConfiguration().screenHeightDp;
676         t = new WindowContainerTransaction();
677         // verify that setting config overrides on parent restricts children.
678         t.setScreenSizeDp(rootTask.mRemoteToken
679                 .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2);
680         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
681         assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp);
682         t = new WindowContainerTransaction();
683         t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED,
684                 SCREEN_HEIGHT_DP_UNDEFINED);
685         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
686         assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
687     }
688 
689     @Test
testCreateDeleteRootTasks()690     public void testCreateDeleteRootTasks() {
691         DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
692 
693         Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
694                 dc, WINDOWING_MODE_FULLSCREEN, null);
695         RunningTaskInfo info1 = task1.getTaskInfo();
696         assertEquals(WINDOWING_MODE_FULLSCREEN,
697                 info1.configuration.windowConfiguration.getWindowingMode());
698         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
699 
700         Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
701                 dc, WINDOWING_MODE_MULTI_WINDOW, null);
702         RunningTaskInfo info2 = task2.getTaskInfo();
703         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
704                 info2.configuration.windowConfiguration.getWindowingMode());
705         assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
706 
707         List<Task> infos = getTasksCreatedByOrganizer(dc);
708         assertEquals(2, infos.size());
709 
710         assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
711         infos = getTasksCreatedByOrganizer(dc);
712         assertEquals(1, infos.size());
713         assertEquals(WINDOWING_MODE_MULTI_WINDOW, infos.get(0).getWindowingMode());
714     }
715 
716     @Test
testSetAdjacentLaunchRoot()717     public void testSetAdjacentLaunchRoot() {
718         DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
719 
720         final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
721                 dc, WINDOWING_MODE_MULTI_WINDOW, null);
722         final RunningTaskInfo info1 = task1.getTaskInfo();
723         final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
724                 dc, WINDOWING_MODE_MULTI_WINDOW, null);
725         final RunningTaskInfo info2 = task2.getTaskInfo();
726 
727         WindowContainerTransaction wct = new WindowContainerTransaction();
728         wct.setAdjacentRoots(info1.token, info2.token);
729         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
730         assertEquals(task1.getAdjacentTaskFragment(), task2);
731         assertEquals(task2.getAdjacentTaskFragment(), task1);
732 
733         wct = new WindowContainerTransaction();
734         wct.setLaunchAdjacentFlagRoot(info1.token);
735         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
736         assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
737 
738         wct = new WindowContainerTransaction();
739         wct.clearAdjacentRoots(info1.token);
740         wct.clearLaunchAdjacentFlagRoot(info1.token);
741         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
742         assertEquals(task1.getAdjacentTaskFragment(), null);
743         assertEquals(task2.getAdjacentTaskFragment(), null);
744         assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null);
745     }
746 
747     @Test
testTileAddRemoveChild()748     public void testTileAddRemoveChild() {
749         final StubOrganizer listener = new StubOrganizer();
750         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
751         Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
752                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
753         RunningTaskInfo info1 = task.getTaskInfo();
754 
755         final Task rootTask = createTask(
756                 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
757         assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
758         WindowContainerTransaction wct = new WindowContainerTransaction();
759         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
760         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
761         assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
762                 rootTask.getWindowingMode());
763 
764         // Info should reflect new membership
765         List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
766         info1 = infos.get(0).getTaskInfo();
767         assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
768 
769         // Children inherit configuration
770         Rect newSize = new Rect(10, 10, 300, 300);
771         Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
772         Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
773         c.windowConfiguration.setBounds(newSize);
774         doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any());
775         task1.onRequestedOverrideConfigurationChanged(c);
776         assertEquals(newSize, rootTask.getBounds());
777 
778         wct = new WindowContainerTransaction();
779         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
780         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
781         assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
782         infos = getTasksCreatedByOrganizer(mDisplayContent);
783         info1 = infos.get(0).getTaskInfo();
784         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
785     }
786 
787     @Test
testAddInsetsSource()788     public void testAddInsetsSource() {
789         final Task rootTask = createTask(mDisplayContent);
790 
791         final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0);
792         navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect(
793                 0, 200, 1080, 700));
794 
795         final WindowContainerTransaction wct = new WindowContainerTransaction();
796         wct.addInsetsSource(
797                 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(),
798                 new Binder(),
799                 0 /* index */,
800                 WindowInsets.Type.systemOverlays(),
801                 new Rect(0, 0, 1080, 200));
802         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
803 
804         assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources
805                 .valueAt(0).getType()).isEqualTo(
806                         WindowInsets.Type.systemOverlays());
807     }
808 
809     @Test
testRemoveInsetsSource()810     public void testRemoveInsetsSource() {
811         final Task rootTask = createTask(mDisplayContent);
812 
813         final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0);
814         navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect(
815                 0, 200, 1080, 700));
816         final Binder owner = new Binder();
817         final WindowContainerTransaction wct = new WindowContainerTransaction();
818         wct.addInsetsSource(
819                 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(),
820                 owner,
821                 0 /* index */,
822                 WindowInsets.Type.systemOverlays(),
823                 new Rect(0, 0, 1080, 200));
824         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
825 
826         final WindowContainerTransaction wct2 = new WindowContainerTransaction();
827         wct2.removeInsetsSource(
828                 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(),
829                 owner,
830                 0 /* index */,
831                 WindowInsets.Type.systemOverlays());
832         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct2);
833 
834         assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources.size()).isEqualTo(0);
835     }
836 
837     @Test
testTaskInfoCallback()838     public void testTaskInfoCallback() {
839         final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>();
840         final boolean[] called = {false};
841         final StubOrganizer listener = new StubOrganizer() {
842             @Override
843             public void onTaskInfoChanged(RunningTaskInfo info) {
844                 lastReportedTiles.add(info);
845                 called[0] = true;
846             }
847         };
848         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
849         Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
850                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
851         RunningTaskInfo info1 = task.getTaskInfo();
852         // Ensure events dispatch to organizer.
853         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
854         lastReportedTiles.clear();
855         called[0] = false;
856 
857         final Task rootTask = createTask(
858                 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
859         Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
860         WindowContainerTransaction wct = new WindowContainerTransaction();
861         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
862         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
863         assertTrue(called[0]);
864         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
865 
866         lastReportedTiles.clear();
867         called[0] = false;
868         final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
869         wct = new WindowContainerTransaction();
870         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
871                 info1.token, true /* onTop */);
872         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
873         assertTrue(called[0]);
874         assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
875 
876         lastReportedTiles.clear();
877         called[0] = false;
878         task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */);
879         mAtm.mTaskOrganizerController.dispatchPendingEvents();
880         assertTrue(called[0]);
881         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
882 
883         lastReportedTiles.clear();
884         called[0] = false;
885         wct = new WindowContainerTransaction();
886         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
887                 null, true /* onTop */);
888         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
889                 null, true /* onTop */);
890         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
891         assertTrue(called[0]);
892         assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
893     }
894 
895     @Test
testHierarchyTransaction()896     public void testHierarchyTransaction() {
897         final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
898         final StubOrganizer listener = new StubOrganizer() {
899             @Override
900             public void onTaskInfoChanged(RunningTaskInfo info) {
901                 lastReportedTiles.put(info.token.asBinder(), info);
902             }
903         };
904         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
905 
906         Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
907                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
908         RunningTaskInfo info1 = task1.getTaskInfo();
909         Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
910                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
911         RunningTaskInfo info2 = task2.getTaskInfo();
912         // Ensure events dispatch to organizer.
913         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
914 
915         // 2 + 1 (home) = 3
916         final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
917                 mDisplayContent.mDisplayId, null /* activityTypes */).size();
918         final Task rootTask = createTask(
919                 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
920 
921         // Check getRootTasks works
922         List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
923                 mDisplayContent.mDisplayId, null /* activityTypes */);
924         assertEquals(initialRootTaskCount + 1, roots.size());
925 
926         lastReportedTiles.clear();
927         WindowContainerTransaction wct = new WindowContainerTransaction();
928         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
929                 info1.token, true /* onTop */);
930         final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
931         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
932                 info2.token, true /* onTop */);
933         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
934         assertFalse(lastReportedTiles.isEmpty());
935         assertEquals(ACTIVITY_TYPE_STANDARD,
936                 lastReportedTiles.get(info1.token.asBinder()).topActivityType);
937         assertEquals(ACTIVITY_TYPE_HOME,
938                 lastReportedTiles.get(info2.token.asBinder()).topActivityType);
939 
940         lastReportedTiles.clear();
941         wct = new WindowContainerTransaction();
942         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
943                 info1.token, false /* onTop */);
944         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
945         assertFalse(lastReportedTiles.isEmpty());
946         // Standard should still be on top of tile 1, so no change there
947         assertFalse(lastReportedTiles.containsKey(info1.token.asBinder()));
948         // But tile 2 has no children, so should become undefined
949         assertEquals(ACTIVITY_TYPE_UNDEFINED,
950                 lastReportedTiles.get(info2.token.asBinder()).topActivityType);
951 
952         // Check the getChildren call
953         List<RunningTaskInfo> children =
954                 mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token,
955                         null /* activityTypes */);
956         assertEquals(2, children.size());
957         children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token,
958                 null /* activityTypes */);
959         assertEquals(0, children.size());
960 
961         // Check that getRootTasks doesn't include children of tiles
962         roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(mDisplayContent.mDisplayId,
963                 null /* activityTypes */);
964         // Home (rootTask2) was moved into task1, so only remain 2 roots: task1 and task2.
965         assertEquals(initialRootTaskCount - 1, roots.size());
966 
967         lastReportedTiles.clear();
968         wct = new WindowContainerTransaction();
969         wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */);
970         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
971         // Home should now be on top. No change occurs in second tile, so not reported
972         assertEquals(1, lastReportedTiles.size());
973         assertEquals(ACTIVITY_TYPE_HOME,
974                 lastReportedTiles.get(info1.token.asBinder()).topActivityType);
975 
976         // This just needs to not crash (ie. it should be possible to reparent to display twice)
977         wct = new WindowContainerTransaction();
978         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
979         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
980         wct = new WindowContainerTransaction();
981         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
982         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
983     }
984 
getTasksCreatedByOrganizer(DisplayContent dc)985     private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
986         final ArrayList<Task> out = new ArrayList<>();
987         dc.forAllRootTasks(task -> {
988             if (task.mCreatedByOrganizer) {
989                 out.add(task);
990             }
991         });
992         return out;
993     }
994 
995     @Test
testBLASTCallbackWithActivityChildren()996     public void testBLASTCallbackWithActivityChildren() {
997         final Task rootTaskController1 = createRootTask();
998         final Task task = createTask(rootTaskController1);
999         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
1000 
1001         w.mActivityRecord.setVisibleRequested(true);
1002         w.mActivityRecord.setVisible(true);
1003 
1004         BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
1005 
1006         BLASTSyncEngine.TransactionReadyListener transactionListener =
1007                 mock(BLASTSyncEngine.TransactionReadyListener.class);
1008 
1009         final int id = bse.startSyncSet(transactionListener, BLAST_TIMEOUT_DURATION, "Test",
1010                 false /* parallel */);
1011         bse.addToSyncSet(id, task);
1012         bse.setReady(id);
1013         bse.onSurfacePlacement();
1014 
1015         // Even though w is invisible (and thus activity isn't waiting on it), activity will
1016         // continue to wait until it has at-least 1 visible window.
1017         // Since we have a child window we still shouldn't be done.
1018         verify(transactionListener, never()).onTransactionReady(anyInt(), any());
1019 
1020         makeWindowVisible(w);
1021         bse.onSurfacePlacement();
1022         w.immediatelyNotifyBlastSync();
1023         bse.onSurfacePlacement();
1024 
1025         verify(transactionListener).onTransactionReady(anyInt(), any());
1026     }
1027 
1028     static class StubOrganizer extends ITaskOrganizer.Stub {
1029         RunningTaskInfo mInfo;
1030 
1031         @Override
addStartingWindow(StartingWindowInfo info)1032         public void addStartingWindow(StartingWindowInfo info) { }
1033         @Override
removeStartingWindow(StartingWindowRemovalInfo removalInfo)1034         public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { }
1035         @Override
copySplashScreenView(int taskId)1036         public void copySplashScreenView(int taskId) { }
1037         @Override
onTaskAppeared(RunningTaskInfo info, SurfaceControl leash)1038         public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) {
1039             mInfo = info;
1040         }
1041         @Override
onTaskVanished(RunningTaskInfo info)1042         public void onTaskVanished(RunningTaskInfo info) {
1043         }
1044         @Override
onTaskInfoChanged(RunningTaskInfo info)1045         public void onTaskInfoChanged(RunningTaskInfo info) {
1046         }
1047         @Override
onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)1048         public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
1049         }
1050         @Override
onImeDrawnOnTask(int taskId)1051         public void onImeDrawnOnTask(int taskId) throws RemoteException {
1052         }
1053         @Override
onAppSplashScreenViewRemoved(int taskId)1054         public void onAppSplashScreenViewRemoved(int taskId) {
1055         }
1056     };
1057 
makePipableActivity()1058     private ActivityRecord makePipableActivity() {
1059         final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent,
1060                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
1061         record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
1062         record.setPictureInPictureParams(new PictureInPictureParams.Builder()
1063                 .setAutoEnterEnabled(true).build());
1064         spyOn(record);
1065         doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
1066 
1067         record.getTask().setHasBeenVisible(true);
1068         return record;
1069     }
1070 
1071     @Test
testEnterPipParams()1072     public void testEnterPipParams() {
1073         final StubOrganizer o = new StubOrganizer();
1074         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
1075         final ActivityRecord record = makePipableActivity();
1076 
1077         final PictureInPictureParams p = new PictureInPictureParams.Builder()
1078                 .setAspectRatio(new Rational(1, 2)).build();
1079         assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode(
1080                 record.token, p));
1081         waitUntilHandlersIdle();
1082         assertNotNull(o.mInfo);
1083         assertNotNull(o.mInfo.pictureInPictureParams);
1084     }
1085 
1086     @Test
testChangePipParams()1087     public void testChangePipParams() {
1088         class ChangeSavingOrganizer extends StubOrganizer {
1089             RunningTaskInfo mChangedInfo;
1090             @Override
1091             public void onTaskInfoChanged(RunningTaskInfo info) {
1092                 mChangedInfo = info;
1093             }
1094         }
1095         ChangeSavingOrganizer o = new ChangeSavingOrganizer();
1096         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
1097 
1098         final ActivityRecord record = makePipableActivity();
1099         final PictureInPictureParams p = new PictureInPictureParams.Builder()
1100                 .setAspectRatio(new Rational(1, 2)).build();
1101         assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode(
1102                 record.token, p));
1103         waitUntilHandlersIdle();
1104         assertNotNull(o.mInfo);
1105         assertNotNull(o.mInfo.pictureInPictureParams);
1106 
1107         final PictureInPictureParams p2 = new PictureInPictureParams.Builder()
1108                 .setAspectRatio(new Rational(3, 4)).build();
1109         mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2);
1110         waitUntilHandlersIdle();
1111         // Ensure events dispatch to organizer.
1112         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1113         assertNotNull(o.mChangedInfo);
1114         assertNotNull(o.mChangedInfo.pictureInPictureParams);
1115         final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatio();
1116         assertEquals(3, ratio.getNumerator());
1117         assertEquals(4, ratio.getDenominator());
1118     }
1119 
1120     @Test
testChangeTaskDescription()1121     public void testChangeTaskDescription() {
1122         class ChangeSavingOrganizer extends StubOrganizer {
1123             RunningTaskInfo mChangedInfo;
1124             @Override
1125             public void onTaskInfoChanged(RunningTaskInfo info) {
1126                 mChangedInfo = info;
1127             }
1128         }
1129         ChangeSavingOrganizer o = new ChangeSavingOrganizer();
1130         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
1131 
1132         final Task rootTask = createRootTask();
1133         final Task task = createTask(rootTask);
1134         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1135 
1136         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1137         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1138         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1139         assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel());
1140     }
1141 
1142     @Test
testPreventDuplicateAppear()1143     public void testPreventDuplicateAppear() throws RemoteException {
1144         final ITaskOrganizer organizer = registerMockOrganizer();
1145         final Task rootTask = createRootTask();
1146         final Task task = createTask(rootTask, false /* fakeDraw */);
1147 
1148         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1149         rootTask.setTaskOrganizer(organizer);
1150         // setHasBeenVisible was already called once by the set-up code.
1151         rootTask.setHasBeenVisible(true);
1152         // Ensure events dispatch to organizer.
1153         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1154         verify(organizer, times(1))
1155                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
1156 
1157         rootTask.setTaskOrganizer(null);
1158         // Ensure events dispatch to organizer.
1159         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1160         verify(organizer, times(1)).onTaskVanished(any());
1161         rootTask.setTaskOrganizer(organizer);
1162         // Ensure events dispatch to organizer.
1163         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1164         verify(organizer, times(2))
1165                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
1166 
1167         rootTask.removeImmediately();
1168         // Ensure events dispatch to organizer.
1169         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1170         verify(organizer, times(2)).onTaskVanished(any());
1171     }
1172 
1173     @Test
testInterceptBackPressedOnTaskRoot()1174     public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
1175         final ITaskOrganizer organizer = registerMockOrganizer();
1176         final Task rootTask = createRootTask();
1177         final Task task = createTask(rootTask);
1178         final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
1179         final Task rootTask2 = createRootTask();
1180         final Task task2 = createTask(rootTask2);
1181         final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2);
1182 
1183         assertTrue(rootTask.isOrganized());
1184         assertTrue(rootTask2.isOrganized());
1185 
1186         // Verify a back pressed does not call the organizer
1187         mWm.mAtmService.mActivityClientController.onBackPressed(activity.token,
1188                 new IRequestFinishCallback.Default());
1189         // Ensure events dispatch to organizer.
1190         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1191         verify(organizer, never()).onBackPressedOnTaskRoot(any());
1192 
1193         // Enable intercepting back
1194         mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
1195                 rootTask.mRemoteToken.toWindowContainerToken(), true);
1196 
1197         // Verify now that the back press does call the organizer
1198         mWm.mAtmService.mActivityClientController.onBackPressed(activity.token,
1199                 new IRequestFinishCallback.Default());
1200         // Ensure events dispatch to organizer.
1201         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1202         verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
1203 
1204         // Disable intercepting back
1205         mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
1206                 rootTask.mRemoteToken.toWindowContainerToken(), false);
1207 
1208         // Verify now that the back press no longer calls the organizer
1209         mWm.mAtmService.mActivityClientController.onBackPressed(activity.token,
1210                 new IRequestFinishCallback.Default());
1211         // Ensure events dispatch to organizer.
1212         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1213         verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
1214     }
1215 
1216     @Test
testBLASTCallbackWithWindows()1217     public void testBLASTCallbackWithWindows() throws Exception {
1218         final Task rootTaskController = createRootTask();
1219         final Task task = createTask(rootTaskController);
1220         final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
1221         final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2");
1222         makeWindowVisible(w1);
1223         makeWindowVisible(w2);
1224 
1225         IWindowContainerTransactionCallback mockCallback =
1226                 mock(IWindowContainerTransactionCallback.class);
1227         int id = mWm.mAtmService.mWindowOrganizerController.startSyncWithOrganizer(mockCallback);
1228 
1229         mWm.mAtmService.mWindowOrganizerController.addToSyncSet(id, task);
1230         mWm.mAtmService.mWindowOrganizerController.setSyncReady(id);
1231 
1232         // Since we have a window we have to wait for it to draw to finish sync.
1233         verify(mockCallback, never()).onTransactionReady(anyInt(), any());
1234         assertTrue(w1.useBLASTSync());
1235         assertTrue(w2.useBLASTSync());
1236 
1237         // Make second (bottom) ready. If we started with the top, since activities fillsParent
1238         // by default, the sync would be considered finished.
1239         w2.immediatelyNotifyBlastSync();
1240         mWm.mSyncEngine.onSurfacePlacement();
1241         verify(mockCallback, never()).onTransactionReady(anyInt(), any());
1242 
1243         assertEquals(SYNC_STATE_READY, w2.mSyncState);
1244         // Even though one Window finished drawing, both windows should still be using blast sync
1245         assertTrue(w1.useBLASTSync());
1246         assertTrue(w2.useBLASTSync());
1247 
1248         // A drawn window can complete the sync state automatically.
1249         w1.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
1250         makeLastConfigReportedToClient(w1, true /* visible */);
1251         mWm.mSyncEngine.onSurfacePlacement();
1252         verify(mockCallback).onTransactionReady(anyInt(), any());
1253         assertFalse(w1.useBLASTSync());
1254         assertFalse(w2.useBLASTSync());
1255     }
1256 
1257     @Test
testDisplayAreaHiddenTransaction()1258     public void testDisplayAreaHiddenTransaction() {
1259         removeGlobalMinSizeRestriction();
1260 
1261         WindowContainerTransaction trx = new WindowContainerTransaction();
1262 
1263         TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
1264 
1265         trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), true);
1266         mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx);
1267 
1268         taskDisplayArea.forAllTasks(daTask -> {
1269             assertTrue(daTask.isForceHidden());
1270         });
1271 
1272         trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), false);
1273         mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx);
1274 
1275         taskDisplayArea.forAllTasks(daTask -> {
1276             assertFalse(daTask.isForceHidden());
1277         });
1278     }
1279 
1280     @Test
testReparentToOrganizedTask()1281     public void testReparentToOrganizedTask() {
1282         final ITaskOrganizer organizer = registerMockOrganizer();
1283         Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
1284                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
1285         final Task task1 = createRootTask();
1286         final Task task2 = createTask(rootTask, false /* fakeDraw */);
1287         WindowContainerTransaction wct = new WindowContainerTransaction();
1288         wct.reparent(task1.mRemoteToken.toWindowContainerToken(),
1289                 rootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
1290         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1291         assertTrue(task1.isOrganized());
1292         assertTrue(task2.isOrganized());
1293     }
1294 
1295     @Test
testAppearDeferThenInfoChange()1296     public void testAppearDeferThenInfoChange() {
1297         final ITaskOrganizer organizer = registerMockOrganizer();
1298         final Task rootTask = createRootTask();
1299         // Flush EVENT_APPEARED.
1300         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1301 
1302         // Assume layout defer
1303         mWm.mWindowPlacerLocked.deferLayout();
1304 
1305         final Task task = createTask(rootTask);
1306         final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
1307 
1308         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1309         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1310         waitUntilHandlersIdle();
1311 
1312         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1313         assertEquals(1, pendingEvents.size());
1314         assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType);
1315         assertEquals("TestDescription",
1316                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1317     }
1318 
1319     @Test
testAppearDeferThenVanish()1320     public void testAppearDeferThenVanish() {
1321         final ITaskOrganizer organizer = registerMockOrganizer();
1322         final Task rootTask = createRootTask();
1323         // Flush EVENT_APPEARED.
1324         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1325 
1326         // Assume layout defer
1327         mWm.mWindowPlacerLocked.deferLayout();
1328 
1329         final Task task = createTask(rootTask);
1330 
1331         rootTask.removeImmediately();
1332         waitUntilHandlersIdle();
1333 
1334         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1335         assertEquals(0, pendingEvents.size());
1336     }
1337 
1338     @Test
testInfoChangeDeferMultiple()1339     public void testInfoChangeDeferMultiple() {
1340         final ITaskOrganizer organizer = registerMockOrganizer();
1341         final Task rootTask = createRootTask();
1342         final Task task = createTask(rootTask);
1343         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1344 
1345         // Assume layout defer
1346         mWm.mWindowPlacerLocked.deferLayout();
1347 
1348         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1349         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1350         waitUntilHandlersIdle();
1351 
1352         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1353         assertEquals(1, pendingEvents.size());
1354         assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
1355         assertEquals("TestDescription",
1356                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1357 
1358         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2"));
1359         waitUntilHandlersIdle();
1360 
1361         pendingEvents = getTaskPendingEvent(organizer, rootTask);
1362         assertEquals(1, pendingEvents.size());
1363         assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
1364         assertEquals("TestDescription2",
1365                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1366     }
1367 
1368     @Test
testInfoChangDeferThenVanish()1369     public void testInfoChangDeferThenVanish() {
1370         final ITaskOrganizer organizer = registerMockOrganizer();
1371         final Task rootTask = createRootTask();
1372         final Task task = createTask(rootTask);
1373         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1374 
1375         // Assume layout defer
1376         mWm.mWindowPlacerLocked.deferLayout();
1377 
1378         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1379         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1380 
1381         rootTask.removeImmediately();
1382         waitUntilHandlersIdle();
1383 
1384         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1385         assertEquals(1, pendingEvents.size());
1386         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
1387         assertEquals("TestDescription",
1388                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1389     }
1390 
1391     @Test
testVanishDeferThenInfoChange()1392     public void testVanishDeferThenInfoChange() {
1393         final ITaskOrganizer organizer = registerMockOrganizer();
1394         final Task rootTask = createRootTask();
1395         final Task task = createTask(rootTask);
1396         createActivityRecordAndDispatchPendingEvents(task);
1397 
1398         // Assume layout defer
1399         mWm.mWindowPlacerLocked.deferLayout();
1400 
1401         rootTask.removeImmediately();
1402         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1403         waitUntilHandlersIdle();
1404 
1405         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1406         assertEquals(1, pendingEvents.size());
1407         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
1408     }
1409 
1410     @Test
testVanishDeferThenBackOnRoot()1411     public void testVanishDeferThenBackOnRoot() {
1412         final ITaskOrganizer organizer = registerMockOrganizer();
1413         final Task rootTask = createRootTask();
1414         final Task task = createTask(rootTask);
1415         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1416 
1417         // Assume layout defer
1418         mWm.mWindowPlacerLocked.deferLayout();
1419 
1420         rootTask.removeImmediately();
1421         mWm.mAtmService.mActivityClientController.onBackPressed(record.token,
1422                 new IRequestFinishCallback.Default());
1423         waitUntilHandlersIdle();
1424 
1425         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1426         assertEquals(1, pendingEvents.size());
1427         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
1428     }
1429 
getTaskPendingEvent(ITaskOrganizer organizer, Task task)1430     private ArrayList<PendingTaskEvent> getTaskPendingEvent(ITaskOrganizer organizer, Task task) {
1431         ArrayList<PendingTaskEvent> total =
1432                 mWm.mAtmService.mTaskOrganizerController
1433                         .getTaskOrganizerPendingEvents(organizer.asBinder())
1434                         .getPendingEventList();
1435         ArrayList<PendingTaskEvent> result = new ArrayList();
1436 
1437         for (int i = 0; i < total.size(); i++) {
1438             PendingTaskEvent entry = total.get(i);
1439             if (entry.mTask.mTaskId == task.mTaskId) {
1440                 result.add(entry);
1441             }
1442         }
1443 
1444         return result;
1445     }
1446 
1447     @Test
testReparentNonResizableTaskToSplitScreen()1448     public void testReparentNonResizableTaskToSplitScreen() {
1449         final ActivityRecord activity = new ActivityBuilder(mAtm)
1450                 .setCreateTask(true)
1451                 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
1452                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
1453                 .build();
1454         final Task rootTask = activity.getRootTask();
1455         rootTask.setResizeMode(activity.info.resizeMode);
1456         final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
1457                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
1458         final WindowContainerTransaction wct = new WindowContainerTransaction();
1459         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
1460                 splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
1461 
1462         // Can't reparent non-resizable to split screen
1463         mAtm.mSupportsNonResizableMultiWindow = -1;
1464         mAtm.mWindowOrganizerController.applyTransaction(wct);
1465 
1466         assertEquals(rootTask, activity.getRootTask());
1467 
1468         // Allow reparent non-resizable to split screen
1469         mAtm.mSupportsNonResizableMultiWindow = 1;
1470         mAtm.mWindowOrganizerController.applyTransaction(wct);
1471 
1472         assertEquals(splitPrimaryRootTask, activity.getRootTask());
1473     }
1474 
1475     @Test
testSizeCompatModeChangedOnFirstOrganizedTask()1476     public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
1477         final ITaskOrganizer organizer = registerMockOrganizer();
1478         final Task rootTask = createRootTask();
1479         final Task task = createTask(rootTask);
1480         final ActivityRecord activity = createActivityRecordAndDispatchPendingEvents(task);
1481         final ArgumentCaptor<RunningTaskInfo> infoCaptor =
1482                 ArgumentCaptor.forClass(RunningTaskInfo.class);
1483 
1484         assertTrue(rootTask.isOrganized());
1485 
1486         doReturn(true).when(activity).inSizeCompatMode();
1487         doReturn(true).when(activity).isState(RESUMED);
1488 
1489         // Ensure task info show top activity in size compat.
1490         rootTask.onSizeCompatActivityChanged();
1491         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1492         verify(organizer).onTaskInfoChanged(infoCaptor.capture());
1493         RunningTaskInfo info = infoCaptor.getValue();
1494         assertEquals(rootTask.mTaskId, info.taskId);
1495         assertTrue(info.topActivityInSizeCompat);
1496 
1497         // Ensure task info show top activity that is not visible as not in size compat.
1498         clearInvocations(organizer);
1499         doReturn(false).when(activity).isVisible();
1500         rootTask.onSizeCompatActivityChanged();
1501         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1502         verify(organizer).onTaskInfoChanged(infoCaptor.capture());
1503         info = infoCaptor.getValue();
1504         assertEquals(rootTask.mTaskId, info.taskId);
1505         assertFalse(info.topActivityInSizeCompat);
1506 
1507         // Ensure task info show non size compat top activity as not in size compat.
1508         clearInvocations(organizer);
1509         doReturn(true).when(activity).isVisible();
1510         doReturn(false).when(activity).inSizeCompatMode();
1511         rootTask.onSizeCompatActivityChanged();
1512         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1513         verify(organizer).onTaskInfoChanged(infoCaptor.capture());
1514         info = infoCaptor.getValue();
1515         assertEquals(rootTask.mTaskId, info.taskId);
1516         assertFalse(info.topActivityInSizeCompat);
1517     }
1518 
1519     @Test
testStartTasksInTransaction()1520     public void testStartTasksInTransaction() {
1521         WindowContainerTransaction wct = new WindowContainerTransaction();
1522         ActivityOptions testOptions = ActivityOptions.makeBasic();
1523         testOptions.setTransientLaunch();
1524         wct.startTask(1, null /* options */);
1525         wct.startTask(2, testOptions.toBundle());
1526         spyOn(mWm.mAtmService.mTaskSupervisor);
1527         doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents(
1528                 anyInt(), anyInt(), anyInt(), any());
1529         clearInvocations(mWm.mAtmService);
1530         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1531 
1532         verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
1533                 anyInt(), anyInt(), eq(1), any());
1534 
1535         final ArgumentCaptor<SafeActivityOptions> optionsCaptor =
1536                 ArgumentCaptor.forClass(SafeActivityOptions.class);
1537         verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
1538                 anyInt(), anyInt(), eq(2), optionsCaptor.capture());
1539         assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
1540     }
1541 
1542     @Test
testResumeTopsWhenLeavingPinned()1543     public void testResumeTopsWhenLeavingPinned() {
1544         final ActivityRecord record = makePipableActivity();
1545         final Task rootTask = record.getRootTask();
1546 
1547         clearInvocations(mWm.mAtmService.mRootWindowContainer);
1548         final WindowContainerTransaction t = new WindowContainerTransaction();
1549         WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken();
1550         t.setWindowingMode(wct, WINDOWING_MODE_PINNED);
1551         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
1552         verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
1553 
1554         clearInvocations(mWm.mAtmService.mRootWindowContainer);
1555         // The token for the PIP root task may have changed when the task entered PIP mode, so do
1556         // not reuse the one from above.
1557         final WindowContainerToken newToken =
1558                 record.getRootTask().mRemoteToken.toWindowContainerToken();
1559         t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN);
1560         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
1561         verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
1562     }
1563 
createActivityRecordAndDispatchPendingEvents(Task task)1564     private ActivityRecord createActivityRecordAndDispatchPendingEvents(Task task) {
1565         final ActivityRecord record = createActivityRecord(task);
1566         // Flush EVENT_APPEARED.
1567         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1568         return record;
1569     }
1570 
1571     /**
1572      * Verifies that task vanished is called for a specific task.
1573      */
assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)1574     private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)
1575             throws RemoteException {
1576         ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class);
1577         verify(organizer, atLeastOnce()).onTaskVanished(arg.capture());
1578         List<RunningTaskInfo> taskInfos = arg.getAllValues();
1579 
1580         HashSet<Integer> vanishedTaskIds = new HashSet<>();
1581         for (int i = 0; i < taskInfos.size(); i++) {
1582             vanishedTaskIds.add(taskInfos.get(i).taskId);
1583         }
1584         HashSet<Integer> taskIds = new HashSet<>();
1585         for (int i = 0; i < tasks.length; i++) {
1586             taskIds.add(tasks[i].mTaskId);
1587         }
1588 
1589         assertTrue(expectVanished
1590                 ? vanishedTaskIds.containsAll(taskIds)
1591                 : !vanishedTaskIds.removeAll(taskIds));
1592     }
1593 
assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks)1594     private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) {
1595         HashSet<Integer> taskIds = new HashSet<>();
1596         for (int i = 0; i < taskInfos.size(); i++) {
1597             taskIds.add(taskInfos.get(i).getTaskInfo().taskId);
1598         }
1599         for (int i = 0; i < expectedTasks.length; i++) {
1600             assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
1601         }
1602     }
1603 }
1604