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 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
35 
36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
38 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
44 import static com.android.server.wm.ActivityRecord.State.RESUMED;
45 import static com.android.server.wm.WindowContainer.POSITION_TOP;
46 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
47 import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
48 import static com.android.server.wm.testing.Assert.assertThrows;
49 
50 import static com.google.common.truth.Truth.assertThat;
51 
52 import static org.junit.Assert.assertArrayEquals;
53 import static org.junit.Assert.assertEquals;
54 import static org.junit.Assert.assertFalse;
55 import static org.junit.Assert.assertNotNull;
56 import static org.junit.Assert.assertTrue;
57 import static org.mockito.ArgumentMatchers.any;
58 import static org.mockito.ArgumentMatchers.anyBoolean;
59 import static org.mockito.ArgumentMatchers.anyInt;
60 import static org.mockito.ArgumentMatchers.eq;
61 import static org.mockito.Mockito.atLeastOnce;
62 import static org.mockito.Mockito.clearInvocations;
63 
64 import android.annotation.NonNull;
65 import android.app.ActivityManager;
66 import android.app.ActivityManager.RunningTaskInfo;
67 import android.app.ActivityOptions;
68 import android.app.ActivityTaskManager.RootTaskInfo;
69 import android.app.IRequestFinishCallback;
70 import android.app.PictureInPictureParams;
71 import android.content.pm.ActivityInfo;
72 import android.content.pm.ParceledListSlice;
73 import android.content.res.Configuration;
74 import android.graphics.Rect;
75 import android.os.Binder;
76 import android.os.IBinder;
77 import android.os.RemoteException;
78 import android.platform.test.annotations.Presubmit;
79 import android.util.ArrayMap;
80 import android.util.Rational;
81 import android.view.Display;
82 import android.view.SurfaceControl;
83 import android.view.WindowInsets;
84 import android.window.ITaskFragmentOrganizer;
85 import android.window.ITaskOrganizer;
86 import android.window.IWindowContainerTransactionCallback;
87 import android.window.StartingWindowInfo;
88 import android.window.StartingWindowRemovalInfo;
89 import android.window.TaskAppearedInfo;
90 import android.window.TaskFragmentOrganizer;
91 import android.window.WindowContainerToken;
92 import android.window.WindowContainerTransaction;
93 
94 import androidx.test.filters.SmallTest;
95 
96 import com.android.server.wm.TaskOrganizerController.PendingTaskEvent;
97 import com.android.window.flags.Flags;
98 
99 import org.junit.Test;
100 import org.junit.runner.RunWith;
101 import org.mockito.ArgumentCaptor;
102 
103 import java.util.ArrayList;
104 import java.util.HashSet;
105 import java.util.List;
106 import java.util.function.BiConsumer;
107 
108 /**
109  * Test class for {@link ITaskOrganizer} and {@link android.window.ITaskOrganizerController}.
110  *
111  * Build/Install/Run:
112  *  atest WmTests:WindowOrganizerTests
113  */
114 @SmallTest
115 @Presubmit
116 @RunWith(WindowTestRunner.class)
117 public class WindowOrganizerTests extends WindowTestsBase {
118 
createMockOrganizer()119     private ITaskOrganizer createMockOrganizer() {
120         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
121         when(organizer.asBinder()).thenReturn(new Binder());
122         return organizer;
123     }
124 
registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks)125     private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) {
126         final ITaskOrganizer organizer = createMockOrganizer();
127         ParceledListSlice<TaskAppearedInfo> tasks =
128                 mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
129         if (existingTasks != null) {
130             existingTasks.addAll(tasks.getList());
131         }
132         return organizer;
133     }
134 
registerMockOrganizer()135     private ITaskOrganizer registerMockOrganizer() {
136         return registerMockOrganizer(null);
137     }
138 
createTask(Task rootTask, boolean fakeDraw)139     Task createTask(Task rootTask, boolean fakeDraw) {
140         final Task task = createTaskInRootTask(rootTask, 0);
141 
142         if (fakeDraw) {
143             task.setHasBeenVisible(true);
144         }
145         return task;
146     }
147 
createTask(Task rootTask)148     Task createTask(Task rootTask) {
149         // Fake draw notifications for most of our tests.
150         return createTask(rootTask, true);
151     }
152 
createRootTask()153     Task createRootTask() {
154         return createTask(mDisplayContent);
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
testTaskFragmentHiddenFocusableTranslucentChanges()582     public void testTaskFragmentHiddenFocusableTranslucentChanges() {
583         mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
584 
585         removeGlobalMinSizeRestriction();
586         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
587                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
588 
589         final WindowContainerTransaction t = new WindowContainerTransaction();
590         final TaskFragmentOrganizer organizer =
591                 createTaskFragmentOrganizer(t, true /* isSystemOrganizer */);
592 
593         final IBinder token = new Binder();
594         final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
595                 .setParentTask(rootTask)
596                 .setFragmentToken(token)
597                 .setOrganizer(organizer)
598                 .createActivityCount(1)
599                 .build();
600 
601         // Should be visible and focusable initially.
602         assertTrue(rootTask.shouldBeVisible(null));
603         assertTrue(taskFragment.shouldBeVisible(null));
604         assertTrue(taskFragment.isFocusable());
605         assertTrue(taskFragment.isTopActivityFocusable());
606         assertFalse(taskFragment.isForceTranslucent());
607 
608         // Apply transaction to the TaskFragment hidden and not focusable.
609         t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
610         t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
611         t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), true);
612         mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
613                 t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
614                 false /* shouldApplyIndependently */, null /* remoteTransition */);
615 
616         // Should be not visible and not focusable after the transaction.
617         assertFalse(taskFragment.shouldBeVisible(null));
618         assertFalse(taskFragment.isFocusable());
619         assertFalse(taskFragment.isTopActivityFocusable());
620         assertTrue(taskFragment.isForceTranslucent());
621 
622         // Apply transaction to the TaskFragment not hidden and focusable.
623         t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), false);
624         t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), true);
625         t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), false);
626         mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
627                 t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
628                 false /* shouldApplyIndependently */, null /* remoteTransition */);
629 
630         // Should be visible and focusable after the transaction.
631         assertTrue(taskFragment.shouldBeVisible(null));
632         assertTrue(taskFragment.isFocusable());
633         assertTrue(taskFragment.isTopActivityFocusable());
634         assertFalse(taskFragment.isForceTranslucent());
635     }
636 
637     @Test
testTaskFragmentChangeHidden_throwsWhenNotSystemOrganizer()638     public void testTaskFragmentChangeHidden_throwsWhenNotSystemOrganizer() {
639         // Non-system organizers are not allow to update the hidden state.
640         testTaskFragmentChangesWithoutSystemOrganizerThrowException(
641                 (t, windowContainerToken) -> t.setHidden(windowContainerToken, true));
642     }
643 
644     @Test
testTaskFragmentChangeFocusable_throwsWhenNotSystemOrganizer()645     public void testTaskFragmentChangeFocusable_throwsWhenNotSystemOrganizer() {
646         // Non-system organizers are not allow to update the focusable state.
647         testTaskFragmentChangesWithoutSystemOrganizerThrowException(
648                 (t, windowContainerToken) -> t.setFocusable(windowContainerToken, false));
649     }
650 
651     @Test
testTaskFragmentChangeTranslucent_throwsWhenNotSystemOrganizer()652     public void testTaskFragmentChangeTranslucent_throwsWhenNotSystemOrganizer() {
653         // Non-system organizers are not allow to update the translucent state.
654         testTaskFragmentChangesWithoutSystemOrganizerThrowException(
655                 (t, windowContainerToken) -> t.setForceTranslucent(windowContainerToken, true));
656     }
657 
testTaskFragmentChangesWithoutSystemOrganizerThrowException( BiConsumer<WindowContainerTransaction, WindowContainerToken> addOp)658     private void testTaskFragmentChangesWithoutSystemOrganizerThrowException(
659             BiConsumer<WindowContainerTransaction, WindowContainerToken> addOp) {
660         mSetFlagsRule.enableFlags(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG);
661 
662         removeGlobalMinSizeRestriction();
663         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
664                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
665 
666         final WindowContainerTransaction t = new WindowContainerTransaction();
667         final TaskFragmentOrganizer organizer =
668                 createTaskFragmentOrganizer(t, false /* isSystemOrganizer */);
669 
670         final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
671                 .setParentTask(rootTask)
672                 .setFragmentToken(new Binder())
673                 .setOrganizer(organizer)
674                 .createActivityCount(1)
675                 .build();
676 
677         addOp.accept(t, taskFragment.mRemoteToken.toWindowContainerToken());
678 
679         assertThrows(SecurityException.class, () ->
680                 mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
681                         t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
682                         false /* shouldApplyIndependently */, null /* remoteTransition */)
683         );
684     }
685 
686     @Test
testContainerTranslucentChanges()687     public void testContainerTranslucentChanges() {
688         removeGlobalMinSizeRestriction();
689         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
690                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
691         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
692         WindowContainerTransaction t = new WindowContainerTransaction();
693         assertFalse(rootTask.isTranslucent(activity));
694         t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), true);
695         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
696         assertTrue(rootTask.isTranslucent(activity));
697         t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), false);
698         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
699         assertFalse(rootTask.isTranslucent(activity));
700     }
701 
702     @Test
testSetIgnoreOrientationRequest_taskDisplayArea()703     public void testSetIgnoreOrientationRequest_taskDisplayArea() {
704         removeGlobalMinSizeRestriction();
705         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
706         final Task rootTask = taskDisplayArea.createRootTask(
707                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
708         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
709         taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
710         mDisplayContent.setFocusedApp(activity);
711         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
712 
713         // TDA returns UNSET when ignoreOrientationRequest == true
714         // DC is UNSPECIFIED when child returns UNSET
715         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
716         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
717 
718         WindowContainerTransaction t = new WindowContainerTransaction();
719         t.setIgnoreOrientationRequest(
720                 taskDisplayArea.mRemoteToken.toWindowContainerToken(),
721                 false /* ignoreOrientationRequest */);
722         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
723 
724         // TDA returns app request orientation when ignoreOrientationRequest == false
725         // DC uses the same as TDA returns when it is not UNSET.
726         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
727         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
728 
729         t.setIgnoreOrientationRequest(
730                 taskDisplayArea.mRemoteToken.toWindowContainerToken(),
731                 true /* ignoreOrientationRequest */);
732         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
733 
734         // TDA returns UNSET when ignoreOrientationRequest == true
735         // DC is UNSPECIFIED when child returns UNSET
736         assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
737         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
738     }
739 
740     @Test
testSetIgnoreOrientationRequest_displayContent()741     public void testSetIgnoreOrientationRequest_displayContent() {
742         removeGlobalMinSizeRestriction();
743         final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
744         final Task rootTask = taskDisplayArea.createRootTask(
745                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
746         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
747         mDisplayContent.setFocusedApp(activity);
748         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
749 
750         // DC uses the orientation request from app
751         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
752 
753         WindowContainerTransaction t = new WindowContainerTransaction();
754         t.setIgnoreOrientationRequest(
755                 mDisplayContent.mRemoteToken.toWindowContainerToken(),
756                 true /* ignoreOrientationRequest */);
757         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
758 
759         // DC returns UNSPECIFIED when ignoreOrientationRequest == true
760         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
761 
762         t.setIgnoreOrientationRequest(
763                 mDisplayContent.mRemoteToken.toWindowContainerToken(),
764                 false /* ignoreOrientationRequest */);
765         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
766 
767         // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false
768         assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
769     }
770 
771     @Test
testOverrideConfigSize()772     public void testOverrideConfigSize() {
773         removeGlobalMinSizeRestriction();
774         final Task rootTask = new TaskBuilder(mSupervisor)
775                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
776         final Task task = rootTask.getTopMostTask();
777         WindowContainerTransaction t = new WindowContainerTransaction();
778         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
779         final int origScreenWDp = task.getConfiguration().screenHeightDp;
780         final int origScreenHDp = task.getConfiguration().screenHeightDp;
781         t = new WindowContainerTransaction();
782         // verify that setting config overrides on parent restricts children.
783         t.setScreenSizeDp(rootTask.mRemoteToken
784                 .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2);
785         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
786         assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp);
787         t = new WindowContainerTransaction();
788         t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED,
789                 SCREEN_HEIGHT_DP_UNDEFINED);
790         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
791         assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp);
792     }
793 
794     @Test
testCreateDeleteRootTasks()795     public void testCreateDeleteRootTasks() {
796         DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
797 
798         Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
799                 dc, WINDOWING_MODE_FULLSCREEN, null);
800         RunningTaskInfo info1 = task1.getTaskInfo();
801         assertEquals(WINDOWING_MODE_FULLSCREEN,
802                 info1.configuration.windowConfiguration.getWindowingMode());
803         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
804 
805         Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
806                 dc, WINDOWING_MODE_MULTI_WINDOW, null);
807         RunningTaskInfo info2 = task2.getTaskInfo();
808         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
809                 info2.configuration.windowConfiguration.getWindowingMode());
810         assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
811 
812         List<Task> infos = getTasksCreatedByOrganizer(dc);
813         assertEquals(2, infos.size());
814 
815         assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
816         infos = getTasksCreatedByOrganizer(dc);
817         assertEquals(1, infos.size());
818         assertEquals(WINDOWING_MODE_MULTI_WINDOW, infos.get(0).getWindowingMode());
819     }
820 
821     @Test
testSetAdjacentLaunchRoot()822     public void testSetAdjacentLaunchRoot() {
823         DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
824 
825         final Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
826                 dc, WINDOWING_MODE_MULTI_WINDOW, null);
827         final RunningTaskInfo info1 = task1.getTaskInfo();
828         final Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
829                 dc, WINDOWING_MODE_MULTI_WINDOW, null);
830         final RunningTaskInfo info2 = task2.getTaskInfo();
831 
832         WindowContainerTransaction wct = new WindowContainerTransaction();
833         wct.setAdjacentRoots(info1.token, info2.token);
834         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
835         assertEquals(task1.getAdjacentTaskFragment(), task2);
836         assertEquals(task2.getAdjacentTaskFragment(), task1);
837 
838         wct = new WindowContainerTransaction();
839         wct.setLaunchAdjacentFlagRoot(info1.token);
840         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
841         assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
842 
843         wct = new WindowContainerTransaction();
844         wct.clearAdjacentRoots(info1.token);
845         wct.clearLaunchAdjacentFlagRoot(info1.token);
846         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
847         assertEquals(task1.getAdjacentTaskFragment(), null);
848         assertEquals(task2.getAdjacentTaskFragment(), null);
849         assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null);
850     }
851 
852     @Test
testTileAddRemoveChild()853     public void testTileAddRemoveChild() {
854         final StubOrganizer listener = new StubOrganizer();
855         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
856         Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
857                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
858         RunningTaskInfo info1 = task.getTaskInfo();
859 
860         final Task rootTask = createTask(
861                 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
862         assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
863         WindowContainerTransaction wct = new WindowContainerTransaction();
864         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
865         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
866         assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
867                 rootTask.getWindowingMode());
868 
869         // Info should reflect new membership
870         List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
871         info1 = infos.get(0).getTaskInfo();
872         assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
873 
874         // Children inherit configuration
875         Rect newSize = new Rect(10, 10, 300, 300);
876         Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
877         Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
878         c.windowConfiguration.setBounds(newSize);
879         doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any());
880         task1.onRequestedOverrideConfigurationChanged(c);
881         assertEquals(newSize, rootTask.getBounds());
882 
883         wct = new WindowContainerTransaction();
884         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
885         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
886         assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode());
887         infos = getTasksCreatedByOrganizer(mDisplayContent);
888         info1 = infos.get(0).getTaskInfo();
889         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
890     }
891 
892     @Test
testAddInsetsSource()893     public void testAddInsetsSource() {
894         final Task rootTask = createTask(mDisplayContent);
895 
896         final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0);
897         navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect(
898                 0, 200, 1080, 700));
899 
900         final WindowContainerTransaction wct = new WindowContainerTransaction();
901         wct.addInsetsSource(
902                 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(),
903                 new Binder(),
904                 0 /* index */,
905                 WindowInsets.Type.systemOverlays(),
906                 new Rect(0, 0, 1080, 200),
907                 null /* boundingRects */);
908         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
909 
910         assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources
911                 .valueAt(0).getType()).isEqualTo(
912                         WindowInsets.Type.systemOverlays());
913     }
914 
915     @Test
testAddInsetsSource_withBoundingRects()916     public void testAddInsetsSource_withBoundingRects() {
917         final Task rootTask = createTask(mDisplayContent);
918 
919         final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0);
920         navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect(
921                 0, 200, 1080, 700));
922 
923         final Rect[] boundingRects = new Rect[]{
924                 new Rect(0, 0, 10, 10), new Rect(100, 100, 200, 100)
925         };
926         final WindowContainerTransaction wct = new WindowContainerTransaction();
927         wct.addInsetsSource(
928                 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(),
929                 new Binder(),
930                 0 /* index */,
931                 WindowInsets.Type.systemOverlays(),
932                 new Rect(0, 0, 1080, 200),
933                 boundingRects);
934         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
935 
936         assertArrayEquals(boundingRects, navigationBarInsetsReceiverTask.mLocalInsetsSources
937                 .valueAt(0).getBoundingRects());
938     }
939 
940     @Test
testRemoveInsetsSource()941     public void testRemoveInsetsSource() {
942         final Task rootTask = createTask(mDisplayContent);
943 
944         final Task navigationBarInsetsReceiverTask = createTaskInRootTask(rootTask, 0);
945         navigationBarInsetsReceiverTask.getConfiguration().windowConfiguration.setBounds(new Rect(
946                 0, 200, 1080, 700));
947         final Binder owner = new Binder();
948         final WindowContainerTransaction wct = new WindowContainerTransaction();
949         wct.addInsetsSource(
950                 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(),
951                 owner,
952                 0 /* index */,
953                 WindowInsets.Type.systemOverlays(),
954                 new Rect(0, 0, 1080, 200),
955                 null /* boundingRects */);
956         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
957 
958         final WindowContainerTransaction wct2 = new WindowContainerTransaction();
959         wct2.removeInsetsSource(
960                 navigationBarInsetsReceiverTask.mRemoteToken.toWindowContainerToken(),
961                 owner,
962                 0 /* index */,
963                 WindowInsets.Type.systemOverlays());
964         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct2);
965 
966         assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSources.size()).isEqualTo(0);
967     }
968 
969     @Test
testTaskInfoCallback()970     public void testTaskInfoCallback() {
971         final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>();
972         final boolean[] called = {false};
973         final StubOrganizer listener = new StubOrganizer() {
974             @Override
975             public void onTaskInfoChanged(RunningTaskInfo info) {
976                 lastReportedTiles.add(info);
977                 called[0] = true;
978             }
979         };
980         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
981         Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
982                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
983         RunningTaskInfo info1 = task.getTaskInfo();
984         // Ensure events dispatch to organizer.
985         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
986         lastReportedTiles.clear();
987         called[0] = false;
988 
989         final Task rootTask = createTask(
990                 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
991         Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
992         WindowContainerTransaction wct = new WindowContainerTransaction();
993         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */);
994         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
995         assertTrue(called[0]);
996         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
997 
998         lastReportedTiles.clear();
999         called[0] = false;
1000         final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
1001         wct = new WindowContainerTransaction();
1002         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
1003                 info1.token, true /* onTop */);
1004         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1005         assertTrue(called[0]);
1006         assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
1007 
1008         lastReportedTiles.clear();
1009         called[0] = false;
1010         task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */);
1011         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1012         assertTrue(called[0]);
1013         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
1014 
1015         lastReportedTiles.clear();
1016         called[0] = false;
1017         wct = new WindowContainerTransaction();
1018         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
1019                 null, true /* onTop */);
1020         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
1021                 null, true /* onTop */);
1022         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1023         assertTrue(called[0]);
1024         assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
1025     }
1026 
1027     @Test
testHierarchyTransaction()1028     public void testHierarchyTransaction() {
1029         final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
1030         final StubOrganizer listener = new StubOrganizer() {
1031             @Override
1032             public void onTaskInfoChanged(RunningTaskInfo info) {
1033                 lastReportedTiles.put(info.token.asBinder(), info);
1034             }
1035         };
1036         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
1037 
1038         Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
1039                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
1040         RunningTaskInfo info1 = task1.getTaskInfo();
1041         Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
1042                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
1043         RunningTaskInfo info2 = task2.getTaskInfo();
1044         // Ensure events dispatch to organizer.
1045         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1046 
1047         // 2 + 1 (home) = 3
1048         final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
1049                 mDisplayContent.mDisplayId, null /* activityTypes */).size();
1050         final Task rootTask = createTask(
1051                 mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
1052 
1053         // Check getRootTasks works
1054         List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
1055                 mDisplayContent.mDisplayId, null /* activityTypes */);
1056         assertEquals(initialRootTaskCount + 1, roots.size());
1057 
1058         lastReportedTiles.clear();
1059         WindowContainerTransaction wct = new WindowContainerTransaction();
1060         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
1061                 info1.token, true /* onTop */);
1062         final Task rootTask2 = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
1063         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
1064                 info2.token, true /* onTop */);
1065         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1066         assertFalse(lastReportedTiles.isEmpty());
1067         assertEquals(ACTIVITY_TYPE_STANDARD,
1068                 lastReportedTiles.get(info1.token.asBinder()).topActivityType);
1069         assertEquals(ACTIVITY_TYPE_HOME,
1070                 lastReportedTiles.get(info2.token.asBinder()).topActivityType);
1071 
1072         lastReportedTiles.clear();
1073         wct = new WindowContainerTransaction();
1074         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(),
1075                 info1.token, false /* onTop */);
1076         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1077         assertFalse(lastReportedTiles.isEmpty());
1078         // Standard should still be on top of tile 1, so no change there
1079         assertFalse(lastReportedTiles.containsKey(info1.token.asBinder()));
1080         // But tile 2 has no children, so should become undefined
1081         assertEquals(ACTIVITY_TYPE_UNDEFINED,
1082                 lastReportedTiles.get(info2.token.asBinder()).topActivityType);
1083 
1084         // Check the getChildren call
1085         List<RunningTaskInfo> children =
1086                 mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token,
1087                         null /* activityTypes */);
1088         assertEquals(2, children.size());
1089         children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token,
1090                 null /* activityTypes */);
1091         assertEquals(0, children.size());
1092 
1093         // Check that getRootTasks doesn't include children of tiles
1094         roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(mDisplayContent.mDisplayId,
1095                 null /* activityTypes */);
1096         // Home (rootTask2) was moved into task1, so only remain 2 roots: task1 and task2.
1097         assertEquals(initialRootTaskCount - 1, roots.size());
1098 
1099         lastReportedTiles.clear();
1100         wct = new WindowContainerTransaction();
1101         wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */);
1102         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1103         // Home should now be on top. No change occurs in second tile, so not reported
1104         assertEquals(1, lastReportedTiles.size());
1105         assertEquals(ACTIVITY_TYPE_HOME,
1106                 lastReportedTiles.get(info1.token.asBinder()).topActivityType);
1107 
1108         // This just needs to not crash (ie. it should be possible to reparent to display twice)
1109         wct = new WindowContainerTransaction();
1110         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
1111         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1112         wct = new WindowContainerTransaction();
1113         wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */);
1114         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1115     }
1116 
getTasksCreatedByOrganizer(DisplayContent dc)1117     private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
1118         final ArrayList<Task> out = new ArrayList<>();
1119         dc.forAllRootTasks(task -> {
1120             if (task.mCreatedByOrganizer) {
1121                 out.add(task);
1122             }
1123         });
1124         return out;
1125     }
1126 
1127     @Test
testBLASTCallbackWithActivityChildren()1128     public void testBLASTCallbackWithActivityChildren() {
1129         final Task rootTaskController1 = createRootTask();
1130         final Task task = createTask(rootTaskController1);
1131         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
1132 
1133         w.mActivityRecord.setVisibleRequested(true);
1134         w.mActivityRecord.setVisible(true);
1135 
1136         BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
1137 
1138         BLASTSyncEngine.TransactionReadyListener transactionListener =
1139                 mock(BLASTSyncEngine.TransactionReadyListener.class);
1140 
1141         final int id = bse.startSyncSet(transactionListener, BLAST_TIMEOUT_DURATION, "Test",
1142                 false /* parallel */);
1143         bse.addToSyncSet(id, task);
1144         bse.setReady(id);
1145         bse.onSurfacePlacement();
1146 
1147         // Even though w is invisible (and thus activity isn't waiting on it), activity will
1148         // continue to wait until it has at-least 1 visible window.
1149         // Since we have a child window we still shouldn't be done.
1150         verify(transactionListener, never()).onTransactionReady(anyInt(), any());
1151 
1152         makeWindowVisible(w);
1153         bse.onSurfacePlacement();
1154         w.immediatelyNotifyBlastSync();
1155         bse.onSurfacePlacement();
1156 
1157         verify(transactionListener).onTransactionReady(anyInt(), any());
1158     }
1159 
1160     static class StubOrganizer extends ITaskOrganizer.Stub {
1161         RunningTaskInfo mInfo;
1162 
1163         @Override
addStartingWindow(StartingWindowInfo info)1164         public void addStartingWindow(StartingWindowInfo info) { }
1165         @Override
removeStartingWindow(StartingWindowRemovalInfo removalInfo)1166         public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { }
1167         @Override
copySplashScreenView(int taskId)1168         public void copySplashScreenView(int taskId) { }
1169         @Override
onTaskAppeared(RunningTaskInfo info, SurfaceControl leash)1170         public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) {
1171             mInfo = info;
1172         }
1173         @Override
onTaskVanished(RunningTaskInfo info)1174         public void onTaskVanished(RunningTaskInfo info) {
1175         }
1176         @Override
onTaskInfoChanged(RunningTaskInfo info)1177         public void onTaskInfoChanged(RunningTaskInfo info) {
1178         }
1179         @Override
onBackPressedOnTaskRoot(RunningTaskInfo taskInfo)1180         public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
1181         }
1182         @Override
onImeDrawnOnTask(int taskId)1183         public void onImeDrawnOnTask(int taskId) throws RemoteException {
1184         }
1185         @Override
onAppSplashScreenViewRemoved(int taskId)1186         public void onAppSplashScreenViewRemoved(int taskId) {
1187         }
1188     };
1189 
makePipableActivity()1190     private ActivityRecord makePipableActivity() {
1191         final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent,
1192                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
1193         record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
1194         record.setPictureInPictureParams(new PictureInPictureParams.Builder()
1195                 .setAutoEnterEnabled(true).build());
1196         spyOn(record);
1197         doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
1198 
1199         record.getTask().setHasBeenVisible(true);
1200         return record;
1201     }
1202 
1203     @Test
testEnterPipParams()1204     public void testEnterPipParams() {
1205         final StubOrganizer o = new StubOrganizer();
1206         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
1207         final ActivityRecord record = makePipableActivity();
1208 
1209         final PictureInPictureParams p = new PictureInPictureParams.Builder()
1210                 .setAspectRatio(new Rational(1, 2)).build();
1211         assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode(
1212                 record.token, p));
1213         waitUntilHandlersIdle();
1214         assertNotNull(o.mInfo);
1215         assertNotNull(o.mInfo.pictureInPictureParams);
1216     }
1217 
1218     @Test
testChangePipParams()1219     public void testChangePipParams() {
1220         class ChangeSavingOrganizer extends StubOrganizer {
1221             RunningTaskInfo mChangedInfo;
1222             @Override
1223             public void onTaskInfoChanged(RunningTaskInfo info) {
1224                 mChangedInfo = info;
1225             }
1226         }
1227         ChangeSavingOrganizer o = new ChangeSavingOrganizer();
1228         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
1229 
1230         final ActivityRecord record = makePipableActivity();
1231         final PictureInPictureParams p = new PictureInPictureParams.Builder()
1232                 .setAspectRatio(new Rational(1, 2)).build();
1233         assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode(
1234                 record.token, p));
1235         waitUntilHandlersIdle();
1236         assertNotNull(o.mInfo);
1237         assertNotNull(o.mInfo.pictureInPictureParams);
1238 
1239         // Bypass the quota check, which causes NPE in current test setup.
1240         if (mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker != null) {
1241             mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker
1242                     .setEnabled(false);
1243         }
1244 
1245         final PictureInPictureParams p2 = new PictureInPictureParams.Builder()
1246                 .setAspectRatio(new Rational(3, 4)).build();
1247         mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2);
1248         waitUntilHandlersIdle();
1249         // Ensure events dispatch to organizer.
1250         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1251         assertNotNull(o.mChangedInfo);
1252         assertNotNull(o.mChangedInfo.pictureInPictureParams);
1253         final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatio();
1254         assertEquals(3, ratio.getNumerator());
1255         assertEquals(4, ratio.getDenominator());
1256     }
1257 
1258     @Test
testChangeTaskDescription()1259     public void testChangeTaskDescription() {
1260         class ChangeSavingOrganizer extends StubOrganizer {
1261             RunningTaskInfo mChangedInfo;
1262             @Override
1263             public void onTaskInfoChanged(RunningTaskInfo info) {
1264                 mChangedInfo = info;
1265             }
1266         }
1267         ChangeSavingOrganizer o = new ChangeSavingOrganizer();
1268         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
1269 
1270         final Task rootTask = createRootTask();
1271         final Task task = createTask(rootTask);
1272         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1273 
1274         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1275         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1276         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1277         assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel());
1278     }
1279 
1280     @Test
testPreventDuplicateAppear()1281     public void testPreventDuplicateAppear() throws RemoteException {
1282         final ITaskOrganizer organizer = registerMockOrganizer();
1283         final Task rootTask = createRootTask();
1284         final Task task = createTask(rootTask, false /* fakeDraw */);
1285 
1286         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1287         rootTask.setTaskOrganizer(organizer);
1288         // setHasBeenVisible was already called once by the set-up code.
1289         rootTask.setHasBeenVisible(true);
1290         // Ensure events dispatch to organizer.
1291         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1292         verify(organizer, times(1))
1293                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
1294 
1295         rootTask.setTaskOrganizer(null);
1296         // Ensure events dispatch to organizer.
1297         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1298         verify(organizer, times(1)).onTaskVanished(any());
1299         rootTask.setTaskOrganizer(organizer);
1300         // Ensure events dispatch to organizer.
1301         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1302         verify(organizer, times(2))
1303                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
1304 
1305         rootTask.removeImmediately();
1306         // Ensure events dispatch to organizer.
1307         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1308         verify(organizer, times(2)).onTaskVanished(any());
1309     }
1310 
1311     @Test
testInterceptBackPressedOnTaskRoot()1312     public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
1313         final ITaskOrganizer organizer = registerMockOrganizer();
1314         final Task rootTask = createRootTask();
1315         final Task task = createTask(rootTask);
1316         final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
1317         final Task rootTask2 = createRootTask();
1318         final Task task2 = createTask(rootTask2);
1319         final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2);
1320 
1321         assertTrue(rootTask.isOrganized());
1322         assertTrue(rootTask2.isOrganized());
1323 
1324         // Verify a back pressed does not call the organizer
1325         mWm.mAtmService.mActivityClientController.onBackPressed(activity.token,
1326                 new IRequestFinishCallback.Default());
1327         // Ensure events dispatch to organizer.
1328         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1329         verify(organizer, never()).onBackPressedOnTaskRoot(any());
1330 
1331         // Enable intercepting back
1332         mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
1333                 rootTask.mRemoteToken.toWindowContainerToken(), true);
1334 
1335         // Verify now that the back press does call the organizer
1336         mWm.mAtmService.mActivityClientController.onBackPressed(activity.token,
1337                 new IRequestFinishCallback.Default());
1338         // Ensure events dispatch to organizer.
1339         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1340         verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
1341 
1342         // Disable intercepting back
1343         mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
1344                 rootTask.mRemoteToken.toWindowContainerToken(), false);
1345 
1346         // Verify now that the back press no longer calls the organizer
1347         mWm.mAtmService.mActivityClientController.onBackPressed(activity.token,
1348                 new IRequestFinishCallback.Default());
1349         // Ensure events dispatch to organizer.
1350         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1351         verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
1352     }
1353 
1354     @Test
testBLASTCallbackWithWindows()1355     public void testBLASTCallbackWithWindows() throws Exception {
1356         final Task rootTaskController = createRootTask();
1357         final Task task = createTask(rootTaskController);
1358         final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
1359         final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2");
1360         makeWindowVisible(w1);
1361         makeWindowVisible(w2);
1362 
1363         IWindowContainerTransactionCallback mockCallback =
1364                 mock(IWindowContainerTransactionCallback.class);
1365         int id = mWm.mAtmService.mWindowOrganizerController.startSyncWithOrganizer(mockCallback);
1366 
1367         mWm.mAtmService.mWindowOrganizerController.addToSyncSet(id, task);
1368         mWm.mAtmService.mWindowOrganizerController.setSyncReady(id);
1369 
1370         // Since we have a window we have to wait for it to draw to finish sync.
1371         verify(mockCallback, never()).onTransactionReady(anyInt(), any());
1372         assertTrue(w1.syncNextBuffer());
1373         assertTrue(w2.syncNextBuffer());
1374 
1375         // Make second (bottom) ready. If we started with the top, since activities fillsParent
1376         // by default, the sync would be considered finished.
1377         w2.immediatelyNotifyBlastSync();
1378         mWm.mSyncEngine.onSurfacePlacement();
1379         verify(mockCallback, never()).onTransactionReady(anyInt(), any());
1380 
1381         assertEquals(SYNC_STATE_READY, w2.mSyncState);
1382         // Even though one Window finished drawing, both windows should still be using blast sync
1383         assertTrue(w1.syncNextBuffer());
1384         assertTrue(w2.syncNextBuffer());
1385 
1386         // A drawn window in non-explicit sync can complete the sync state automatically.
1387         w1.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
1388         w1.mPrepareSyncSeqId = 0;
1389         makeLastConfigReportedToClient(w1, true /* visible */);
1390         mWm.mSyncEngine.onSurfacePlacement();
1391         verify(mockCallback).onTransactionReady(anyInt(), any());
1392         assertFalse(w1.syncNextBuffer());
1393         assertFalse(w2.syncNextBuffer());
1394     }
1395 
1396     @Test
testDisplayAreaHiddenTransaction()1397     public void testDisplayAreaHiddenTransaction() {
1398         removeGlobalMinSizeRestriction();
1399 
1400         WindowContainerTransaction trx = new WindowContainerTransaction();
1401 
1402         TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
1403 
1404         trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), true);
1405         mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx);
1406 
1407         taskDisplayArea.forAllTasks(daTask -> {
1408             assertTrue(daTask.isForceHidden());
1409         });
1410 
1411         trx.setHidden(taskDisplayArea.mRemoteToken.toWindowContainerToken(), false);
1412         mWm.mAtmService.mWindowOrganizerController.applyTransaction(trx);
1413 
1414         taskDisplayArea.forAllTasks(daTask -> {
1415             assertFalse(daTask.isForceHidden());
1416         });
1417     }
1418 
1419     @Test
testReparentToOrganizedTask()1420     public void testReparentToOrganizedTask() {
1421         final ITaskOrganizer organizer = registerMockOrganizer();
1422         Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
1423                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
1424         final Task task1 = createRootTask();
1425         final Task task2 = createTask(rootTask, false /* fakeDraw */);
1426         WindowContainerTransaction wct = new WindowContainerTransaction();
1427         wct.reparent(task1.mRemoteToken.toWindowContainerToken(),
1428                 rootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
1429         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1430         assertTrue(task1.isOrganized());
1431         assertTrue(task2.isOrganized());
1432     }
1433 
1434     @Test
testAppearDeferThenInfoChange()1435     public void testAppearDeferThenInfoChange() {
1436         final ITaskOrganizer organizer = registerMockOrganizer();
1437         final Task rootTask = createRootTask();
1438         // Flush EVENT_APPEARED.
1439         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1440 
1441         // Assume layout defer
1442         mWm.mWindowPlacerLocked.deferLayout();
1443 
1444         final Task task = createTask(rootTask);
1445         final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task);
1446 
1447         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1448         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1449         waitUntilHandlersIdle();
1450 
1451         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1452         assertEquals(1, pendingEvents.size());
1453         assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType);
1454         assertEquals("TestDescription",
1455                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1456     }
1457 
1458     @Test
testReorderWithParents()1459     public void testReorderWithParents() {
1460         /*
1461                   root
1462                ____|______
1463                |         |
1464            firstTda    secondTda
1465                |             |
1466          firstRootTask    secondRootTask
1467 
1468          */
1469         final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
1470         final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
1471                 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
1472                 FEATURE_VENDOR_FIRST);
1473         final Task firstRootTask = firstTaskDisplayArea.createRootTask(
1474                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
1475         final Task secondRootTask = secondTaskDisplayArea.createRootTask(
1476                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
1477         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
1478                 .setTask(firstRootTask).build();
1479         final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
1480                 .setTask(secondRootTask).build();
1481         // This assertion is just a defense to ensure that firstRootTask is not the top most
1482         // by default
1483         assertThat(mDisplayContent.getTopRootTask()).isEqualTo(secondRootTask);
1484         WindowContainerTransaction wct = new WindowContainerTransaction();
1485 
1486         // Reorder to top
1487         wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */,
1488                 true /* includingParents */);
1489         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1490 
1491         // firstRootTask can only be on the top if its TDA was also reordered to the Top which
1492         // in-turn ensures that the reorder happened including the parents.
1493         assertThat(mDisplayContent.getTopRootTask()).isEqualTo(firstRootTask);
1494 
1495         // Reorder to bottom
1496         wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), false /* onTop */,
1497                 true /* includingParents */);
1498         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1499 
1500         // firstRootTask can only be on the bottom if its TDA was also reordered to the bottom
1501         // which in-turn ensures that the reorder happened including the parents.
1502         assertThat(mDisplayContent.getBottomMostTask()).isEqualTo(firstRootTask);
1503     }
1504 
1505     @Test
testAppearDeferThenVanish()1506     public void testAppearDeferThenVanish() {
1507         final ITaskOrganizer organizer = registerMockOrganizer();
1508         final Task rootTask = createRootTask();
1509         // Flush EVENT_APPEARED.
1510         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1511 
1512         // Assume layout defer
1513         mWm.mWindowPlacerLocked.deferLayout();
1514 
1515         final Task task = createTask(rootTask);
1516 
1517         rootTask.removeImmediately();
1518         waitUntilHandlersIdle();
1519 
1520         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1521         assertEquals(0, pendingEvents.size());
1522     }
1523 
1524     @Test
testInfoChangeDeferMultiple()1525     public void testInfoChangeDeferMultiple() {
1526         final ITaskOrganizer organizer = registerMockOrganizer();
1527         final Task rootTask = createRootTask();
1528         final Task task = createTask(rootTask);
1529         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1530 
1531         // Assume layout defer
1532         mWm.mWindowPlacerLocked.deferLayout();
1533 
1534         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1535         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1536         waitUntilHandlersIdle();
1537 
1538         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1539         assertEquals(1, pendingEvents.size());
1540         assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
1541         assertEquals("TestDescription",
1542                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1543 
1544         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2"));
1545         waitUntilHandlersIdle();
1546 
1547         pendingEvents = getTaskPendingEvent(organizer, rootTask);
1548         assertEquals(1, pendingEvents.size());
1549         assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
1550         assertEquals("TestDescription2",
1551                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1552     }
1553 
1554     @Test
testInfoChangDeferThenVanish()1555     public void testInfoChangDeferThenVanish() {
1556         final ITaskOrganizer organizer = registerMockOrganizer();
1557         final Task rootTask = createRootTask();
1558         final Task task = createTask(rootTask);
1559         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1560 
1561         // Assume layout defer
1562         mWm.mWindowPlacerLocked.deferLayout();
1563 
1564         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1565         record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
1566 
1567         rootTask.removeImmediately();
1568         waitUntilHandlersIdle();
1569 
1570         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1571         assertEquals(1, pendingEvents.size());
1572         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
1573         assertEquals("TestDescription",
1574                 pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
1575     }
1576 
1577     @Test
testVanishDeferThenInfoChange()1578     public void testVanishDeferThenInfoChange() {
1579         final ITaskOrganizer organizer = registerMockOrganizer();
1580         final Task rootTask = createRootTask();
1581         final Task task = createTask(rootTask);
1582         createActivityRecordAndDispatchPendingEvents(task);
1583 
1584         // Assume layout defer
1585         mWm.mWindowPlacerLocked.deferLayout();
1586 
1587         rootTask.removeImmediately();
1588         rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
1589         waitUntilHandlersIdle();
1590 
1591         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1592         assertEquals(1, pendingEvents.size());
1593         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
1594     }
1595 
1596     @Test
testVanishDeferThenBackOnRoot()1597     public void testVanishDeferThenBackOnRoot() {
1598         final ITaskOrganizer organizer = registerMockOrganizer();
1599         final Task rootTask = createRootTask();
1600         final Task task = createTask(rootTask);
1601         final ActivityRecord record = createActivityRecordAndDispatchPendingEvents(task);
1602 
1603         // Assume layout defer
1604         mWm.mWindowPlacerLocked.deferLayout();
1605 
1606         rootTask.removeImmediately();
1607         mWm.mAtmService.mActivityClientController.onBackPressed(record.token,
1608                 new IRequestFinishCallback.Default());
1609         waitUntilHandlersIdle();
1610 
1611         ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
1612         assertEquals(1, pendingEvents.size());
1613         assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
1614     }
1615 
getTaskPendingEvent(ITaskOrganizer organizer, Task task)1616     private ArrayList<PendingTaskEvent> getTaskPendingEvent(ITaskOrganizer organizer, Task task) {
1617         ArrayList<PendingTaskEvent> total =
1618                 mWm.mAtmService.mTaskOrganizerController
1619                         .getTaskOrganizerPendingEvents(organizer.asBinder())
1620                         .getPendingEventList();
1621         ArrayList<PendingTaskEvent> result = new ArrayList();
1622 
1623         for (int i = 0; i < total.size(); i++) {
1624             PendingTaskEvent entry = total.get(i);
1625             if (entry.mTask.mTaskId == task.mTaskId) {
1626                 result.add(entry);
1627             }
1628         }
1629 
1630         return result;
1631     }
1632 
1633     @Test
testReparentNonResizableTaskToSplitScreen()1634     public void testReparentNonResizableTaskToSplitScreen() {
1635         final ActivityRecord activity = new ActivityBuilder(mAtm)
1636                 .setCreateTask(true)
1637                 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
1638                 .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
1639                 .build();
1640         final Task rootTask = activity.getRootTask();
1641         rootTask.setResizeMode(activity.info.resizeMode);
1642         final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
1643                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
1644         final WindowContainerTransaction wct = new WindowContainerTransaction();
1645         wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
1646                 splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
1647 
1648         // Can't reparent non-resizable to split screen
1649         mAtm.mSupportsNonResizableMultiWindow = -1;
1650         mAtm.mWindowOrganizerController.applyTransaction(wct);
1651 
1652         assertEquals(rootTask, activity.getRootTask());
1653 
1654         // Allow reparent non-resizable to split screen
1655         mAtm.mSupportsNonResizableMultiWindow = 1;
1656         mAtm.mWindowOrganizerController.applyTransaction(wct);
1657 
1658         assertEquals(splitPrimaryRootTask, activity.getRootTask());
1659     }
1660 
1661     @Test
testSizeCompatModeChangedOnFirstOrganizedTask()1662     public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
1663         final ITaskOrganizer organizer = registerMockOrganizer();
1664         final Task rootTask = createRootTask();
1665         final Task task = createTask(rootTask);
1666         final ActivityRecord activity = createActivityRecordAndDispatchPendingEvents(task);
1667         final ArgumentCaptor<RunningTaskInfo> infoCaptor =
1668                 ArgumentCaptor.forClass(RunningTaskInfo.class);
1669 
1670         assertTrue(rootTask.isOrganized());
1671 
1672         doReturn(true).when(activity).inSizeCompatMode();
1673         doReturn(true).when(activity).isState(RESUMED);
1674 
1675         // Ensure task info show top activity in size compat.
1676         rootTask.onSizeCompatActivityChanged();
1677         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1678         verify(organizer).onTaskInfoChanged(infoCaptor.capture());
1679         RunningTaskInfo info = infoCaptor.getValue();
1680         assertEquals(rootTask.mTaskId, info.taskId);
1681         assertTrue(info.appCompatTaskInfo.topActivityInSizeCompat);
1682 
1683         // Ensure task info show top activity that is not visible as not in size compat.
1684         clearInvocations(organizer);
1685         doReturn(false).when(activity).isVisible();
1686         rootTask.onSizeCompatActivityChanged();
1687         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1688         verify(organizer).onTaskInfoChanged(infoCaptor.capture());
1689         info = infoCaptor.getValue();
1690         assertEquals(rootTask.mTaskId, info.taskId);
1691         assertFalse(info.appCompatTaskInfo.topActivityInSizeCompat);
1692 
1693         // Ensure task info show non size compat top activity as not in size compat.
1694         clearInvocations(organizer);
1695         doReturn(true).when(activity).isVisible();
1696         doReturn(false).when(activity).inSizeCompatMode();
1697         rootTask.onSizeCompatActivityChanged();
1698         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
1699         verify(organizer).onTaskInfoChanged(infoCaptor.capture());
1700         info = infoCaptor.getValue();
1701         assertEquals(rootTask.mTaskId, info.taskId);
1702         assertFalse(info.appCompatTaskInfo.topActivityInSizeCompat);
1703     }
1704 
1705     @Test
testStartTasksInTransaction()1706     public void testStartTasksInTransaction() {
1707         WindowContainerTransaction wct = new WindowContainerTransaction();
1708         ActivityOptions testOptions = ActivityOptions.makeBasic();
1709         testOptions.setTransientLaunch();
1710         wct.startTask(1, null /* options */);
1711         wct.startTask(2, testOptions.toBundle());
1712         spyOn(mWm.mAtmService.mTaskSupervisor);
1713         doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents(
1714                 anyInt(), anyInt(), anyInt(), any());
1715         clearInvocations(mWm.mAtmService);
1716         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
1717 
1718         verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
1719                 anyInt(), anyInt(), eq(1), any());
1720 
1721         final ArgumentCaptor<SafeActivityOptions> optionsCaptor =
1722                 ArgumentCaptor.forClass(SafeActivityOptions.class);
1723         verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
1724                 anyInt(), anyInt(), eq(2), optionsCaptor.capture());
1725         assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
1726     }
1727 
1728     @Test
testResumeTopsWhenLeavingPinned()1729     public void testResumeTopsWhenLeavingPinned() {
1730         final ActivityRecord record = makePipableActivity();
1731         final Task rootTask = record.getRootTask();
1732 
1733         clearInvocations(mWm.mAtmService.mRootWindowContainer);
1734         final WindowContainerTransaction t = new WindowContainerTransaction();
1735         WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken();
1736         t.setWindowingMode(wct, WINDOWING_MODE_PINNED);
1737         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
1738         verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
1739 
1740         clearInvocations(mWm.mAtmService.mRootWindowContainer);
1741         // The token for the PIP root task may have changed when the task entered PIP mode, so do
1742         // not reuse the one from above.
1743         final WindowContainerToken newToken =
1744                 record.getRootTask().mRemoteToken.toWindowContainerToken();
1745         t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN);
1746         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
1747         verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
1748     }
1749 
1750     @Test
testSetAlwaysOnTop()1751     public void testSetAlwaysOnTop() {
1752         final Task rootTask = new TaskBuilder(mSupervisor)
1753                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
1754         testSetAlwaysOnTop(rootTask);
1755 
1756         final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
1757         displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
1758         testSetAlwaysOnTop(displayArea);
1759     }
1760 
testSetAlwaysOnTop(WindowContainer wc)1761     private void testSetAlwaysOnTop(WindowContainer wc) {
1762         final WindowContainerTransaction t = new WindowContainerTransaction();
1763         t.setAlwaysOnTop(wc.mRemoteToken.toWindowContainerToken(), true);
1764         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
1765         assertTrue(wc.isAlwaysOnTop());
1766 
1767         t.setAlwaysOnTop(wc.mRemoteToken.toWindowContainerToken(), false);
1768         mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
1769         assertFalse(wc.isAlwaysOnTop());
1770     }
1771 
createActivityRecordAndDispatchPendingEvents(Task task)1772     private ActivityRecord createActivityRecordAndDispatchPendingEvents(Task task) {
1773         final ActivityRecord record = createActivityRecord(task);
1774         // Flush EVENT_APPEARED.
1775         mAtm.mTaskOrganizerController.dispatchPendingEvents();
1776         return record;
1777     }
1778 
1779     /**
1780      * Verifies that task vanished is called for a specific task.
1781      */
assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)1782     private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)
1783             throws RemoteException {
1784         ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class);
1785         verify(organizer, atLeastOnce()).onTaskVanished(arg.capture());
1786         List<RunningTaskInfo> taskInfos = arg.getAllValues();
1787 
1788         HashSet<Integer> vanishedTaskIds = new HashSet<>();
1789         for (int i = 0; i < taskInfos.size(); i++) {
1790             vanishedTaskIds.add(taskInfos.get(i).taskId);
1791         }
1792         HashSet<Integer> taskIds = new HashSet<>();
1793         for (int i = 0; i < tasks.length; i++) {
1794             taskIds.add(tasks[i].mTaskId);
1795         }
1796 
1797         assertTrue(expectVanished
1798                 ? vanishedTaskIds.containsAll(taskIds)
1799                 : !vanishedTaskIds.removeAll(taskIds));
1800     }
1801 
assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks)1802     private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) {
1803         HashSet<Integer> taskIds = new HashSet<>();
1804         for (int i = 0; i < taskInfos.size(); i++) {
1805             taskIds.add(taskInfos.get(i).getTaskInfo().taskId);
1806         }
1807         for (int i = 0; i < expectedTasks.length; i++) {
1808             assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
1809         }
1810     }
1811 
1812     @NonNull
createTaskFragmentOrganizer( @onNull WindowContainerTransaction t, boolean isSystemOrganizer)1813     private TaskFragmentOrganizer createTaskFragmentOrganizer(
1814             @NonNull WindowContainerTransaction t, boolean isSystemOrganizer) {
1815         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1816         final ITaskFragmentOrganizer organizerInterface =
1817                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
1818         registerTaskFragmentOrganizer(
1819                 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()),
1820                 isSystemOrganizer);
1821         t.setTaskFragmentOrganizer(organizerInterface);
1822 
1823         return organizer;
1824     }
1825 }
1826