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