1 /*
2  * Copyright (C) 2017 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.ActivityTaskManager.INVALID_TASK_ID;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
24 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
25 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
26 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
27 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
28 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
29 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
30 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
31 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
32 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
33 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
34 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
35 import static android.view.Surface.ROTATION_0;
36 import static android.view.Surface.ROTATION_90;
37 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
38 
39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
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.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
45 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
46 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
47 
48 import static com.google.common.truth.Truth.assertThat;
49 
50 import static org.hamcrest.Matchers.not;
51 import static org.hamcrest.Matchers.sameInstance;
52 import static org.junit.Assert.assertEquals;
53 import static org.junit.Assert.assertFalse;
54 import static org.junit.Assert.assertNotEquals;
55 import static org.junit.Assert.assertNotNull;
56 import static org.junit.Assert.assertNull;
57 import static org.junit.Assert.assertTrue;
58 import static org.mockito.ArgumentMatchers.any;
59 import static org.mockito.ArgumentMatchers.anyBoolean;
60 import static org.mockito.ArgumentMatchers.anyInt;
61 import static org.mockito.ArgumentMatchers.anyString;
62 import static org.mockito.ArgumentMatchers.eq;
63 import static org.mockito.ArgumentMatchers.same;
64 import static org.mockito.Mockito.atLeast;
65 import static org.mockito.Mockito.clearInvocations;
66 import static org.mockito.Mockito.never;
67 
68 import android.app.ActivityManager;
69 import android.app.ActivityOptions;
70 import android.app.TaskInfo;
71 import android.app.WindowConfiguration;
72 import android.content.ComponentName;
73 import android.content.Intent;
74 import android.content.pm.ActivityInfo;
75 import android.content.pm.ApplicationInfo;
76 import android.content.res.Configuration;
77 import android.graphics.Point;
78 import android.graphics.Rect;
79 import android.os.IBinder;
80 import android.platform.test.annotations.Presubmit;
81 import android.util.DisplayMetrics;
82 import android.util.Xml;
83 import android.view.Display;
84 import android.view.DisplayInfo;
85 import android.window.TaskFragmentOrganizer;
86 
87 import androidx.test.filters.MediumTest;
88 
89 import com.android.modules.utils.TypedXmlPullParser;
90 import com.android.modules.utils.TypedXmlSerializer;
91 
92 import org.junit.Assert;
93 import org.junit.Before;
94 import org.junit.Test;
95 import org.junit.runner.RunWith;
96 import org.mockito.Mockito;
97 import org.xmlpull.v1.XmlPullParser;
98 import org.xmlpull.v1.XmlPullParserException;
99 
100 import java.io.ByteArrayInputStream;
101 import java.io.ByteArrayOutputStream;
102 import java.io.IOException;
103 import java.io.InputStreamReader;
104 import java.io.Reader;
105 
106 /**
107  * Test class for {@link Task}.
108  *
109  * Build/Install/Run:
110  *  atest WmTests:TaskTests
111  */
112 @MediumTest
113 @Presubmit
114 @RunWith(WindowTestRunner.class)
115 public class TaskTests extends WindowTestsBase {
116 
117     private static final String TASK_TAG = "task";
118 
119     private Rect mParentBounds;
120 
121     @Before
setUp()122     public void setUp() throws Exception {
123         mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
124         removeGlobalMinSizeRestriction();
125     }
126 
127     @Test
testRemoveContainer()128     public void testRemoveContainer() {
129         final Task rootTask = createTask(mDisplayContent);
130         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
131         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
132 
133         task.remove(false /* withTransition */, "testRemoveContainer");
134         // There is still an activity to be destroyed, so the task is not removed immediately.
135         assertNotNull(task.getParent());
136         assertTrue(rootTask.hasChild());
137         assertTrue(task.hasChild());
138         assertTrue(activity.finishing);
139 
140         activity.destroyed("testRemoveContainer");
141         // Assert that the container was removed after the activity is destroyed.
142         assertNull(task.getParent());
143         assertEquals(0, task.getChildCount());
144         assertNull(activity.getParent());
145         verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(task);
146         verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(rootTask);
147     }
148 
149     @Test
testRemoveContainer_multipleNestedTasks()150     public void testRemoveContainer_multipleNestedTasks() {
151         final Task rootTask = createTask(mDisplayContent);
152         rootTask.mCreatedByOrganizer = true;
153         final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
154         final Task task2 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
155         final ActivityRecord activity1 = createActivityRecord(task1);
156         final ActivityRecord activity2 = createActivityRecord(task2);
157         activity1.setVisible(false);
158 
159         // All activities under the root task should be finishing.
160         rootTask.remove(true /* withTransition */, "test");
161         assertTrue(activity1.finishing);
162         assertTrue(activity2.finishing);
163 
164         // After all activities activities are destroyed, the root task should also be removed.
165         activity1.removeImmediately();
166         activity2.removeImmediately();
167         assertFalse(rootTask.isAttached());
168     }
169 
170     @Test
testRemoveContainer_deferRemoval()171     public void testRemoveContainer_deferRemoval() {
172         final Task rootTask = createTask(mDisplayContent);
173         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
174         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
175 
176         doReturn(true).when(task).shouldDeferRemoval();
177 
178         task.removeIfPossible();
179         // For the case of deferred removal the task will still be connected to the its app token
180         // until the task window container is removed.
181         assertNotNull(task.getParent());
182         assertNotEquals(0, task.getChildCount());
183         assertNotNull(activity.getParent());
184 
185         task.removeImmediately();
186         assertNull(task.getParent());
187         assertEquals(0, task.getChildCount());
188         assertNull(activity.getParent());
189     }
190 
191     @Test
testReparent()192     public void testReparent() {
193         final Task taskController1 = createTask(mDisplayContent);
194         final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
195         final Task taskController2 = createTask(mDisplayContent);
196         final Task task2 = createTaskInRootTask(taskController2, 0 /* userId */);
197 
198         boolean gotException = false;
199         try {
200             task.reparent(taskController1, 0, false/* moveParents */, "testReparent");
201         } catch (IllegalArgumentException e) {
202             gotException = true;
203         }
204         assertTrue("Should not be able to reparent to the same parent", gotException);
205 
206         gotException = false;
207         try {
208             task.reparent(null, 0, false/* moveParents */, "testReparent");
209         } catch (Exception e) {
210             gotException = true;
211         }
212         assertTrue("Should not be able to reparent to a task that doesn't exist", gotException);
213 
214         task.reparent(taskController2, 0, false/* moveParents */, "testReparent");
215         assertEquals(taskController2, task.getParent());
216         assertEquals(0, task.getParent().mChildren.indexOf(task));
217         assertEquals(1, task2.getParent().mChildren.indexOf(task2));
218     }
219 
220     @Test
testReparent_BetweenDisplays()221     public void testReparent_BetweenDisplays() {
222         // Create first task on primary display.
223         final Task rootTask1 = createTask(mDisplayContent);
224         final Task task = createTaskInRootTask(rootTask1, 0 /* userId */);
225         assertEquals(mDisplayContent, rootTask1.getDisplayContent());
226 
227         // Create second display and put second task on it.
228         final DisplayContent dc = createNewDisplay();
229         final Task rootTask2 = createTask(dc);
230         final Task task2 = createTaskInRootTask(rootTask2, 0 /* userId */);
231         // Reparent and check state
232         clearInvocations(task);  // reset the number of onDisplayChanged for task.
233         task.reparent(rootTask2, 0, false /* moveParents */, "testReparent_BetweenDisplays");
234         assertEquals(rootTask2, task.getParent());
235         assertEquals(0, task.getParent().mChildren.indexOf(task));
236         assertEquals(1, task2.getParent().mChildren.indexOf(task2));
237         verify(task, times(1)).onDisplayChanged(any());
238     }
239 
240     @Test
testBounds()241     public void testBounds() {
242         final Task rootTask1 = createTask(mDisplayContent);
243         final Task task = createTaskInRootTask(rootTask1, 0 /* userId */);
244 
245         // Check that setting bounds also updates surface position
246         task.setWindowingMode(WINDOWING_MODE_FREEFORM);
247         Rect bounds = new Rect(10, 10, 100, 200);
248         task.setBounds(bounds);
249         assertEquals(new Point(bounds.left, bounds.top), task.getLastSurfacePosition());
250     }
251 
252     @Test
testIsInTask()253     public void testIsInTask() {
254         final Task task1 = createTask(mDisplayContent);
255         final Task task2 = createTask(mDisplayContent);
256         final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task1);
257         final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task2);
258         assertEquals(activity1, task1.isInTask(activity1));
259         assertNull(task1.isInTask(activity2));
260     }
261 
262     @Test
testPerformClearTop()263     public void testPerformClearTop() {
264         final Task task = createTask(mDisplayContent);
265         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
266         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
267         // Detach from process so the activities can be removed from hierarchy when finishing.
268         activity1.detachFromProcess();
269         activity2.detachFromProcess();
270         int[] finishCount = new int[1];
271         assertTrue(task.performClearTop(activity1, 0 /* launchFlags */, finishCount).finishing);
272         assertFalse(task.hasChild());
273         // In real case, the task should be preserved for adding new activity.
274         assertTrue(task.isAttached());
275 
276         final ActivityRecord activityA = new ActivityBuilder(mAtm).setTask(task).build();
277         final ActivityRecord activityB = new ActivityBuilder(mAtm).setTask(task).build();
278         final ActivityRecord activityC = new ActivityBuilder(mAtm).setTask(task).build();
279         activityA.setState(ActivityRecord.State.STOPPED, "test");
280         activityB.setState(ActivityRecord.State.PAUSED, "test");
281         activityC.setState(ActivityRecord.State.RESUMED, "test");
282         doReturn(true).when(activityB).shouldBeVisibleUnchecked();
283         doReturn(true).when(activityC).shouldBeVisibleUnchecked();
284         activityA.getConfiguration().densityDpi += 100;
285         assertTrue(task.performClearTop(activityA, 0 /* launchFlags */, finishCount).finishing);
286         // The bottom activity should destroy directly without relaunch for config change.
287         assertEquals(ActivityRecord.State.DESTROYING, activityA.getState());
288         verify(activityA, never()).startRelaunching();
289     }
290 
291     @Test
testRemoveChildForOverlayTask()292     public void testRemoveChildForOverlayTask() {
293         final Task task = createTask(mDisplayContent);
294         final int taskId = task.mTaskId;
295         final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task);
296         final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task);
297         final ActivityRecord activity3 = createActivityRecord(mDisplayContent, task);
298         activity1.setTaskOverlay(true);
299         activity2.setTaskOverlay(true);
300         activity3.setTaskOverlay(true);
301 
302         assertEquals(3, task.getChildCount());
303         assertTrue(task.onlyHasTaskOverlayActivities(true));
304 
305         task.removeChild(activity1);
306 
307         verify(task.mTaskSupervisor).removeTask(any(), anyBoolean(), anyBoolean(), anyString());
308         assertEquals(2, task.getChildCount());
309         task.forAllActivities((r) -> {
310             assertTrue(r.finishing);
311         });
312     }
313 
314     @Test
testSwitchUser()315     public void testSwitchUser() {
316         final Task rootTask = createTask(mDisplayContent);
317         final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */);
318         final Task leafTask1 = createTaskInRootTask(childTask, 10 /* userId */);
319         final Task leafTask2 = createTaskInRootTask(childTask, 0 /* userId */);
320         assertEquals(1, rootTask.getChildCount());
321         assertEquals(leafTask2, childTask.getTopChild());
322 
323         doReturn(true).when(leafTask1).showToCurrentUser();
324         rootTask.switchUser(10);
325         assertEquals(1, rootTask.getChildCount());
326         assertEquals(leafTask1, childTask.getTopChild());
327     }
328 
329     @Test
testEnsureActivitiesVisible()330     public void testEnsureActivitiesVisible() {
331         final Task rootTask = createTask(mDisplayContent);
332         final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */);
333         final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */);
334         final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask1);
335         final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask2);
336 
337         // Check visibility of occluded tasks
338         doReturn(false).when(leafTask1).shouldBeVisible(any());
339         doReturn(true).when(leafTask2).shouldBeVisible(any());
340         rootTask.ensureActivitiesVisible(
341                 null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
342         assertFalse(activity1.isVisible());
343         assertTrue(activity2.isVisible());
344 
345         // Check visibility of not occluded tasks
346         doReturn(true).when(leafTask1).shouldBeVisible(any());
347         doReturn(true).when(leafTask2).shouldBeVisible(any());
348         rootTask.ensureActivitiesVisible(
349                 null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
350         assertTrue(activity1.isVisible());
351         assertTrue(activity2.isVisible());
352     }
353 
354     @Test
testResolveNonResizableTaskWindowingMode()355     public void testResolveNonResizableTaskWindowingMode() {
356         // Test with no support non-resizable in multi window regardless the screen size.
357         mAtm.mSupportsNonResizableMultiWindow = -1;
358 
359         final Task task = createTask(mDisplayContent);
360         Configuration parentConfig = task.getParent().getConfiguration();
361         parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
362         doReturn(false).when(task).isResizeable();
363         WindowConfiguration requestedOverride =
364                 task.getRequestedOverrideConfiguration().windowConfiguration;
365         WindowConfiguration resolvedOverride =
366                 task.getResolvedOverrideConfiguration().windowConfiguration;
367 
368         // The resolved override windowing mode of a non-resizeable task should be resolved as
369         // fullscreen even as a child of a freeform display.
370         requestedOverride.setWindowingMode(WINDOWING_MODE_UNDEFINED);
371         task.resolveOverrideConfiguration(parentConfig);
372         assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
373 
374         // The resolved override windowing mode of a non-resizeable task should be resolved as
375         // fullscreen, even when requested as freeform windowing mode
376         requestedOverride.setWindowingMode(WINDOWING_MODE_FREEFORM);
377         task.resolveOverrideConfiguration(parentConfig);
378         assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
379 
380         // The resolved override windowing mode of a non-resizeable task can be undefined as long
381         // as its parents is not in multi-window mode.
382         parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
383         requestedOverride.setWindowingMode(WINDOWING_MODE_UNDEFINED);
384         task.resolveOverrideConfiguration(parentConfig);
385         assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
386     }
387 
388     @Test
testHandlesOrientationChangeFromDescendant()389     public void testHandlesOrientationChangeFromDescendant() {
390         final Task rootTask = createTask(mDisplayContent,
391                 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
392         final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */);
393         final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */);
394         leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME);
395         leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD);
396 
397         // We need to use an orientation that is not an exception for the
398         // ignoreOrientationRequest flag.
399         final int orientation = SCREEN_ORIENTATION_PORTRAIT;
400 
401         assertEquals(leafTask2, rootTask.getTopChild());
402         assertTrue(rootTask.handlesOrientationChangeFromDescendant(orientation));
403         // Treat orientation request from home as handled.
404         assertTrue(leafTask1.handlesOrientationChangeFromDescendant(orientation));
405         // Orientation request from standard activity in multi window will not be handled.
406         assertFalse(leafTask2.handlesOrientationChangeFromDescendant(orientation));
407     }
408 
409     @Test
testAlwaysOnTop()410     public void testAlwaysOnTop() {
411         final Task task = createTask(mDisplayContent);
412         task.setAlwaysOnTop(true);
413         task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
414         assertTrue(task.isAlwaysOnTop());
415 
416         task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
417         assertFalse(task.isAlwaysOnTop());
418     }
419 
420     @Test
testRestoreWindowedTask()421     public void testRestoreWindowedTask() throws Exception {
422         final Task expected = createTask(64);
423         expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
424 
425         final byte[] serializedBytes = serializeToBytes(expected);
426         final Task actual = restoreFromBytes(serializedBytes);
427         assertEquals(expected.mTaskId, actual.mTaskId);
428         assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
429     }
430 
431     /** Ensure we have no chance to modify the original intent. */
432     @Test
testCopyBaseIntentForTaskInfo()433     public void testCopyBaseIntentForTaskInfo() {
434         final Task task = createTask(1);
435         task.setTaskDescription(new ActivityManager.TaskDescription());
436         final TaskInfo info = task.getTaskInfo();
437 
438         // The intent of info should be a copy so assert that they are different instances.
439         Assert.assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent())));
440     }
441 
442     @Test
testPropagateFocusedStateToRootTask()443     public void testPropagateFocusedStateToRootTask() {
444         final Task rootTask = createTask(mDefaultDisplay);
445         final Task leafTask = createTaskInRootTask(rootTask, 0 /* userId */);
446 
447         final ActivityRecord activity = createActivityRecord(leafTask);
448 
449         leafTask.getDisplayContent().setFocusedApp(activity);
450 
451         assertTrue(leafTask.getTaskInfo().isFocused);
452         assertTrue(rootTask.getTaskInfo().isFocused);
453 
454         leafTask.getDisplayContent().setFocusedApp(null);
455 
456         assertFalse(leafTask.getTaskInfo().isFocused);
457         assertFalse(rootTask.getTaskInfo().isFocused);
458     }
459 
460     @Test
testReturnsToHomeRootTask()461     public void testReturnsToHomeRootTask() throws Exception {
462         final Task task = createTask(1);
463         spyOn(task);
464         doReturn(true).when(task).hasChild();
465         assertFalse(task.returnsToHomeRootTask());
466         task.intent = null;
467         assertFalse(task.returnsToHomeRootTask());
468         task.intent = new Intent();
469         assertFalse(task.returnsToHomeRootTask());
470         task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
471         assertTrue(task.returnsToHomeRootTask());
472     }
473 
474     /** Ensures that empty bounds cause appBounds to inherit from parent. */
475     @Test
testAppBounds_EmptyBounds()476     public void testAppBounds_EmptyBounds() {
477         final Rect emptyBounds = new Rect();
478         testRootTaskBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
479                 mParentBounds);
480     }
481 
482     /** Ensures that bounds on freeform root tasks are not clipped. */
483     @Test
testAppBounds_FreeFormBounds()484     public void testAppBounds_FreeFormBounds() {
485         final Rect freeFormBounds = new Rect(mParentBounds);
486         freeFormBounds.offset(10, 10);
487         testRootTaskBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
488                 freeFormBounds);
489     }
490 
491     /** Ensures that fully contained bounds are not clipped. */
492     @Test
testAppBounds_ContainedBounds()493     public void testAppBounds_ContainedBounds() {
494         final Rect insetBounds = new Rect(mParentBounds);
495         insetBounds.inset(5, 5, 5, 5);
496         testRootTaskBoundsConfiguration(
497                 WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
498     }
499 
500     @Test
testFitWithinBounds()501     public void testFitWithinBounds() {
502         final Rect parentBounds = new Rect(10, 10, 200, 200);
503         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
504         Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
505                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
506         Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
507         final Configuration parentConfig = rootTask.getConfiguration();
508         parentConfig.windowConfiguration.setBounds(parentBounds);
509         parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
510 
511         // check top and left
512         Rect reqBounds = new Rect(-190, -190, 0, 0);
513         task.setBounds(reqBounds);
514         // Make sure part of it is exposed
515         assertTrue(task.getBounds().right > parentBounds.left);
516         assertTrue(task.getBounds().bottom > parentBounds.top);
517         // Should still be more-or-less in that corner
518         assertTrue(task.getBounds().left <= parentBounds.left);
519         assertTrue(task.getBounds().top <= parentBounds.top);
520 
521         assertEquals(reqBounds.width(), task.getBounds().width());
522         assertEquals(reqBounds.height(), task.getBounds().height());
523 
524         // check bottom and right
525         reqBounds = new Rect(210, 210, 400, 400);
526         task.setBounds(reqBounds);
527         // Make sure part of it is exposed
528         assertTrue(task.getBounds().left < parentBounds.right);
529         assertTrue(task.getBounds().top < parentBounds.bottom);
530         // Should still be more-or-less in that corner
531         assertTrue(task.getBounds().right >= parentBounds.right);
532         assertTrue(task.getBounds().bottom >= parentBounds.bottom);
533 
534         assertEquals(reqBounds.width(), task.getBounds().width());
535         assertEquals(reqBounds.height(), task.getBounds().height());
536     }
537 
538     /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
539     @Test
testBoundsOnModeChangeFreeformToFullscreen()540     public void testBoundsOnModeChangeFreeformToFullscreen() {
541         DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
542         Task rootTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true)
543                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
544         Task task = rootTask.getBottomMostTask();
545         task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
546         DisplayInfo info = new DisplayInfo();
547         display.mDisplay.getDisplayInfo(info);
548         final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
549         final Rect freeformBounds = new Rect(fullScreenBounds);
550         freeformBounds.inset((int) (freeformBounds.width() * 0.2),
551                 (int) (freeformBounds.height() * 0.2));
552         task.setBounds(freeformBounds);
553 
554         assertEquals(freeformBounds, task.getBounds());
555 
556         // FULLSCREEN inherits bounds
557         rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
558         assertEquals(fullScreenBounds, task.getBounds());
559         assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
560 
561         // FREEFORM restores bounds
562         rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
563         assertEquals(freeformBounds, task.getBounds());
564     }
565 
566     @Test
testTopActivityEligibleForUserAspectRatioButton()567     public void testTopActivityEligibleForUserAspectRatioButton() {
568         DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
569         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
570                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
571         final Task task = rootTask.getBottomMostTask();
572         final ActivityRecord root = task.getTopNonFinishingActivity();
573         spyOn(mWm.mLetterboxConfiguration);
574         spyOn(root);
575         spyOn(root.mLetterboxUiController);
576 
577         doReturn(true).when(root.mLetterboxUiController)
578                 .shouldEnableUserAspectRatioSettings();
579         doReturn(false).when(root).inSizeCompatMode();
580         doReturn(task).when(root).getOrganizedTask();
581 
582         // The button should be eligible to be displayed
583         assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
584 
585         // When shouldApplyUserMinAspectRatioOverride is disable the button is not enabled
586         doReturn(false).when(root.mLetterboxUiController)
587                 .shouldEnableUserAspectRatioSettings();
588         assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
589         doReturn(true).when(root.mLetterboxUiController)
590                 .shouldEnableUserAspectRatioSettings();
591 
592         // When in size compat mode the button is not enabled
593         doReturn(true).when(root).inSizeCompatMode();
594         assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
595         doReturn(false).when(root).inSizeCompatMode();
596     }
597 
598     /**
599      * Tests that a task with forced orientation has orientation-consistent bounds within the
600      * parent.
601      */
602     @Test
testFullscreenBoundsForcedOrientation()603     public void testFullscreenBoundsForcedOrientation() {
604         final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
605         final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
606         final DisplayContent display = new TestDisplayContent.Builder(mAtm,
607                 fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
608         assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
609         // Fix the display orientation to landscape which is the natural rotation (0) for the test
610         // display.
611         final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
612         dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
613         dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
614 
615         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
616                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
617         final Task task = rootTask.getBottomMostTask();
618         final ActivityRecord root = task.getTopNonFinishingActivity();
619 
620         assertEquals(fullScreenBounds, task.getBounds());
621 
622         // Setting app to fixed portrait fits within parent
623         root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
624         assertEquals(root, task.getRootActivity());
625         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
626         // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds.
627         assertThat(task.getBounds().height()).isLessThan(task.getBounds().width());
628         assertEquals(fullScreenBounds, task.getBounds());
629 
630         // Top activity gets used
631         final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(rootTask)
632                 .build();
633         assertEquals(top, task.getTopNonFinishingActivity());
634         top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
635         assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
636         assertEquals(task.getBounds().width(), fullScreenBounds.width());
637 
638         // Setting app to unspecified restores
639         top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
640         assertEquals(fullScreenBounds, task.getBounds());
641 
642         // Setting app to fixed landscape and changing display
643         top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
644         // Fix the display orientation to portrait which is 90 degrees for the test display.
645         dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
646 
647         // Fixed orientation request should be resolved on activity level. Task fills display
648         // bounds.
649         assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
650         assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
651         assertEquals(fullScreenBoundsPort, task.getBounds());
652 
653         // in FREEFORM, no constraint
654         final Rect freeformBounds = new Rect(display.getBounds());
655         freeformBounds.inset((int) (freeformBounds.width() * 0.2),
656                 (int) (freeformBounds.height() * 0.2));
657         rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
658         task.setBounds(freeformBounds);
659         assertEquals(freeformBounds, task.getBounds());
660 
661         // FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
662         rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
663         rootTask.setBounds(null);
664         assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
665         assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
666         assertEquals(fullScreenBoundsPort, task.getBounds());
667 
668         // FREEFORM restores bounds as before
669         rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
670         assertEquals(freeformBounds, task.getBounds());
671     }
672 
673     @Test
testReportsOrientationRequestInLetterboxForOrientation()674     public void testReportsOrientationRequestInLetterboxForOrientation() {
675         final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
676         final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
677         final DisplayContent display = new TestDisplayContent.Builder(mAtm,
678                 fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build();
679         assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId));
680         // Fix the display orientation to landscape which is the natural rotation (0) for the test
681         // display.
682         final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
683         dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
684         dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
685 
686         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
687                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
688         final Task task = rootTask.getBottomMostTask();
689         ActivityRecord root = task.getTopNonFinishingActivity();
690 
691         assertEquals(fullScreenBounds, task.getBounds());
692 
693         // Setting app to fixed portrait fits within parent on activity level. Task fills parent.
694         root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
695         assertThat(root.getBounds().width()).isLessThan(root.getBounds().height());
696         assertEquals(task.getBounds(), fullScreenBounds);
697 
698         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
699     }
700 
701     @Test
testIgnoresForcedOrientationWhenParentHandles()702     public void testIgnoresForcedOrientationWhenParentHandles() {
703         final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
704         DisplayContent display = new TestDisplayContent.Builder(
705                 mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
706 
707         display.getRequestedOverrideConfiguration().orientation =
708                 Configuration.ORIENTATION_LANDSCAPE;
709         display.onRequestedOverrideConfigurationChanged(
710                 display.getRequestedOverrideConfiguration());
711         Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
712                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
713         Task task = rootTask.getBottomMostTask();
714         ActivityRecord root = task.getTopNonFinishingActivity();
715 
716         final WindowContainer parentWindowContainer =
717                 new WindowContainer(mSystemServicesTestRule.getWindowManagerService());
718         spyOn(parentWindowContainer);
719         parentWindowContainer.setBounds(fullScreenBounds);
720         doReturn(parentWindowContainer).when(task).getParent();
721         doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea();
722         doReturn(rootTask).when(task).getRootTask();
723         doReturn(true).when(parentWindowContainer)
724                 .handlesOrientationChangeFromDescendant(anyInt());
725 
726         // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
727         // bounds because its parent says it will handle it at a later time.
728         root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
729         assertEquals(root, task.getRootActivity());
730         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
731         assertEquals(fullScreenBounds, task.getBounds());
732     }
733 
734     @Test
testComputeConfigResourceOverrides()735     public void testComputeConfigResourceOverrides() {
736         final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920);
737         TestDisplayContent display = new TestDisplayContent.Builder(
738                 mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
739         final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
740         final Configuration inOutConfig = new Configuration();
741         final Configuration parentConfig = new Configuration();
742         final Rect parentBounds = new Rect(0, 0, 250, 500);
743         final Rect parentAppBounds = new Rect(0, 0, 250, 480);
744         parentConfig.windowConfiguration.setBounds(parentBounds);
745         parentConfig.windowConfiguration.setAppBounds(parentAppBounds);
746         parentConfig.densityDpi = 400;
747         parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200
748         parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100
749         parentConfig.windowConfiguration.setRotation(ROTATION_0);
750 
751         // By default, the input bounds will fill parent.
752         task.computeConfigResourceOverrides(inOutConfig, parentConfig);
753 
754         assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp);
755         assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp);
756         assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds());
757         assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation);
758 
759         // If bounds are overridden, config properties should be made to match. Surface hierarchy
760         // will crop for policy.
761         inOutConfig.setToDefaults();
762         final int longSide = 960;
763         final int shortSide = 540;
764         parentConfig.densityDpi = 192;
765         final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide);
766         inOutConfig.windowConfiguration.setBounds(largerPortraitBounds);
767         task.computeConfigResourceOverrides(inOutConfig, parentConfig);
768         // The override bounds are beyond the parent, the out appBounds should not be intersected
769         // by parent appBounds.
770         assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds());
771         assertEquals(800, inOutConfig.screenHeightDp); // 960/(192/160) = 800
772         assertEquals(450, inOutConfig.screenWidthDp); // 540/(192/160) = 450
773 
774         inOutConfig.setToDefaults();
775         // Landscape bounds.
776         final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide);
777         inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds);
778 
779         // Setup the display with a top stable inset. The later assertion will ensure the inset is
780         // excluded from screenHeightDp.
781         final int statusBarHeight = 100;
782         final DisplayInfo di = display.getDisplayInfo();
783         display.getDisplayPolicy().getDecorInsetsInfo(di.rotation,
784                 di.logicalWidth, di.logicalHeight).mConfigInsets.top = statusBarHeight;
785 
786         // Without limiting to be inside the parent bounds, the out screen size should keep relative
787         // to the input bounds.
788         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
789         final ActivityRecord.CompatDisplayInsets compatInsets =
790                 new ActivityRecord.CompatDisplayInsets(
791                         display, activity, /* fixedOrientationBounds= */ null);
792         task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatInsets);
793 
794         assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
795         final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
796         final int expectedHeightDp = (int) ((shortSide - statusBarHeight) / density + 0.5f);
797         assertEquals(expectedHeightDp, inOutConfig.screenHeightDp);
798         final int expectedWidthDp = (int) (longSide / density + 0.5f);
799         assertEquals(expectedWidthDp, inOutConfig.screenWidthDp);
800         assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation);
801     }
802 
803     @Test
testComputeConfigResourceLayoutOverrides()804     public void testComputeConfigResourceLayoutOverrides() {
805         final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500);
806         TestDisplayContent display = new TestDisplayContent.Builder(
807                 mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
808         final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
809         final Configuration inOutConfig = new Configuration();
810         final Configuration parentConfig = new Configuration();
811         final Rect nonLongBounds = new Rect(0, 0, 1000, 1250);
812         parentConfig.windowConfiguration.setBounds(fullScreenBounds);
813         parentConfig.windowConfiguration.setAppBounds(fullScreenBounds);
814         parentConfig.densityDpi = 400;
815         parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi;
816         parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi;
817         parentConfig.windowConfiguration.setRotation(ROTATION_0);
818 
819         // Set BOTH screenW/H to an override value
820         inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi;
821         inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi;
822         task.computeConfigResourceOverrides(inOutConfig, parentConfig);
823 
824         // screenLayout should honor override when both screenW/H are set.
825         assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0);
826     }
827 
828     @Test
testComputeNestedConfigResourceOverrides()829     public void testComputeNestedConfigResourceOverrides() {
830         final Task task = new TaskBuilder(mSupervisor).build();
831         assertTrue(task.getResolvedOverrideBounds().isEmpty());
832         int origScreenH = task.getConfiguration().screenHeightDp;
833         Configuration rootTaskConfig = new Configuration();
834         rootTaskConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration());
835         rootTaskConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
836 
837         // Set bounds on root task (not task) and verify that the task resource configuration
838         // changes despite it's override bounds being empty.
839         Rect bounds = new Rect(task.getRootTask().getBounds());
840         bounds.bottom = (int) (bounds.bottom * 0.6f);
841         rootTaskConfig.windowConfiguration.setBounds(bounds);
842         task.getRootTask().onRequestedOverrideConfigurationChanged(rootTaskConfig);
843         assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp);
844     }
845 
846     @Test
testFullScreenTaskNotAdjustedByMinimalSize()847     public void testFullScreenTaskNotAdjustedByMinimalSize() {
848         final Task fullscreenTask = new TaskBuilder(mSupervisor).build();
849         final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds());
850         final ActivityInfo aInfo = new ActivityInfo();
851         aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */,
852                 0 /* height */, 0 /* heightFraction */, 0 /* gravity */,
853                 originalTaskBounds.width() * 2 /* minWidth */,
854                 originalTaskBounds.height() * 2 /* minHeight */);
855         fullscreenTask.setMinDimensions(aInfo);
856         fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration());
857 
858         assertEquals(originalTaskBounds, fullscreenTask.getBounds());
859     }
860 
861     @Test
testInsetDisregardedWhenFreeformOverlapsNavBar()862     public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
863         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
864         Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
865                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
866         DisplayInfo displayInfo = new DisplayInfo();
867         mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
868         final int displayHeight = displayInfo.logicalHeight;
869         final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
870         final Configuration inOutConfig = new Configuration();
871         final Configuration parentConfig = new Configuration();
872         final int longSide = 1200;
873         final int shortSide = 600;
874         parentConfig.densityDpi = 400;
875         parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px
876         parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px
877         parentConfig.windowConfiguration.setRotation(ROTATION_0);
878 
879         final int longSideDp = 480; // longSide / density = 1200 / 400 * 160
880         final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160
881         final int screenLayout = parentConfig.screenLayout
882                 & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
883         final int reducedScreenLayout =
884                 Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp);
885 
886         // Portrait bounds overlapping with navigation bar, without insets.
887         final Rect freeformBounds = new Rect(0,
888                 displayHeight - 10 - longSide,
889                 shortSide,
890                 displayHeight - 10);
891         inOutConfig.windowConfiguration.setBounds(freeformBounds);
892         // Set to freeform mode to verify bug fix.
893         inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
894 
895         task.computeConfigResourceOverrides(inOutConfig, parentConfig);
896 
897         // screenW/H should not be effected by parent since overridden and freeform
898         assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
899                 inOutConfig.screenWidthDp);
900         assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
901                 inOutConfig.screenHeightDp);
902         assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
903 
904         inOutConfig.setToDefaults();
905         // Landscape bounds overlapping with navigtion bar, without insets.
906         freeformBounds.set(0,
907                 displayHeight - 10 - shortSide,
908                 longSide,
909                 displayHeight - 10);
910         inOutConfig.windowConfiguration.setBounds(freeformBounds);
911         inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
912 
913         task.computeConfigResourceOverrides(inOutConfig, parentConfig);
914 
915         assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi,
916                 inOutConfig.screenWidthDp);
917         assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi,
918                 inOutConfig.screenHeightDp);
919         assertEquals(reducedScreenLayout, inOutConfig.screenLayout);
920     }
921 
922     /** Ensures that the alias intent won't have target component resolved. */
923     @Test
testTaskIntentActivityAlias()924     public void testTaskIntentActivityAlias() {
925         final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
926         final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
927         final ComponentName aliasComponent =
928                 new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
929         final ComponentName targetComponent =
930                 new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
931 
932         final Intent intent = new Intent();
933         intent.setPackage(DEFAULT_COMPONENT_PACKAGE_NAME);
934         intent.setComponent(aliasComponent);
935         final ActivityInfo info = new ActivityInfo();
936         info.applicationInfo = new ApplicationInfo();
937         info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
938         info.targetActivity = targetClassName;
939 
940         final Task task = new Task.Builder(mAtm)
941                 .setTaskId(1)
942                 .setActivityInfo(info)
943                 .setIntent(intent)
944                 .build();
945         assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
946                 task.intent.getComponent().getClassName());
947 
948         ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent(
949                 aliasComponent).setTargetActivity(targetClassName).build();
950         assertEquals("Should be the same intent filter.", true,
951                 task.isSameIntentFilter(aliasActivity));
952 
953         ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent(
954                 targetComponent).build();
955         assertEquals("Should be the same intent filter.", true,
956                 task.isSameIntentFilter(targetActivity));
957 
958         ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build();
959         assertEquals("Should not be the same intent filter.", false,
960                 task.isSameIntentFilter(defaultActivity));
961     }
962 
963     /** Test that root activity index is reported correctly for several activities in the task. */
964     @Test
testFindRootIndex()965     public void testFindRootIndex() {
966         final Task task = getTestTask();
967         // Add an extra activity on top of the root one
968         new ActivityBuilder(mAtm).setTask(task).build();
969 
970         assertEquals("The root activity in the task must be reported.", task.getChildAt(0),
971                 task.getRootActivity(
972                         true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
973     }
974 
975     /**
976      * Test that root activity index is reported correctly for several activities in the task when
977      * the activities on the bottom are finishing.
978      */
979     @Test
testFindRootIndex_finishing()980     public void testFindRootIndex_finishing() {
981         final Task task = getTestTask();
982         // Add extra two activities and mark the two on the bottom as finishing.
983         final ActivityRecord activity0 = task.getBottomMostActivity();
984         activity0.finishing = true;
985         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
986         activity1.finishing = true;
987         new ActivityBuilder(mAtm).setTask(task).build();
988 
989         assertEquals("The first non-finishing activity in the task must be reported.",
990                 task.getChildAt(2), task.getRootActivity(
991                         true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
992     }
993 
994     /**
995      * Test that root activity index is reported correctly for several activities in the task when
996      * looking for the 'effective root'.
997      */
998     @Test
testFindRootIndex_effectiveRoot()999     public void testFindRootIndex_effectiveRoot() {
1000         final Task task = getTestTask();
1001         // Add an extra activity on top of the root one
1002         new ActivityBuilder(mAtm).setTask(task).build();
1003 
1004         assertEquals("The root activity in the task must be reported.",
1005                 task.getChildAt(0), task.getRootActivity(
1006                         false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
1007     }
1008 
1009     /**
1010      * Test that root activity index is reported correctly when looking for the 'effective root' in
1011      * case when bottom activities are relinquishing task identity or finishing.
1012      */
1013     @Test
testFindRootIndex_effectiveRoot_finishingAndRelinquishing()1014     public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() {
1015         final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
1016         final Task task = activity0.getTask();
1017         // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
1018         // one above as finishing.
1019         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1020         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1021         activity1.finishing = true;
1022         new ActivityBuilder(mAtm).setTask(task).build();
1023 
1024         assertEquals("The first non-finishing activity and non-relinquishing task identity "
1025                 + "must be reported.", task.getChildAt(2), task.getRootActivity(
1026                 false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
1027     }
1028 
1029     /**
1030      * Test that root activity index is reported correctly when looking for the 'effective root'
1031      * for the case when there is only a single activity that also has relinquishTaskIdentity set.
1032      */
1033     @Test
testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity()1034     public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() {
1035         final Task task = getTestTask();
1036         // Set relinquishTaskIdentity for the only activity in the task
1037         task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1038 
1039         assertEquals("The root activity in the task must be reported.",
1040                 task.getChildAt(0), task.getRootActivity(
1041                         false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
1042     }
1043 
1044     /**
1045      * Test that the topmost activity index is reported correctly when looking for the
1046      * 'effective root' for the case when all activities have relinquishTaskIdentity set.
1047      */
1048     @Test
testFindRootIndex_effectiveRoot_relinquishingMultipleActivities()1049     public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
1050         final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
1051         final Task task = activity0.getTask();
1052         // Set relinquishTaskIdentity for all activities in the task
1053         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1054         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1055         activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1056 
1057         assertEquals("The topmost activity in the task must be reported.",
1058                 task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
1059                         false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
1060     }
1061 
1062     /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
1063     @Test
testGetRootActivity()1064     public void testGetRootActivity() {
1065         final Task task = getTestTask();
1066         // Add an extra activity on top of the root one
1067         new ActivityBuilder(mAtm).setTask(task).build();
1068 
1069         assertEquals("The root activity in the task must be reported.",
1070                 task.getBottomMostActivity(), task.getRootActivity());
1071     }
1072 
1073     /**
1074      * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}.
1075      */
1076     @Test
testGetRootActivity_finishing()1077     public void testGetRootActivity_finishing() {
1078         final Task task = getTestTask();
1079         // Add an extra activity on top of the root one
1080         new ActivityBuilder(mAtm).setTask(task).build();
1081         // Mark the root as finishing
1082         task.getBottomMostActivity().finishing = true;
1083 
1084         assertEquals("The first non-finishing activity in the task must be reported.",
1085                 task.getChildAt(1), task.getRootActivity());
1086     }
1087 
1088     /**
1089      * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}.
1090      */
1091     @Test
testGetRootActivity_relinquishTaskIdentity()1092     public void testGetRootActivity_relinquishTaskIdentity() {
1093         final Task task = getTestTask();
1094         // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY.
1095         final ActivityRecord activity0 = task.getBottomMostActivity();
1096         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1097         // Add an extra activity on top of the root one.
1098         new ActivityBuilder(mAtm).setTask(task).build();
1099 
1100         assertEquals("The root activity in the task must be reported.",
1101                 task.getBottomMostActivity(), task.getRootActivity());
1102     }
1103 
1104     /**
1105      * Test that no activity is reported in {@link Task#getRootActivity()} when all activities
1106      * in the task are finishing.
1107      */
1108     @Test
testGetRootActivity_allFinishing()1109     public void testGetRootActivity_allFinishing() {
1110         final Task task = getTestTask();
1111         // Mark the bottom-most activity as finishing.
1112         final ActivityRecord activity0 = task.getBottomMostActivity();
1113         activity0.finishing = true;
1114         // Add an extra activity on top of the root one and mark it as finishing
1115         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1116         activity1.finishing = true;
1117 
1118         assertNull("No activity must be reported if all are finishing", task.getRootActivity());
1119     }
1120 
1121     /**
1122      * Test that first non-finishing activity is the root of task.
1123      */
1124     @Test
testIsRootActivity()1125     public void testIsRootActivity() {
1126         final Task task = getTestTask();
1127         // Mark the bottom-most activity as finishing.
1128         final ActivityRecord activity0 = task.getBottomMostActivity();
1129         activity0.finishing = true;
1130         // Add an extra activity on top of the root one.
1131         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1132 
1133         assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask());
1134         assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask());
1135     }
1136 
1137     /**
1138      * Test that if all activities in the task are finishing, then the one on the bottom is the
1139      * root of task.
1140      */
1141     @Test
testIsRootActivity_allFinishing()1142     public void testIsRootActivity_allFinishing() {
1143         final Task task = getTestTask();
1144         // Mark the bottom-most activity as finishing.
1145         final ActivityRecord activity0 = task.getBottomMostActivity();
1146         activity0.finishing = true;
1147         // Add an extra activity on top of the root one and mark it as finishing
1148         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1149         activity1.finishing = true;
1150 
1151         assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask());
1152         assertFalse("Finishing activity on top must not be the root of task",
1153                 activity1.isRootOfTask());
1154     }
1155 
1156     /**
1157      * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}.
1158      */
1159     @Test
testGetTaskForActivity()1160     public void testGetTaskForActivity() {
1161         final Task task0 = getTestTask();
1162         final ActivityRecord activity0 = task0.getBottomMostActivity();
1163 
1164         final Task task1 = getTestTask();
1165         final ActivityRecord activity1 = task1.getBottomMostActivity();
1166 
1167         assertEquals(task0.mTaskId,
1168                 ActivityRecord.getTaskForActivityLocked(activity0.token, false /* onlyRoot */));
1169         assertEquals(task1.mTaskId,
1170                 ActivityRecord.getTaskForActivityLocked(activity1.token,  false /* onlyRoot */));
1171     }
1172 
1173     /**
1174      * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing
1175      * activity.
1176      */
1177     @Test
testGetTaskForActivity_onlyRoot_finishing()1178     public void testGetTaskForActivity_onlyRoot_finishing() {
1179         final Task task = getTestTask();
1180         // Make the current root activity finishing
1181         final ActivityRecord activity0 = task.getBottomMostActivity();
1182         activity0.finishing = true;
1183         // Add an extra activity on top - this will be the new root
1184         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1185         // Add one more on top
1186         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
1187 
1188         assertEquals(task.mTaskId,
1189                 ActivityRecord.getTaskForActivityLocked(activity0.token, true /* onlyRoot */));
1190         assertEquals(task.mTaskId,
1191                 ActivityRecord.getTaskForActivityLocked(activity1.token, true /* onlyRoot */));
1192         assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
1193                 ActivityRecord.getTaskForActivityLocked(activity2.token, true /* onlyRoot */));
1194     }
1195 
1196     /**
1197      * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that
1198      * relinquishes task identity.
1199      */
1200     @Test
testGetTaskForActivity_onlyRoot_relinquishTaskIdentity()1201     public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
1202         final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build();
1203         final Task task = activity0.getTask();
1204         // Make the current root activity relinquish task identity
1205         activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1206         // Add an extra activity on top - this will be the new root
1207         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1208         // Add one more on top
1209         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
1210 
1211         assertEquals(task.mTaskId,
1212                 ActivityRecord.getTaskForActivityLocked(activity0.token, true /* onlyRoot */));
1213         assertEquals(task.mTaskId,
1214                 ActivityRecord.getTaskForActivityLocked(activity1.token, true /* onlyRoot */));
1215         assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
1216                 ActivityRecord.getTaskForActivityLocked(activity2.token, true /* onlyRoot */));
1217     }
1218 
1219     /**
1220      * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root
1221      * entries.
1222      */
1223     @Test
testGetTaskForActivity_notOnlyRoot()1224     public void testGetTaskForActivity_notOnlyRoot() {
1225         final Task task = getTestTask();
1226         // Mark the bottom-most activity as finishing.
1227         final ActivityRecord activity0 = task.getBottomMostActivity();
1228         activity0.finishing = true;
1229 
1230         // Add an extra activity on top of the root one and make it relinquish task identity
1231         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1232         activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1233 
1234         // Add one more activity on top
1235         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
1236 
1237         assertEquals(task.mTaskId,
1238                 ActivityRecord.getTaskForActivityLocked(activity0.token, false /* onlyRoot */));
1239         assertEquals(task.mTaskId,
1240                 ActivityRecord.getTaskForActivityLocked(activity1.token, false /* onlyRoot */));
1241         assertEquals(task.mTaskId,
1242                 ActivityRecord.getTaskForActivityLocked(activity2.token, false /* onlyRoot */));
1243     }
1244 
1245     /**
1246      * Test {@link Task#updateEffectiveIntent()}.
1247      */
1248     @Test
testUpdateEffectiveIntent()1249     public void testUpdateEffectiveIntent() {
1250         // Test simple case with a single activity.
1251         final Task task = getTestTask();
1252         final ActivityRecord activity0 = task.getBottomMostActivity();
1253 
1254         spyOn(task);
1255         task.updateEffectiveIntent();
1256         verify(task).setIntent(eq(activity0));
1257     }
1258 
1259     /**
1260      * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This
1261      * should make the task use the second activity when updating the intent.
1262      */
1263     @Test
testUpdateEffectiveIntent_rootFinishing()1264     public void testUpdateEffectiveIntent_rootFinishing() {
1265         // Test simple case with a single activity.
1266         final Task task = getTestTask();
1267         final ActivityRecord activity0 = task.getBottomMostActivity();
1268         // Mark the bottom-most activity as finishing.
1269         activity0.finishing = true;
1270         // Add an extra activity on top of the root one
1271         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1272 
1273         spyOn(task);
1274         task.updateEffectiveIntent();
1275         verify(task).setIntent(eq(activity1));
1276     }
1277 
1278     /**
1279      * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or
1280      * relinquishing task identity. In this case the root activity should still be used when
1281      * updating the intent (legacy behavior).
1282      */
1283     @Test
testUpdateEffectiveIntent_allFinishing()1284     public void testUpdateEffectiveIntent_allFinishing() {
1285         // Test simple case with a single activity.
1286         final Task task = getTestTask();
1287         final ActivityRecord activity0 = task.getBottomMostActivity();
1288         // Mark the bottom-most activity as finishing.
1289         activity0.finishing = true;
1290         // Add an extra activity on top of the root one and make it relinquish task identity
1291         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1292         activity1.finishing = true;
1293 
1294         // Task must still update the intent using the root activity (preserving legacy behavior).
1295         spyOn(task);
1296         task.updateEffectiveIntent();
1297         verify(task).setIntent(eq(activity0));
1298     }
1299 
1300     /**
1301      * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but
1302      * another with different uid. This should make the task use the root activity when updating the
1303      * intent.
1304      */
1305     @Test
testUpdateEffectiveIntent_relinquishingWithDifferentUid()1306     public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() {
1307         final ActivityRecord activity0 = new ActivityBuilder(mAtm)
1308                 .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
1309         final Task task = activity0.getTask();
1310 
1311         // Add an extra activity on top
1312         new ActivityBuilder(mAtm).setUid(11).setTask(task).build();
1313 
1314         spyOn(task);
1315         task.updateEffectiveIntent();
1316         verify(task).setIntent(eq(activity0));
1317     }
1318 
1319     /**
1320      * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity.
1321      * This should make the task use the topmost activity when updating the intent.
1322      */
1323     @Test
testUpdateEffectiveIntent_relinquishingMultipleActivities()1324     public void testUpdateEffectiveIntent_relinquishingMultipleActivities() {
1325         final ActivityRecord activity0 = new ActivityBuilder(mAtm)
1326                 .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build();
1327         final Task task = activity0.getTask();
1328         // Add an extra activity on top
1329         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
1330         activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
1331 
1332         // Add an extra activity on top
1333         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build();
1334 
1335         spyOn(task);
1336         task.updateEffectiveIntent();
1337         verify(task).setIntent(eq(activity2));
1338     }
1339 
1340     @Test
testSaveLaunchingStateWhenConfigurationChanged()1341     public void testSaveLaunchingStateWhenConfigurationChanged() {
1342         LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
1343         spyOn(persister);
1344 
1345         final Task task = getTestTask();
1346         task.setHasBeenVisible(false);
1347         task.getDisplayContent().getDefaultTaskDisplayArea()
1348                 .setWindowingMode(WINDOWING_MODE_FREEFORM);
1349         task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
1350 
1351         task.setHasBeenVisible(true);
1352         task.onConfigurationChanged(task.getParent().getConfiguration());
1353 
1354         verify(persister).saveTask(task, task.getDisplayContent());
1355     }
1356 
1357     @Test
testSaveLaunchingStateWhenClearingParent()1358     public void testSaveLaunchingStateWhenClearingParent() {
1359         LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
1360         spyOn(persister);
1361 
1362         final Task task = getTestTask();
1363         task.setHasBeenVisible(true);
1364         task.getDisplayContent()
1365                 .getDefaultTaskDisplayArea()
1366                 .setWindowingMode(WINDOWING_MODE_FREEFORM);
1367         task.getRootTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
1368         final DisplayContent oldDisplay = task.getDisplayContent();
1369 
1370         LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams();
1371         persister.getLaunchParams(task, null, params);
1372         assertEquals(WINDOWING_MODE_FREEFORM, params.mWindowingMode);
1373 
1374         task.setHasBeenVisible(true);
1375         task.removeImmediately();
1376 
1377         verify(persister).saveTask(task, oldDisplay);
1378 
1379         persister.getLaunchParams(task, null, params);
1380         assertEquals(WINDOWING_MODE_FREEFORM, params.mWindowingMode);
1381     }
1382 
1383     @Test
testNotSaveLaunchingStateNonFreeformDisplay()1384     public void testNotSaveLaunchingStateNonFreeformDisplay() {
1385         LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
1386         spyOn(persister);
1387 
1388         final Task task = getTestTask();
1389         task.setHasBeenVisible(false);
1390         task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
1391 
1392         task.setHasBeenVisible(true);
1393         task.onConfigurationChanged(task.getParent().getConfiguration());
1394 
1395         Mockito.verify(persister, never()).saveTask(same(task), any());
1396     }
1397 
1398     @Test
testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow()1399     public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() {
1400         LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
1401         spyOn(persister);
1402 
1403         final Task task = getTestTask();
1404         task.setHasBeenVisible(false);
1405         task.getDisplayContent().getDefaultTaskDisplayArea()
1406                 .setWindowingMode(WINDOWING_MODE_FREEFORM);
1407         task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
1408 
1409         task.setHasBeenVisible(true);
1410         task.onConfigurationChanged(task.getParent().getConfiguration());
1411 
1412         Mockito.verify(persister, never()).saveTask(same(task), any());
1413     }
1414 
1415     @Test
testNotSaveLaunchingStateForNonLeafTask()1416     public void testNotSaveLaunchingStateForNonLeafTask() {
1417         LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
1418         spyOn(persister);
1419 
1420         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true)
1421                 .setCreateParentTask(true).build().getRootTask();
1422         task.setHasBeenVisible(false);
1423         task.getDisplayContent().getDefaultTaskDisplayArea()
1424                 .setWindowingMode(WINDOWING_MODE_FREEFORM);
1425         task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
1426 
1427         final Task leafTask = createTaskInRootTask(task, 0 /* userId */);
1428 
1429         leafTask.setHasBeenVisible(true);
1430         task.setHasBeenVisible(true);
1431         task.onConfigurationChanged(task.getParent().getConfiguration());
1432 
1433         Mockito.verify(persister, never()).saveTask(same(task), any());
1434         verify(persister).saveTask(same(leafTask), any());
1435     }
1436 
1437     @Test
testNotSpecifyOrientationByFloatingTask()1438     public void testNotSpecifyOrientationByFloatingTask() {
1439         final Task task = new TaskBuilder(mSupervisor)
1440                 .setCreateActivity(true).setCreateParentTask(true).build();
1441         final ActivityRecord activity = task.getTopMostActivity();
1442         final WindowContainer<?> parentContainer = task.getParent();
1443         final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
1444         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
1445 
1446         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation());
1447         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
1448 
1449         task.setWindowingMode(WINDOWING_MODE_PINNED);
1450 
1451         // TDA returns the last orientation when child returns UNSET
1452         assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation());
1453         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
1454     }
1455 
1456     @Test
testNotSpecifyOrientation_taskDisplayAreaNotFocused()1457     public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() {
1458         final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
1459         final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
1460                 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
1461                 FEATURE_VENDOR_FIRST);
1462         final Task firstRootTask = firstTaskDisplayArea.createRootTask(
1463                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
1464         final Task secondRootTask = secondTaskDisplayArea.createRootTask(
1465                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
1466         final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
1467                 .setTask(firstRootTask).build();
1468         final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
1469                 .setTask(secondRootTask).build();
1470         firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
1471         secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
1472 
1473         // Activity on TDA1 is focused
1474         mDisplayContent.setFocusedApp(firstActivity);
1475 
1476         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
1477         assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
1478 
1479         // No focused app, TDA1 is still recorded as last focused.
1480         mDisplayContent.setFocusedApp(null);
1481 
1482         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
1483         assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
1484 
1485         // Activity on TDA2 is focused
1486         mDisplayContent.setFocusedApp(secondActivity);
1487 
1488         assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation());
1489         assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation());
1490     }
1491 
1492     @Test
testTaskOrientationOnDisplayWindowingModeChange()1493     public void testTaskOrientationOnDisplayWindowingModeChange() {
1494         // Skip unnecessary operations to speed up the test.
1495         mAtm.deferWindowLayout();
1496         final Task task = getTestTask();
1497         final ActivityRecord activity = task.getTopMostActivity();
1498         final DisplayContent display = task.getDisplayContent();
1499         mWm.setWindowingMode(display.mDisplayId, WINDOWING_MODE_FREEFORM);
1500 
1501         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
1502         assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
1503         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, display.getLastOrientation());
1504 
1505         mWm.setWindowingMode(display.mDisplayId, WINDOWING_MODE_FULLSCREEN);
1506         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
1507         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, display.getLastOrientation());
1508         assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
1509     }
1510 
1511     @Test
testGetNonNullDimmerOnUntrustedDisplays()1512     public void testGetNonNullDimmerOnUntrustedDisplays() {
1513         final DisplayInfo untrustedDisplayInfo = new DisplayInfo(mDisplayInfo);
1514         untrustedDisplayInfo.flags &= ~Display.FLAG_TRUSTED;
1515         final DisplayContent untrustedDisplay = createNewDisplay(untrustedDisplayInfo);
1516         final ActivityRecord activity = createActivityRecord(untrustedDisplay);
1517         activity.setOccludesParent(false);
1518         assertNotNull(activity.getTask().getDimmer());
1519     }
1520 
1521     @Test
testResumeTask_doNotResumeTaskFragmentBehindTranslucent()1522     public void testResumeTask_doNotResumeTaskFragmentBehindTranslucent() {
1523         final Task task = createTask(mDisplayContent);
1524         final TaskFragment tfBehind = createTaskFragmentWithActivity(task);
1525         final TaskFragment tfFront = createTaskFragmentWithActivity(task);
1526         spyOn(tfFront);
1527         doReturn(true).when(tfFront).isTranslucent(any());
1528 
1529         // TaskFragment behind another translucent TaskFragment should not be resumed.
1530         assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
1531                 tfBehind.getVisibility(null /* starting */));
1532         assertTrue(tfBehind.isFocusable());
1533         assertFalse(tfBehind.canBeResumed(null /* starting */));
1534 
1535         spyOn(tfBehind);
1536         task.resumeTopActivityUncheckedLocked(null /* prev */, ActivityOptions.makeBasic(),
1537                 false /* deferPause */);
1538 
1539         verify(tfBehind, never()).resumeTopActivity(any(), any(), anyBoolean());
1540     }
1541 
1542     @Test
testGetTaskFragment()1543     public void testGetTaskFragment() {
1544         final Task parentTask = createTask(mDisplayContent);
1545         final TaskFragment tf0 = createTaskFragmentWithActivity(parentTask);
1546         final TaskFragment tf1 = createTaskFragmentWithActivity(parentTask);
1547 
1548         assertNull("Could not find it because there's no organized TaskFragment",
1549                 parentTask.getTaskFragment(TaskFragment::isOrganizedTaskFragment));
1550 
1551         doReturn(true).when(tf0).isOrganizedTaskFragment();
1552 
1553         assertEquals("tf0 must be return because it's the organized TaskFragment.",
1554                 tf0, parentTask.getTaskFragment(TaskFragment::isOrganizedTaskFragment));
1555     }
1556 
1557     @Test
testReorderActivityToFront()1558     public void testReorderActivityToFront() {
1559         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
1560         final Task task =  new TaskBuilder(mSupervisor).setCreateActivity(true).build();
1561         final ActivityRecord activity = task.getTopMostActivity();
1562 
1563         final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
1564         final ActivityRecord embeddedActivity = fragment.getTopMostActivity();
1565         task.moveActivityToFront(activity);
1566         assertEquals("Activity must be moved to front", activity, task.getTopMostActivity());
1567 
1568         doNothing().when(fragment).sendTaskFragmentInfoChanged();
1569         task.moveActivityToFront(embeddedActivity);
1570         assertEquals("Activity must be moved to front", embeddedActivity,
1571                 task.getTopMostActivity());
1572         assertEquals("Activity must not be embedded", embeddedActivity,
1573                 task.getTopChild());
1574     }
1575 
getTestTask()1576     private Task getTestTask() {
1577         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
1578         return task.getBottomMostTask();
1579     }
1580 
testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, Rect expectedConfigBounds)1581     private void testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
1582             Rect expectedConfigBounds) {
1583 
1584         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
1585         Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
1586                 true /* onTop */);
1587         Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
1588 
1589         final Configuration parentConfig = rootTask.getConfiguration();
1590         parentConfig.windowConfiguration.setAppBounds(parentBounds);
1591         task.setBounds(bounds);
1592 
1593         task.resolveOverrideConfiguration(parentConfig);
1594         // Assert that both expected and actual are null or are equal to each other
1595         assertEquals(expectedConfigBounds,
1596                 task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
1597     }
1598 
serializeToBytes(Task r)1599     private byte[] serializeToBytes(Task r) throws Exception {
1600         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
1601             final TypedXmlSerializer serializer = Xml.newFastSerializer();
1602             serializer.setOutput(os, "UTF-8");
1603             serializer.startDocument(null, true);
1604             serializer.startTag(null, TASK_TAG);
1605             r.saveToXml(serializer);
1606             serializer.endTag(null, TASK_TAG);
1607             serializer.endDocument();
1608 
1609             os.flush();
1610             return os.toByteArray();
1611         }
1612     }
1613 
restoreFromBytes(byte[] in)1614     private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
1615         try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
1616             final TypedXmlPullParser parser = Xml.newFastPullParser();
1617             parser.setInput(reader);
1618             assertEquals(XmlPullParser.START_TAG, parser.next());
1619             assertEquals(TASK_TAG, parser.getName());
1620             return Task.restoreFromXml(parser, mAtm.mTaskSupervisor);
1621         }
1622     }
1623 
createTask(int taskId)1624     private Task createTask(int taskId) {
1625         return new Task.Builder(mAtm)
1626                 .setTaskId(taskId)
1627                 .setIntent(new Intent())
1628                 .setRealActivity(ActivityBuilder.getDefaultComponent())
1629                 .setEffectiveUid(10050)
1630                 .buildInner();
1631     }
1632 }
1633