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