diff options
| author | 2021-03-25 02:50:00 +0000 | |
|---|---|---|
| committer | 2021-03-25 02:50:00 +0000 | |
| commit | fbdd35d6d76df283c71e6eb7f9aa77b470fbd348 (patch) | |
| tree | ebcccfe13ea19bbf206c754f4bdb907c932bc936 | |
| parent | bb3a1c43c5d24bad0fea6518ced34766725944dc (diff) | |
| parent | c7b8a545c7e7346f5e3b4b5c2fd3d7b5156c18da (diff) | |
Merge changes Ifc7e4d86,I4ef9abad into sc-dev
* changes:
Rename Stack to Task related terms in tests
Merge some unit test classes
20 files changed, 3391 insertions, 3617 deletions
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java deleted file mode 100644 index 1700707d9866..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; -import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.WindowContainer.POSITION_TOP; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for the {@link DisplayContent} class. - * - * Build/Install/Run: - * atest WmTests:ActivityDisplayTests - */ -@SmallTest -@Presubmit -@RunWith(WindowTestRunner.class) -// TODO(b/144248496): Merge to DisplayContentTests -public class ActivityDisplayTests extends WindowTestsBase { - - @Test - public void testLastFocusedStackIsUpdatedWhenMovingStack() { - // Create a stack at bottom. - final TaskDisplayArea taskDisplayAreas = - mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea(); - final Task stack = - new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); - final Task prevFocusedStack = taskDisplayAreas.getFocusedRootTask(); - - stack.moveToFront("moveStackToFront"); - // After moving the stack to front, the previous focused should be the last focused. - assertTrue(stack.isFocusedRootTaskOnDisplay()); - assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedRootTask()); - - stack.moveToBack("moveStackToBack", null /* task */); - // After moving the stack to back, the stack should be the last focused. - assertEquals(stack, taskDisplayAreas.getLastFocusedRootTask()); - } - - /** - * This test simulates the picture-in-picture menu activity launches an activity to fullscreen - * stack. The fullscreen stack should be the top focused for resuming correctly. - */ - @Test - public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() { - // Create a pinned stack and move to front. - final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea() - .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor) - .setParentTask(pinnedStack).build(); - new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE) - .setTask(pinnedTask).build(); - pinnedStack.moveToFront("movePinnedStackToFront"); - - // The focused stack should be the pinned stack. - assertTrue(pinnedStack.isFocusedRootTaskOnDisplay()); - - // Create a fullscreen stack and move to front. - final Task fullscreenStack = createFullscreenStackWithSimpleActivityAt( - mRootWindowContainer.getDefaultDisplay()); - fullscreenStack.moveToFront("moveFullscreenStackToFront"); - - // The focused stack should be the fullscreen stack. - assertTrue(fullscreenStack.isFocusedRootTaskOnDisplay()); - } - - /** - * Test {@link TaskDisplayArea#mPreferredTopFocusableRootTask} will be cleared when - * the stack is removed or moved to back, and the focused stack will be according to z-order. - */ - @Test - public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() { - // Create a display which only contains 2 stacks. - final DisplayContent display = addNewDisplayContentAt(POSITION_TOP); - final Task stack1 = createFullscreenStackWithSimpleActivityAt(display); - final Task stack2 = createFullscreenStackWithSimpleActivityAt(display); - - // Put stack1 and stack2 on top. - stack1.moveToFront("moveStack1ToFront"); - stack2.moveToFront("moveStack2ToFront"); - assertTrue(stack2.isFocusedRootTaskOnDisplay()); - - // Stack1 should be focused after moving stack2 to back. - stack2.moveToBack("moveStack2ToBack", null /* task */); - assertTrue(stack1.isFocusedRootTaskOnDisplay()); - - // Stack2 should be focused after removing stack1. - stack1.getDisplayArea().removeRootTask(stack1); - assertTrue(stack2.isFocusedRootTaskOnDisplay()); - } - - /** - * Verifies {@link DisplayContent#remove} should not resume home stack on the removing display. - */ - @Test - public void testNotResumeHomeStackOnRemovingDisplay() { - // Create a display which supports system decoration and allows reparenting stacks to - // another display when the display is removed. - final DisplayContent display = new TestDisplayContent.Builder( - mAtm, 1000, 1500).setSystemDecorations(true).build(); - doReturn(false).when(display).shouldDestroyContentOnRemove(); - - // Put home stack on the display. - final Task homeStack = new TaskBuilder(mSupervisor) - .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build(); - - // Put a finishing standard activity which will be reparented. - final Task stack = createFullscreenStackWithSimpleActivityAt(display); - stack.topRunningActivity().makeFinishingLocked(); - - clearInvocations(homeStack); - display.remove(); - - // The removed display should have no focused stack and its home stack should never resume. - assertNull(display.getFocusedRootTask()); - verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any()); - } - - private Task createFullscreenStackWithSimpleActivityAt(DisplayContent display) { - final Task fullscreenStack = display.getDefaultTaskDisplayArea().createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task fullscreenTask = new TaskBuilder(mAtm.mTaskSupervisor) - .setParentTask(fullscreenStack).build(); - new ActivityBuilder(mAtm).setTask(fullscreenTask).build(); - return fullscreenStack; - } - - /** - * Verifies the correct activity is returned when querying the top running activity. - */ - @Test - public void testTopRunningActivity() { - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); - final ActivityRecord activity = stack.getTopNonFinishingActivity(); - - // Create empty stack on top. - final Task emptyStack = new TaskBuilder(mSupervisor).build(); - - // Make sure the top running activity is not affected when keyguard is not locked. - assertTopRunningActivity(activity, display); - - // Check to make sure activity not reported when it cannot show on lock and lock is on. - doReturn(true).when(keyguard).isKeyguardLocked(); - assertEquals(activity, display.topRunningActivity()); - assertNull(display.topRunningActivity(true /* considerKeyguardState */)); - - // Move stack with activity to top. - stack.moveToFront("testStackToFront"); - assertEquals(stack, display.getFocusedRootTask()); - assertEquals(activity, display.topRunningActivity()); - assertNull(display.topRunningActivity(true /* considerKeyguardState */)); - - // Add activity that should be shown on the keyguard. - final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm) - .setTask(stack) - .setActivityFlags(FLAG_SHOW_WHEN_LOCKED) - .build(); - - // Ensure the show when locked activity is returned. - assertTopRunningActivity(showWhenLockedActivity, display); - - // Move empty stack to front. The running activity in focusable stack which below the - // empty stack should be returned. - emptyStack.moveToFront("emptyStackToFront"); - assertEquals(stack, display.getFocusedRootTask()); - assertTopRunningActivity(showWhenLockedActivity, display); - } - - private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) { - assertEquals(top, display.topRunningActivity()); - assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */)); - } - - /** - * This test enforces that alwaysOnTop stack is placed at proper position. - */ - @Test - public void testAlwaysOnTopStackLocation() { - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task alwaysOnTopStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(alwaysOnTopStack).build(); - alwaysOnTopStack.setAlwaysOnTop(true); - taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopStack, - false /* includingParents */); - assertTrue(alwaysOnTopStack.isAlwaysOnTop()); - // Ensure always on top state is synced to the children of the stack. - assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop()); - assertEquals(alwaysOnTopStack, taskDisplayArea.getTopRootTask()); - - final Task pinnedStack = taskDisplayArea.createRootTask( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask()); - assertEquals(pinnedStack, taskDisplayArea.getTopRootTask()); - - final Task anotherAlwaysOnTopStack = taskDisplayArea.createRootTask( - WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); - anotherAlwaysOnTopStack.setAlwaysOnTop(true); - taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack, - false /* includingParents */); - assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); - int topPosition = taskDisplayArea.getRootTaskCount() - 1; - // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the - // existing alwaysOnTop stack. - assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack)); - - final Task nonAlwaysOnTopStack = taskDisplayArea.createRootTask( - WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); - assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea()); - topPosition = taskDisplayArea.getRootTaskCount() - 1; - // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the - // existing other non-alwaysOnTop stacks. - assertEquals(topPosition - 3, getTaskIndexOf(taskDisplayArea, nonAlwaysOnTopStack)); - - anotherAlwaysOnTopStack.setAlwaysOnTop(false); - taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack, - false /* includingParents */); - assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop()); - // Ensure, when always on top is turned off for a stack, the stack is put just below all - // other always on top stacks. - assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack)); - anotherAlwaysOnTopStack.setAlwaysOnTop(true); - - // Ensure always on top state changes properly when windowing mode changes. - anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop()); - assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack)); - anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); - assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack)); - - final Task dreamStack = taskDisplayArea.createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */); - assertEquals(taskDisplayArea, dreamStack.getDisplayArea()); - assertTrue(dreamStack.isAlwaysOnTop()); - topPosition = taskDisplayArea.getRootTaskCount() - 1; - // Ensure dream shows above all activities, including PiP - assertEquals(dreamStack, taskDisplayArea.getTopRootTask()); - assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, pinnedStack)); - - final Task assistStack = taskDisplayArea.createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); - assertEquals(taskDisplayArea, assistStack.getDisplayArea()); - assertFalse(assistStack.isAlwaysOnTop()); - topPosition = taskDisplayArea.getRootTaskCount() - 1; - - // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream - // is false and on top of everything when true. - final boolean isAssistantOnTop = mContext.getResources() - .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream); - assertEquals(isAssistantOnTop ? topPosition : topPosition - 4, - getTaskIndexOf(taskDisplayArea, assistStack)); - } - - @Test - public void testRemoveRootTaskInWindowingModes() { - removeStackTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes( - WINDOWING_MODE_FULLSCREEN)); - } - - @Test - public void testRemoveStackWithActivityTypes() { - removeStackTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes( - ACTIVITY_TYPE_STANDARD)); - } - - private void removeStackTests(Runnable runnable) { - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task stack1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task stack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task stack3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task stack4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack1).build(); - final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack2).build(); - final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack3).build(); - final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack4).build(); - - // Reordering stacks while removing stacks. - doAnswer(invocation -> { - taskDisplayArea.positionChildAt(POSITION_TOP, stack3, false /*includingParents*/); - return true; - }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any()); - - // Removing stacks from the display while removing stacks. - doAnswer(invocation -> { - taskDisplayArea.removeRootTask(stack2); - return true; - }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); - - runnable.run(); - verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any()); - verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any()); - verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); - verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any()); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index ce5fc4021eac..678defe36566 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -405,7 +405,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { public void testGetAnimationTargets_taskContainsMultipleTasks() { // [DisplayContent] - [Task] -+- [Task1] - [ActivityRecord1] (opening, invisible) // +- [Task2] - [ActivityRecord2] (closing, visible) - final Task parentTask = createTaskStackOnDisplay(mDisplayContent); + final Task parentTask = createTask(mDisplayContent); final ActivityRecord activity1 = createActivityRecordWithParentTask(parentTask); activity1.setVisible(false); activity1.mVisibleRequested = true; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 83aca5e2d482..c3279bf05737 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -177,13 +177,13 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - public void testCleanAppTransitionWhenTaskStackReparent() { + public void testCleanAppTransitionWhenRootTaskReparent() { // Create 2 displays & presume both display the state is ON for ready to display & animate. final DisplayContent dc1 = createNewDisplay(Display.STATE_ON); final DisplayContent dc2 = createNewDisplay(Display.STATE_ON); - final Task stack1 = createTaskStackOnDisplay(dc1); - final Task task1 = createTaskInStack(stack1, 0 /* userId */); + final Task rootTask1 = createTask(dc1); + final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */); final ActivityRecord activity1 = createNonAttachedActivityRecord(dc1); task1.addChild(activity1, 0); @@ -198,8 +198,8 @@ public class AppTransitionTests extends WindowTestsBase { dc1.mOpeningApps.add(activity1); assertTrue(dc1.mOpeningApps.size() > 0); - // Move stack to another display. - stack1.reparent(dc2.getDefaultTaskDisplayArea(), true); + // Move root task to another display. + rootTask1.reparent(dc2.getDefaultTaskDisplayArea(), true); // Verify if token are cleared from both pending transition list in former display. assertFalse(dc1.mOpeningApps.contains(activity1)); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 24b4f65f65ff..0afd39f1ed01 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -16,10 +16,12 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -65,6 +67,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; @@ -350,29 +353,29 @@ public class DisplayContentTests extends WindowTestsBase { } /** - * This tests stack movement between displays and proper stack's, task's and app token's display - * container references updates. + * This tests root task movement between displays and proper root task's, task's and app token's + * display container references updates. */ @Test - public void testMoveStackBetweenDisplays() { + public void testMoveRootTaskBetweenDisplays() { // Create a second display. final DisplayContent dc = createNewDisplay(); - // Add stack with activity. - final Task stack = createTaskStackOnDisplay(dc); - assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId()); - assertEquals(dc, stack.getDisplayContent()); + // Add root task with activity. + final Task rootTask = createTask(dc); + assertEquals(dc.getDisplayId(), rootTask.getDisplayContent().getDisplayId()); + assertEquals(dc, rootTask.getDisplayContent()); - final Task task = createTaskInStack(stack, 0 /* userId */); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); final ActivityRecord activity = createNonAttachedActivityRecord(dc); task.addChild(activity, 0); assertEquals(dc, task.getDisplayContent()); assertEquals(dc, activity.getDisplayContent()); - // Move stack to first display. - stack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */); - assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId()); - assertEquals(mDisplayContent, stack.getDisplayContent()); + // Move root task to first display. + rootTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */); + assertEquals(mDisplayContent.getDisplayId(), rootTask.getDisplayContent().getDisplayId()); + assertEquals(mDisplayContent, rootTask.getDisplayContent()); assertEquals(mDisplayContent, task.getDisplayContent()); assertEquals(mDisplayContent, activity.getDisplayContent()); } @@ -424,7 +427,7 @@ public class DisplayContentTests extends WindowTestsBase { } /** - * Tests tapping on a stack in different display results in window gaining focus. + * Tests tapping on a root task in different display results in window gaining focus. */ @Test public void testInputEventBringsCorrectDisplayInFocus() { @@ -432,16 +435,16 @@ public class DisplayContentTests extends WindowTestsBase { // Create a second display final DisplayContent dc1 = createNewDisplay(); - // Add stack with activity. - final Task stack0 = createTaskStackOnDisplay(dc0); - final Task task0 = createTaskInStack(stack0, 0 /* userId */); + // Add root task with activity. + final Task rootTask0 = createTask(dc0); + final Task task0 = createTaskInRootTask(rootTask0, 0 /* userId */); final ActivityRecord activity = createNonAttachedActivityRecord(dc0); task0.addChild(activity, 0); dc0.configureDisplayPolicy(); assertNotNull(dc0.mTapDetector); - final Task stack1 = createTaskStackOnDisplay(dc1); - final Task task1 = createTaskInStack(stack1, 0 /* userId */); + final Task rootTask1 = createTask(dc1); + final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */); final ActivityRecord activity1 = createNonAttachedActivityRecord(dc0); task1.addChild(activity1, 0); dc1.configureDisplayPolicy(); @@ -889,13 +892,13 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent newDisplay = createNewDisplay(); final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); - final Task stack = mDisplayContent.getTopRootTask(); - final ActivityRecord activity = stack.topRunningActivity(); + final Task rootTask = mDisplayContent.getTopRootTask(); + final ActivityRecord activity = rootTask.topRunningActivity(); doReturn(true).when(activity).shouldBeVisibleUnchecked(); final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1"); - final Task stack1 = newDisplay.getTopRootTask(); - final ActivityRecord activity1 = stack1.topRunningActivity(); + final Task rootTask1 = newDisplay.getTopRootTask(); + final ActivityRecord activity1 = rootTask1.topRunningActivity(); doReturn(true).when(activity1).shouldBeVisibleUnchecked(); appWin.setHasSurface(true); appWin1.setHasSurface(true); @@ -934,28 +937,28 @@ public class DisplayContentTests extends WindowTestsBase { dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); - final Task stack = new TaskBuilder(mSupervisor) + final Task rootTask = new TaskBuilder(mSupervisor) .setDisplay(dc) .setCreateActivity(true) .build(); - doReturn(true).when(stack).isVisible(); + doReturn(true).when(rootTask).isVisible(); - final Task freeformStack = new TaskBuilder(mSupervisor) + final Task freeformRootTask = new TaskBuilder(mSupervisor) .setDisplay(dc) .setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FREEFORM) .build(); - doReturn(true).when(freeformStack).isVisible(); - freeformStack.getTopChild().setBounds(100, 100, 300, 400); + doReturn(true).when(freeformRootTask).isVisible(); + freeformRootTask.getTopChild().setBounds(100, 100, 300, 400); assertTrue(dc.getDefaultTaskDisplayArea().isRootTaskVisible(WINDOWING_MODE_FREEFORM)); - freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); + freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); assertEquals(SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation()); - stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); + rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); } @@ -1683,66 +1686,6 @@ public class DisplayContentTests extends WindowTestsBase { } @Test - public void testGetOrCreateRootHomeTask_defaultDisplay() { - TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea(); - - // Remove the current home stack if it exists so a new one can be created below. - Task homeTask = defaultTaskDisplayArea.getRootHomeTask(); - if (homeTask != null) { - defaultTaskDisplayArea.removeChild(homeTask); - } - assertNull(defaultTaskDisplayArea.getRootHomeTask()); - - assertNotNull(defaultTaskDisplayArea.getOrCreateRootHomeTask()); - } - - @Test - public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() { - DisplayContent display = createNewDisplay(); - doReturn(true).when(display).supportsSystemDecorations(); - - // Remove the current home stack if it exists so a new one can be created below. - TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); - Task homeTask = taskDisplayArea.getRootHomeTask(); - if (homeTask != null) { - taskDisplayArea.removeChild(homeTask); - } - assertNull(taskDisplayArea.getRootHomeTask()); - - assertNotNull(taskDisplayArea.getOrCreateRootHomeTask()); - } - - @Test - public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() { - DisplayContent display = createNewDisplay(); - TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); - doReturn(false).when(display).supportsSystemDecorations(); - - assertNull(taskDisplayArea.getRootHomeTask()); - assertNull(taskDisplayArea.getOrCreateRootHomeTask()); - } - - @Test - public void testGetOrCreateRootHomeTask_untrustedDisplay() { - DisplayContent display = createNewDisplay(); - TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); - doReturn(false).when(display).isTrusted(); - - assertNull(taskDisplayArea.getRootHomeTask()); - assertNull(taskDisplayArea.getOrCreateRootHomeTask()); - } - - @Test - public void testGetOrCreateRootHomeTask_dontMoveToTop() { - DisplayContent display = createNewDisplay(); - display.mDontMoveToTop = true; - TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); - - assertNull(taskDisplayArea.getRootHomeTask()); - assertNull(taskDisplayArea.getOrCreateRootHomeTask()); - } - - @Test public void testValidWindowingLayer() { final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer(); assertNotNull(windowingLayer); @@ -1761,8 +1704,8 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testFindScrollCaptureTargetWindow_behindWindow() { DisplayContent display = createNewDisplay(); - Task stack = createTaskStackOnDisplay(display); - Task task = createTaskInStack(stack, 0 /* userId */); + Task rootTask = createTask(display); + Task task = createTaskInRootTask(rootTask, 0 /* userId */); WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window"); WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); @@ -1774,8 +1717,8 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testFindScrollCaptureTargetWindow_cantReceiveKeys() { DisplayContent display = createNewDisplay(); - Task stack = createTaskStackOnDisplay(display); - Task task = createTaskInStack(stack, 0 /* userId */); + Task rootTask = createTask(display); + Task task = createTaskInRootTask(rootTask, 0 /* userId */); WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window"); WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible"); invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false @@ -1788,8 +1731,8 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testFindScrollCaptureTargetWindow_taskId() { DisplayContent display = createNewDisplay(); - Task stack = createTaskStackOnDisplay(display); - Task task = createTaskInStack(stack, 0 /* userId */); + Task rootTask = createTask(display); + Task task = createTaskInRootTask(rootTask, 0 /* userId */); WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window"); WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); @@ -1800,8 +1743,8 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() { DisplayContent display = createNewDisplay(); - Task stack = createTaskStackOnDisplay(display); - Task task = createTaskInStack(stack, 0 /* userId */); + Task rootTask = createTask(display); + Task task = createTaskInRootTask(rootTask, 0 /* userId */); WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window"); window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); @@ -1828,7 +1771,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() { final DisplayContent dc = createNewDisplay(); - final Task stack = new TaskBuilder(mSupervisor) + final Task rootTask = new TaskBuilder(mSupervisor) .setDisplay(dc) .build(); doAnswer(invocation -> { @@ -1837,7 +1780,7 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(config.windowConfiguration.getWindowingMode(), config.windowConfiguration.getDisplayWindowingMode()); return null; - }).when(stack).onConfigurationChanged(any()); + }).when(rootTask).onConfigurationChanged(any()); dc.setWindowingMode(WINDOWING_MODE_FREEFORM); dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN); } @@ -2072,6 +2015,130 @@ public class DisplayContentTests extends WindowTestsBase { 0 /* delta */); } + /** + * Verifies {@link DisplayContent#remove} should not resume home root task on the removing + * display. + */ + @Test + public void testNotResumeHomeRootTaskOnRemovingDisplay() { + // Create a display which supports system decoration and allows reparenting root tasks to + // another display when the display is removed. + final DisplayContent display = new TestDisplayContent.Builder( + mAtm, 1000, 1500).setSystemDecorations(true).build(); + doReturn(false).when(display).shouldDestroyContentOnRemove(); + + // Put home root task on the display. + final Task homeRootTask = new TaskBuilder(mSupervisor) + .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build(); + + // Put a finishing standard activity which will be reparented. + final Task rootTask = createTaskWithActivity(display.getDefaultTaskDisplayArea(), + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */); + rootTask.topRunningActivity().makeFinishingLocked(); + + clearInvocations(homeRootTask); + display.remove(); + + // The removed display should have no focused root task and its home root task should never + // resume. + assertNull(display.getFocusedRootTask()); + verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(any(), any()); + } + + /** + * Verifies the correct activity is returned when querying the top running activity. + */ + @Test + public void testTopRunningActivity() { + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final KeyguardController keyguard = mSupervisor.getKeyguardController(); + final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); + final ActivityRecord activity = rootTask.getTopNonFinishingActivity(); + + // Create empty root task on top. + final Task emptyRootTask = new TaskBuilder(mSupervisor).build(); + + // Make sure the top running activity is not affected when keyguard is not locked. + assertTopRunningActivity(activity, display); + + // Check to make sure activity not reported when it cannot show on lock and lock is on. + doReturn(true).when(keyguard).isKeyguardLocked(); + assertEquals(activity, display.topRunningActivity()); + assertNull(display.topRunningActivity(true /* considerKeyguardState */)); + + // Move root task with activity to top. + rootTask.moveToFront("testRootTaskToFront"); + assertEquals(rootTask, display.getFocusedRootTask()); + assertEquals(activity, display.topRunningActivity()); + assertNull(display.topRunningActivity(true /* considerKeyguardState */)); + + // Add activity that should be shown on the keyguard. + final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm) + .setTask(rootTask) + .setActivityFlags(FLAG_SHOW_WHEN_LOCKED) + .build(); + + // Ensure the show when locked activity is returned. + assertTopRunningActivity(showWhenLockedActivity, display); + + // Move empty root task to front. The running activity in focusable root task which below + // the empty root task should be returned. + emptyRootTask.moveToFront("emptyRootTaskToFront"); + assertEquals(rootTask, display.getFocusedRootTask()); + assertTopRunningActivity(showWhenLockedActivity, display); + } + + private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) { + assertEquals(top, display.topRunningActivity()); + assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */)); + } + + @Test + public void testRemoveRootTaskInWindowingModes() { + removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes( + WINDOWING_MODE_FULLSCREEN)); + } + + @Test + public void testRemoveRootTaskWithActivityTypes() { + removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes( + ACTIVITY_TYPE_STANDARD)); + } + + private void removeRootTaskTests(Runnable runnable) { + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final Task rootTask2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final Task rootTask3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, ON_TOP); + final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask1).build(); + final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask2).build(); + final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask3).build(); + final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask4).build(); + + // Reordering root tasks while removing root tasks. + doAnswer(invocation -> { + taskDisplayArea.positionChildAt(POSITION_TOP, rootTask3, false /*includingParents*/); + return true; + }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any()); + + // Removing root tasks from the display while removing root tasks. + doAnswer(invocation -> { + taskDisplayArea.removeRootTask(rootTask2); + return true; + }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); + + runnable.run(); + verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any()); + verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any()); + verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); + verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any()); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 4e2697ab64f8..1bddd7b4ef64 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -17,8 +17,6 @@ package com.android.server.wm; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; @@ -124,9 +122,8 @@ public class DragDropControllerTests extends WindowTestsBase { */ private WindowState createDropTargetWindow(String name, int ownerId) { final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); - final Task stack = createTaskStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task task = createTaskInStack(stack, ownerId); + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, ownerId); task.addChild(activity, 0); // Use a new TestIWindow so we don't collect events for other windows diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index f97e79444d59..7d137bcb2cff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -488,9 +488,9 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { @Test public void testIsAnimatingByRecents() { final ActivityRecord homeActivity = createHomeActivity(); - final Task rootTask = createTaskStackOnDisplay(mDefaultDisplay); - final Task childTask = createTaskInStack(rootTask, 0 /* userId */); - final Task leafTask = createTaskInStack(childTask, 0 /* userId */); + final Task rootTask = createTask(mDefaultDisplay); + final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */); + final Task leafTask = createTaskInRootTask(childTask, 0 /* userId */); spyOn(leafTask); doReturn(true).when(leafTask).isVisible(); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java deleted file mode 100644 index 8388f2a6c6fa..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ /dev/null @@ -1,953 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.Display.TYPE_VIRTUAL; -import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE; -import static com.android.server.wm.Task.ActivityState.STOPPED; -import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.contains; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.refEq; - -import android.app.ActivityOptions; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.ResolveInfo; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.platform.test.annotations.Presubmit; -import android.util.MergedConfiguration; -import android.util.Pair; - -import androidx.test.filters.MediumTest; - -import com.android.internal.app.ResolverActivity; -import com.android.server.wm.Task.ActivityState; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; - -/** - * Tests for the {@link RootWindowContainer} class. - * - * Build/Install/Run: - * atest WmTests:RootActivityContainerTests - */ -@MediumTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class RootActivityContainerTests extends WindowTestsBase { - - @Before - public void setUp() throws Exception { - doNothing().when(mAtm).updateSleepIfNeededLocked(); - } - - /** - * This test ensures that we do not try to restore a task based off an invalid task id. We - * should expect {@code null} to be returned in this case. - */ - @Test - public void testRestoringInvalidTask() { - mRootWindowContainer.getDefaultDisplay().removeAllTasks(); - Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/, - MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); - assertNull(task); - } - - /** - * This test ensures that an existing task in the pinned stack is moved to the fullscreen - * activity stack when a new task is added. - */ - @Test - public void testReplacingTaskInPinnedStack() { - Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(fullscreenTask).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm) - .setTask(fullscreenTask).build(); - - fullscreenTask.moveToFront("testReplacingTaskInPinnedStack"); - - // Ensure full screen stack has both tasks. - ensureStackPlacement(fullscreenTask, firstActivity, secondActivity); - - // Move first activity to pinned stack. - mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove"); - - final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); - Task pinnedStack = taskDisplayArea.getRootPinnedTask(); - // Ensure a task has moved over. - ensureStackPlacement(pinnedStack, firstActivity); - ensureStackPlacement(fullscreenTask, secondActivity); - - // Move second activity to pinned stack. - mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove"); - - // Need to get stacks again as a new instance might have been created. - pinnedStack = taskDisplayArea.getRootPinnedTask(); - fullscreenTask = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD); - // Ensure stacks have swapped tasks. - ensureStackPlacement(pinnedStack, secondActivity); - ensureStackPlacement(fullscreenTask, firstActivity); - } - - @Test - public void testMovingBottomMostStackActivityToPinnedStack() { - final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(fullscreenTask).build(); - final Task task = firstActivity.getTask(); - - final ActivityRecord secondActivity = new ActivityBuilder(mAtm) - .setTask(fullscreenTask).build(); - - fullscreenTask.moveTaskToBack(task); - - // Ensure full screen stack has both tasks. - ensureStackPlacement(fullscreenTask, firstActivity, secondActivity); - assertEquals(task.getTopMostActivity(), secondActivity); - firstActivity.setState(STOPPED, "testMovingBottomMostStackActivityToPinnedStack"); - - - // Move first activity to pinned stack. - mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove"); - - assertTrue(firstActivity.mRequestForceTransition); - } - - private static void ensureStackPlacement(Task task, ActivityRecord... activities) { - final ArrayList<ActivityRecord> taskActivities = new ArrayList<>(); - - task.forAllActivities((Consumer<ActivityRecord>) taskActivities::add, false); - - assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + taskActivities, - taskActivities.size(), activities != null ? activities.length : 0); - - if (activities == null) { - return; - } - - for (ActivityRecord activity : activities) { - assertTrue(taskActivities.contains(activity)); - } - } - - @Test - public void testApplySleepTokens() { - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final Task stack = new TaskBuilder(mSupervisor) - .setDisplay(display) - .setOnTop(false) - .build(); - - // Make sure we wake and resume in the case the display is turning on and the keyguard is - // not showing. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - false /* keyguardShowing */, true /* expectWakeFromSleep */, - true /* expectResumeTopActivity */); - - // Make sure we wake and don't resume when the display is turning on and the keyguard is - // showing. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - true /* keyguardShowing */, true /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - - // Make sure we wake and don't resume when the display is turning on and the keyguard is - // not showing as unfocused. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, false /* isFocusedStack */, - false /* keyguardShowing */, true /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - - // Should not do anything if the display state hasn't changed. - verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - false /* keyguardShowing */, false /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - } - - private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard, - Task stack, boolean displaySleeping, boolean displayShouldSleep, - boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, - boolean expectResumeTopActivity) { - reset(stack); - - doReturn(displayShouldSleep).when(display).shouldSleep(); - doReturn(displaySleeping).when(display).isSleeping(); - doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); - - doReturn(isFocusedStack).when(stack).isFocusedRootTaskOnDisplay(); - doReturn(isFocusedStack ? stack : null).when(display).getFocusedRootTask(); - TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea(); - doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedRootTask(); - mRootWindowContainer.applySleepTokens(true); - verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); - verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( - null /* target */, null /* targetOptions */); - } - - @Test - public void testAwakeFromSleepingWithAppConfiguration() { - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); - activity.moveFocusableActivityToTop("test"); - assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay()); - ActivityRecordTests.setRotatedScreenOrientationSilently(activity); - - final Configuration rotatedConfig = new Configuration(); - display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation() - .rotationForOrientation(activity.getOrientation(), display.getRotation())); - assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation); - // Assume the activity was shown in different orientation. For example, the top activity is - // landscape and the portrait lockscreen is shown. - activity.setLastReportedConfiguration( - new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig)); - activity.setState(ActivityState.STOPPED, "sleep"); - - display.setIsSleeping(true); - doReturn(false).when(display).shouldSleep(); - // Allow to resume when awaking. - setBooted(mAtm); - mRootWindowContainer.applySleepTokens(true); - - // The display orientation should be changed by the activity so there is no relaunch. - verify(activity, never()).relaunchActivityLocked(anyBoolean()); - assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation); - } - - /** - * Verifies that removal of activity with task and stack is done correctly. - */ - @Test - public void testRemovingStackOnAppCrash() { - final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer - .getDefaultTaskDisplayArea(); - final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount(); - final Task stack = defaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build(); - - assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount()); - - // Let's pretend that the app has crashed. - firstActivity.app.setThread(null); - mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); - - // Verify that the stack was removed. - assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount()); - } - - /** - * Verifies that removal of activities with task and stack is done correctly when there are - * several task display areas. - */ - @Test - public void testRemovingStackOnAppCrash_multipleDisplayAreas() { - final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer - .getDefaultTaskDisplayArea(); - final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount(); - final Task stack = defaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build(); - assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount()); - - final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent(); - final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( - dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); - final Task secondStack = secondTaskDisplayArea.createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - new ActivityBuilder(mAtm).setTask(secondStack).setUseProcess(firstActivity.app).build(); - assertEquals(1, secondTaskDisplayArea.getRootTaskCount()); - - // Let's pretend that the app has crashed. - firstActivity.app.setThread(null); - mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); - - // Verify that the stacks were removed. - assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount()); - assertEquals(0, secondTaskDisplayArea.getRootTaskCount()); - } - - @Test - public void testFocusability() { - final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer - .getDefaultTaskDisplayArea(); - final Task stack = defaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); - - // Created stacks are focusable by default. - assertTrue(stack.isTopActivityFocusable()); - assertTrue(activity.isFocusable()); - - // If the stack is made unfocusable, its activities should inherit that. - stack.setFocusable(false); - assertFalse(stack.isTopActivityFocusable()); - assertFalse(activity.isFocusable()); - - final Task pinnedStack = defaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) - .setTask(pinnedStack).build(); - - // We should not be focusable when in pinned mode - assertFalse(pinnedStack.isTopActivityFocusable()); - assertFalse(pinnedActivity.isFocusable()); - - // Add flag forcing focusability. - pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; - - // Task with FLAG_ALWAYS_FOCUSABLE should be focusable. - assertTrue(pinnedStack.isTopActivityFocusable()); - assertTrue(pinnedActivity.isFocusable()); - } - - /** - * Verify that split-screen primary stack will be chosen if activity is launched that targets - * split-screen secondary, but a matching existing instance is found on top of split-screen - * primary stack. - */ - @Test - public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { - // Create primary split-screen stack with a task and an activity. - final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea() - .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryStack).build(); - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build(); - - // Find a launch stack for the top activity in split-screen primary, while requesting - // split-screen secondary. - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); - final Task result = - mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */); - - // Assert that the primary stack is returned. - assertEquals(primaryStack, result); - } - - /** - * Verify that home stack would be moved to front when the top activity is Recents. - */ - @Test - public void testFindTaskToMoveToFrontWhenRecentsOnTop() { - // Create stack/task on default display. - final Task targetStack = new TaskBuilder(mSupervisor) - .setCreateActivity(true) - .setOnTop(false) - .build(); - final Task targetTask = targetStack.getBottomMostTask(); - - // Create Recents on top of the display. - final Task stack = new TaskBuilder(mSupervisor) - .setCreateActivity(true) - .setActivityType(ACTIVITY_TYPE_RECENTS) - .build(); - - final String reason = "findTaskToMoveToFront"; - mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, - false); - - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason)); - } - - /** - * Verify that home stack won't be moved to front if the top activity on other display is - * Recents. - */ - @Test - public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { - // Create tasks on default display. - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build(); - - // Create Recents on secondary display. - final TestDisplayContent secondDisplay = addNewDisplayContentAt( - DisplayContent.POSITION_TOP); - final Task rootTask = secondDisplay.getDefaultTaskDisplayArea() - .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); - new ActivityBuilder(mAtm).setTask(rootTask).build(); - - final String reason = "findTaskToMoveToFront"; - mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, - false); - - verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason)); - } - - /** - * Verify if a stack is not at the topmost position, it should be able to resume its activity if - * the stack is the top focused. - */ - @Test - public void testResumeActivityWhenNonTopmostStackIsTopFocused() { - // Create a root task at bottom. - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); - taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/); - - // Assume the task is not at the topmost position (e.g. behind always-on-top stacks) but it - // is the current top focused task. - assertFalse(rootTask.isTopRootTaskInDisplayArea()); - doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); - - // Use the task as target to resume. - mRootWindowContainer.resumeFocusedTasksTopActivities( - rootTask, activity, null /* targetOptions */); - - // Verify the target task should resume its activity. - verify(rootTask, times(1)).resumeTopActivityUncheckedLocked( - eq(activity), eq(null /* targetOptions */)); - } - - /** - * Verify that home activity will be started on a display even if another display has a - * focusable activity. - */ - @Test - public void testResumeFocusedStacksStartsHomeActivity_NoActivities() { - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - taskDisplayArea.getRootHomeTask().removeIfPossible(); - taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); - - doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); - - mAtm.setBooted(true); - - // Trigger resume on all displays - mRootWindowContainer.resumeFocusedTasksTopActivities(); - - // Verify that home activity was started on the default display - verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); - } - - /** - * Verify that home activity will be started on a display even if another display has a - * focusable activity. - */ - @Test - public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() { - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - taskDisplayArea.getRootHomeTask().removeIfPossible(); - taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); - - // Create an activity on secondary display. - final TestDisplayContent secondDisplay = addNewDisplayContentAt( - DisplayContent.POSITION_TOP); - final Task rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - new ActivityBuilder(mAtm).setTask(rootTask).build(); - - doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); - - mAtm.setBooted(true); - - // Trigger resume on all displays - mRootWindowContainer.resumeFocusedTasksTopActivities(); - - // Verify that home activity was started on the default display - verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); - } - - /** - * Verify that a lingering transition is being executed in case the activity to be resumed is - * already resumed - */ - @Test - public void testResumeActivityLingeringTransition() { - // Create a root task at top. - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(rootTask).setOnTop(true).build(); - activity.setState(ActivityState.RESUMED, "test"); - - // Assume the task is at the topmost position - assertTrue(rootTask.isTopRootTaskInDisplayArea()); - - // Use the task as target to resume. - mRootWindowContainer.resumeFocusedTasksTopActivities(); - - // Verify the lingering app transition is being executed because it's already resumed - verify(rootTask, times(1)).executeAppTransition(any()); - } - - @Test - public void testResumeActivityLingeringTransition_notExecuted() { - // Create a root task at bottom. - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setTask(rootTask).setOnTop(true).build(); - activity.setState(ActivityState.RESUMED, "test"); - taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/); - - // Assume the task is at the topmost position - assertFalse(rootTask.isTopRootTaskInDisplayArea()); - doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); - - // Use the task as target to resume. - mRootWindowContainer.resumeFocusedTasksTopActivities(); - - // Verify the lingering app transition is being executed because it's already resumed - verify(rootTask, never()).executeAppTransition(any()); - } - - /** - * Tests that home activities can be started on the displays that supports system decorations. - */ - @Test - public void testStartHomeOnAllDisplays() { - mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); - mockResolveSecondaryHomeActivity(); - - // Create secondary displays. - final TestDisplayContent secondDisplay = - new TestDisplayContent.Builder(mAtm, 1000, 1500) - .setSystemDecorations(true).build(); - - doReturn(true).when(mRootWindowContainer) - .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); - doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), - anyBoolean()); - - mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome"); - - assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome()); - assertNotNull(secondDisplay.getTopRootTask()); - assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome()); - } - - /** - * Tests that home activities won't be started before booting when display added. - */ - @Test - public void testNotStartHomeBeforeBoot() { - final int displayId = 1; - final boolean isBooting = mAtm.mAmInternal.isBooting(); - final boolean isBooted = mAtm.mAmInternal.isBooted(); - try { - mAtm.mAmInternal.setBooting(false); - mAtm.mAmInternal.setBooted(false); - mRootWindowContainer.onDisplayAdded(displayId); - verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); - } finally { - mAtm.mAmInternal.setBooting(isBooting); - mAtm.mAmInternal.setBooted(isBooted); - } - } - - /** - * Tests whether home can be started if being instrumented. - */ - @Test - public void testCanStartHomeWhenInstrumented() { - final ActivityInfo info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - final WindowProcessController app = mock(WindowProcessController.class); - doReturn(app).when(mAtm).getProcessController(any(), anyInt()); - - // Can not start home if we don't want to start home while home is being instrumented. - doReturn(true).when(app).isInstrumenting(); - final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer - .getDefaultTaskDisplayArea(); - assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, - false /* allowInstrumenting*/)); - - // Can start home for other cases. - assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, - true /* allowInstrumenting*/)); - - doReturn(false).when(app).isInstrumenting(); - assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, - false /* allowInstrumenting*/)); - assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, - true /* allowInstrumenting*/)); - } - - /** - * Tests that secondary home activity should not be resolved if device is still locked. - */ - @Test - public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() { - // Create secondary displays. - final TestDisplayContent secondDisplay = - new TestDisplayContent.Builder(mAtm, 1000, 1500) - .setSystemDecorations(true).build(); - - // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false. - final int currentUser = mRootWindowContainer.mCurrentUser; - mRootWindowContainer.mCurrentUser = -1; - - mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", - secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); - - try { - verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any()); - } finally { - mRootWindowContainer.mCurrentUser = currentUser; - } - } - - /** - * Tests that secondary home activity should not be resolved if display does not support system - * decorations. - */ - @Test - public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() { - // Create secondary displays. - final TestDisplayContent secondDisplay = - new TestDisplayContent.Builder(mAtm, 1000, 1500) - .setSystemDecorations(false).build(); - - mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", - secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); - - verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any()); - } - - /** - * Tests that when starting {@link #ResolverActivity} for home, it should use the standard - * activity type (in a new stack) so the order of back stack won't be broken. - */ - @Test - public void testStartResolverActivityForHome() { - final ActivityInfo info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - info.applicationInfo.packageName = "android"; - info.name = ResolverActivity.class.getName(); - doReturn(info).when(mRootWindowContainer).resolveHomeActivity(anyInt(), any()); - - mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY); - final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity(); - - assertEquals(info, resolverActivity.info); - assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType()); - } - - /** - * Tests that secondary home should be selected if primary home not set. - */ - @Test - public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() { - // Setup: primary home not set. - final Intent primaryHomeIntent = mAtm.getHomeIntent(); - final ActivityInfo aInfoPrimary = new ActivityInfo(); - aInfoPrimary.name = ResolverActivity.class.getName(); - doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(primaryHomeIntent)); - // Setup: set secondary home. - mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); - - // Run the test. - final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer - .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); - final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); - assertEquals(aInfoSecondary.name, resolvedInfo.first.name); - assertEquals(aInfoSecondary.applicationInfo.packageName, - resolvedInfo.first.applicationInfo.packageName); - } - - /** - * Tests that the default secondary home activity is always picked when it is in forced by - * config_useSystemProvidedLauncherForSecondary. - */ - @Test - public void testResolveSecondaryHomeActivityForced() { - // SetUp: set primary home. - mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); - // SetUp: set secondary home and force it. - mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */); - final Intent secondaryHomeIntent = - mAtm.getSecondaryHomeIntent(null /* preferredPackage */); - final List<ResolveInfo> resolutions = new ArrayList<>(); - final ResolveInfo resolveInfo = new ResolveInfo(); - final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); - resolveInfo.activityInfo = aInfoSecondary; - resolutions.add(resolveInfo); - doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), - refEq(secondaryHomeIntent)); - doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), - anyBoolean()); - - // Run the test. - final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer - .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); - assertEquals(aInfoSecondary.name, resolvedInfo.first.name); - assertEquals(aInfoSecondary.applicationInfo.packageName, - resolvedInfo.first.applicationInfo.packageName); - } - - /** - * Tests that secondary home should be selected if primary home not support secondary displays - * or there is no matched activity in the same package as selected primary home. - */ - @Test - public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() { - // Setup: there is no matched activity in the same package as selected primary home. - mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); - final List<ResolveInfo> resolutions = new ArrayList<>(); - doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); - // Setup: set secondary home. - mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); - - // Run the test. - final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer - .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); - final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); - assertEquals(aInfoSecondary.name, resolvedInfo.first.name); - assertEquals(aInfoSecondary.applicationInfo.packageName, - resolvedInfo.first.applicationInfo.packageName); - } - /** - * Tests that primary home activity should be selected if it already support secondary displays. - */ - @Test - public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() { - // SetUp: set primary home. - mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); - // SetUp: put primary home info on 2nd item - final List<ResolveInfo> resolutions = new ArrayList<>(); - final ResolveInfo infoFake1 = new ResolveInfo(); - infoFake1.activityInfo = new ActivityInfo(); - infoFake1.activityInfo.name = "fakeActivity1"; - infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); - infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; - final ResolveInfo infoFake2 = new ResolveInfo(); - final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */); - infoFake2.activityInfo = aInfoPrimary; - resolutions.add(infoFake1); - resolutions.add(infoFake2); - doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); - - doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), - anyBoolean()); - - // Run the test. - final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer - .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); - assertEquals(aInfoPrimary.name, resolvedInfo.first.name); - assertEquals(aInfoPrimary.applicationInfo.packageName, - resolvedInfo.first.applicationInfo.packageName); - } - - /** - * Tests that the first one that matches should be selected if there are multiple activities. - */ - @Test - public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { - // SetUp: set primary home. - mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); - // Setup: prepare two eligible activity info. - final List<ResolveInfo> resolutions = new ArrayList<>(); - final ResolveInfo infoFake1 = new ResolveInfo(); - infoFake1.activityInfo = new ActivityInfo(); - infoFake1.activityInfo.name = "fakeActivity1"; - infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); - infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; - final ResolveInfo infoFake2 = new ResolveInfo(); - infoFake2.activityInfo = new ActivityInfo(); - infoFake2.activityInfo.name = "fakeActivity2"; - infoFake2.activityInfo.applicationInfo = new ApplicationInfo(); - infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2"; - resolutions.add(infoFake1); - resolutions.add(infoFake2); - doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); - - doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), - anyBoolean()); - - // Use the first one of matched activities in the same package as selected primary home. - final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer - .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); - - assertEquals(infoFake1.activityInfo.applicationInfo.packageName, - resolvedInfo.first.applicationInfo.packageName); - assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name); - } - - /** - * Test that {@link RootWindowContainer#getLaunchRootTask} with the real caller id will get the - * expected stack when requesting the activity launch on the secondary display. - */ - @Test - public void testGetLaunchStackWithRealCallerId() { - // Create a non-system owned virtual display. - final TestDisplayContent secondaryDisplay = - new TestDisplayContent.Builder(mAtm, 1000, 1500) - .setType(TYPE_VIRTUAL).setOwnerUid(100).build(); - - // Create an activity with specify the original launch pid / uid. - final ActivityRecord r = new ActivityBuilder(mAtm).setLaunchedFromPid(200) - .setLaunchedFromUid(200).build(); - - // Simulate ActivityStarter to find a launch stack for requesting the activity to launch - // on the secondary display with realCallerId. - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchDisplayId(secondaryDisplay.mDisplayId); - options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); - doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId, - 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info); - final Task result = mRootWindowContainer.getLaunchRootTask(r, options, - null /* task */, true /* onTop */, null, 300 /* test realCallerPid */, - 300 /* test realCallerUid */); - - // Assert that the stack is returned as expected. - assertNotNull(result); - assertEquals("The display ID of the stack should same as secondary display ", - secondaryDisplay.mDisplayId, result.getDisplayId()); - } - - @Test - public void testGetValidLaunchStackOnDisplayWithCandidateRootTask() { - // Create a root task with an activity on secondary display. - final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300, - 600).build(); - final Task task = new TaskBuilder(mSupervisor) - .setDisplay(secondaryDisplay).build(); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); - - // Make sure the root task is valid and can be reused on default display. - final Task stack = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea( - mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, - null /* options */, null /* launchParams */); - assertEquals(task, stack); - } - - @Test - public void testSwitchUser_missingHomeRootTask() { - final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - doReturn(fullscreenTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); - - final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - Task homeStack = taskDisplayArea.getRootHomeTask(); - if (homeStack != null) { - homeStack.removeImmediately(); - } - assertNull(taskDisplayArea.getRootHomeTask()); - - int currentUser = mRootWindowContainer.mCurrentUser; - int otherUser = currentUser + 1; - - mRootWindowContainer.switchUser(otherUser, null); - - assertNotNull(taskDisplayArea.getRootHomeTask()); - assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask()); - } - - /** - * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity - * info for test cases. - * - * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use - * secondary home intent. - * @param forceSystemProvided Indicate to force using system provided home activity. - */ - private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) { - ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome); - Intent targetIntent; - if (primaryHome) { - targetIntent = mAtm.getHomeIntent(); - } else { - Resources resources = mContext.getResources(); - spyOn(resources); - doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString( - com.android.internal.R.string.config_secondaryHomePackage); - doReturn(forceSystemProvided).when(resources).getBoolean( - com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); - targetIntent = mAtm.getSecondaryHomeIntent(null /* preferredPackage */); - } - doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(), - refEq(targetIntent)); - } - - /** - * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent - * activity info for test cases. - */ - private void mockResolveSecondaryHomeActivity() { - final Intent secondaryHomeIntent = mAtm - .getSecondaryHomeIntent(null /* preferredPackage */); - final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false); - doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) - .resolveSecondaryHomeActivity(anyInt(), any()); - } - - private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) { - final ActivityInfo aInfo = new ActivityInfo(); - aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity"; - aInfo.applicationInfo = new ApplicationInfo(); - aInfo.applicationInfo.packageName = - primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage"; - return aInfo; - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index 3a7954b3a5ce..748622b810d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.server.wm; @@ -27,12 +27,14 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; @@ -49,6 +51,8 @@ import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE; import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove; +import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; +import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -56,26 +60,30 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.app.IApplicationThread; +import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.pm.ActivityInfo; +import android.graphics.Rect; import android.os.Binder; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; -import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -84,15 +92,16 @@ import java.util.ArrayList; import java.util.function.Consumer; /** - * Tests for the {@link ActivityStack} class. + * Tests for the root {@link Task} behavior. * * Build/Install/Run: - * atest WmTests:ActivityStackTests + * atest WmTests:RootTaskTests */ @SmallTest @Presubmit @RunWith(WindowTestRunner.class) -public class ActivityStackTests extends WindowTestsBase { +public class RootTaskTests extends WindowTestsBase { + private TaskDisplayArea mDefaultTaskDisplayArea; @Before @@ -101,6 +110,172 @@ public class ActivityStackTests extends WindowTestsBase { } @Test + public void testRootTaskPositionChildAt() { + final Task rootTask = createTask(mDisplayContent); + final Task task1 = createTaskInRootTask(rootTask, 0 /* userId */); + final Task task2 = createTaskInRootTask(rootTask, 1 /* userId */); + + // Current user root task should be moved to top. + rootTask.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */); + assertEquals(rootTask.mChildren.get(0), task2); + assertEquals(rootTask.mChildren.get(1), task1); + + // Non-current user won't be moved to top. + rootTask.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */); + assertEquals(rootTask.mChildren.get(0), task2); + assertEquals(rootTask.mChildren.get(1), task1); + + // Non-leaf task should be moved to top regardless of the user id. + createTaskInRootTask(task2, 0 /* userId */); + createTaskInRootTask(task2, 1 /* userId */); + rootTask.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */); + assertEquals(rootTask.mChildren.get(0), task1); + assertEquals(rootTask.mChildren.get(1), task2); + } + + @Test + public void testClosingAppDifferentTaskOrientation() { + final ActivityRecord activity1 = createActivityRecord(mDisplayContent); + activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + final ActivityRecord activity2 = createActivityRecord(mDisplayContent); + activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); + + final WindowContainer parent = activity1.getTask().getParent(); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation()); + mDisplayContent.mClosingApps.add(activity2); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation()); + } + + @Test + public void testMoveTaskToBackDifferentTaskOrientation() { + final ActivityRecord activity1 = createActivityRecord(mDisplayContent); + activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + final ActivityRecord activity2 = createActivityRecord(mDisplayContent); + activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); + + final WindowContainer parent = activity1.getTask().getParent(); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation()); + } + + @Test + public void testRootTaskRemoveImmediately() { + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); + assertEquals(rootTask, task.getRootTask()); + + // Remove root task and check if its child is also removed. + rootTask.removeImmediately(); + assertNull(rootTask.getDisplayContent()); + assertNull(task.getParent()); + } + + @Test + public void testRemoveContainer() { + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); + + assertNotNull(rootTask); + assertNotNull(task); + rootTask.removeIfPossible(); + // Assert that the container was removed. + assertNull(rootTask.getParent()); + assertEquals(0, rootTask.getChildCount()); + assertNull(rootTask.getDisplayContent()); + assertNull(task.getDisplayContent()); + assertNull(task.getParent()); + } + + @Test + public void testRemoveContainer_deferRemoval() { + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); + + // Root task removal is deferred if one of its child is animating. + doReturn(true).when(rootTask).hasWindowsAlive(); + doReturn(rootTask).when(task).getAnimatingContainer( + eq(TRANSITION | CHILDREN), anyInt()); + + rootTask.removeIfPossible(); + // For the case of deferred removal the task controller will still be connected to its + // container until the root task window container is removed. + assertNotNull(rootTask.getParent()); + assertNotEquals(0, rootTask.getChildCount()); + assertNotNull(task); + + rootTask.removeImmediately(); + // After removing, the task will be isolated. + assertNull(task.getParent()); + assertEquals(0, task.getChildCount()); + } + + @Test + public void testReparent() { + // Create first root task on primary display. + final Task rootTask1 = createTask(mDisplayContent); + final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */); + + // Create second display and put second root task on it. + final DisplayContent dc = createNewDisplay(); + final Task rootTask2 = createTask(dc); + + // Reparent + clearInvocations(task1); // reset the number of onDisplayChanged for task. + rootTask1.reparent(dc.getDefaultTaskDisplayArea(), true /* onTop */); + assertEquals(dc, rootTask1.getDisplayContent()); + final int stack1PositionInParent = rootTask1.getParent().mChildren.indexOf(rootTask1); + final int stack2PositionInParent = rootTask1.getParent().mChildren.indexOf(rootTask2); + assertEquals(stack1PositionInParent, stack2PositionInParent + 1); + verify(task1, times(1)).onDisplayChanged(any()); + } + + @Test + public void testTaskOutset() { + final Task task = createTask(mDisplayContent); + final int taskOutset = 10; + spyOn(task); + doReturn(taskOutset).when(task).getTaskOutset(); + doReturn(true).when(task).inMultiWindowMode(); + + // Mock the resolved override windowing mode to non-fullscreen + final WindowConfiguration windowConfiguration = + task.getResolvedOverrideConfiguration().windowConfiguration; + spyOn(windowConfiguration); + doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) + .when(windowConfiguration).getWindowingMode(); + + // Prevent adjust task dimensions + doNothing().when(task).adjustForMinimalTaskDimensions(any(), any(), any()); + + final Rect taskBounds = new Rect(200, 200, 800, 1000); + // Update surface position and size by the given bounds. + task.setBounds(taskBounds); + + assertEquals(taskBounds.width() + 2 * taskOutset, task.getLastSurfaceSize().x); + assertEquals(taskBounds.height() + 2 * taskOutset, task.getLastSurfaceSize().y); + assertEquals(taskBounds.left - taskOutset, task.getLastSurfacePosition().x); + assertEquals(taskBounds.top - taskOutset, task.getLastSurfacePosition().y); + } + + @Test + public void testActivityAndTaskGetsProperType() { + final Task task1 = new TaskBuilder(mSupervisor).build(); + ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent); + + // First activity should become standard + task1.addChild(activity1, 0); + assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity1.getActivityType()); + assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType()); + + // Second activity should also become standard + ActivityRecord activity2 = createNonAttachedActivityRecord(mDisplayContent); + task1.addChild(activity2, WindowContainer.POSITION_TOP); + assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity2.getActivityType()); + assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType()); + } + + @Test public void testResumedActivity() { final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); final Task task = r.getTask(); @@ -113,40 +288,40 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testResumedActivityFromTaskReparenting() { - final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); final ActivityRecord r = new ActivityBuilder(mAtm) - .setCreateTask(true).setParentTask(parentTask).build(); + .setCreateTask(true).setParentTask(rootTask).build(); final Task task = r.getTask(); - // Ensure moving task between two stacks updates resumed activity + // Ensure moving task between two root tasks updates resumed activity r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); - assertEquals(r, parentTask.getResumedActivity()); + assertEquals(r, rootTask.getResumedActivity()); - final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build(); - task.reparent(destStack, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT, + final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT, false /* animate */, true /* deferResume*/, "testResumedActivityFromTaskReparenting"); - assertNull(parentTask.getResumedActivity()); - assertEquals(r, destStack.getResumedActivity()); + assertNull(rootTask.getResumedActivity()); + assertEquals(r, destRootTask.getResumedActivity()); } @Test public void testResumedActivityFromActivityReparenting() { - final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); final ActivityRecord r = new ActivityBuilder(mAtm) - .setCreateTask(true).setParentTask(parentTask).build(); + .setCreateTask(true).setParentTask(rootTask).build(); final Task task = r.getTask(); - // Ensure moving task between two stacks updates resumed activity + // Ensure moving task between two root tasks updates resumed activity r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); - assertEquals(r, parentTask.getResumedActivity()); + assertEquals(r, rootTask.getResumedActivity()); - final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build(); - task.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, + final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, false /* animate */, false /* deferResume*/, "testResumedActivityFromActivityReparenting"); - assertNull(parentTask.getResumedActivity()); - assertEquals(r, destStack.getResumedActivity()); + assertNull(rootTask.getResumedActivity()); + assertEquals(r, destRootTask.getResumedActivity()); } @Test @@ -155,7 +330,7 @@ public class ActivityStackTests extends WindowTestsBase { // We're testing an edge case here where we have primary + fullscreen rather than secondary. organizer.setMoveToSecondaryOnEnter(false); - // Create primary splitscreen stack. + // Create primary splitscreen root task. final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor) .setParentTask(organizer.mPrimary) .setOnTop(true) @@ -168,7 +343,7 @@ public class ActivityStackTests extends WindowTestsBase { primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack", null /* task */); - // Assert that stack is at the bottom. + // Assert that root task is at the bottom. assertEquals(0, getTaskIndexOf(mDefaultTaskDisplayArea, primarySplitScreen)); // Ensure no longer in splitscreen. @@ -182,7 +357,7 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveToPrimarySplitScreenThenMoveToBack() { TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); - // This time, start with a fullscreen activitystack + // This time, start with a fullscreen activity root task. final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -196,7 +371,7 @@ public class ActivityStackTests extends WindowTestsBase { primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack", null /* task */); - // Assert that stack is at the bottom. + // Assert that root task is at the bottom. assertEquals(primarySplitScreen, organizer.mSecondary.getChildAt(0)); // Ensure that the override mode is restored to what it was (fullscreen) @@ -208,7 +383,7 @@ public class ActivityStackTests extends WindowTestsBase { public void testSplitScreenMoveToBack() { TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); // Explicitly reparent task to primary split root to enter split mode, in which implies - // primary on top and secondary containing the home task below another stack. + // primary on top and secondary containing the home task below another root task. final Task primaryTask = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask( @@ -244,24 +419,24 @@ public class ActivityStackTests extends WindowTestsBase { } @Test - public void testRemoveOrganizedTask_UpdateStackReference() { + public void testRemoveOrganizedTask_UpdateRootTaskReference() { final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask(); final ActivityRecord homeActivity = new ActivityBuilder(mAtm) .setTask(rootHomeTask) .build(); - final Task secondaryStack = mAtm.mTaskOrganizerController.createRootTask( + final Task secondaryRootTask = mAtm.mTaskOrganizerController.createRootTask( rootHomeTask.getDisplayContent(), WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); - rootHomeTask.reparent(secondaryStack, POSITION_TOP); - assertEquals(secondaryStack, rootHomeTask.getParent()); + rootHomeTask.reparent(secondaryRootTask, POSITION_TOP); + assertEquals(secondaryRootTask, rootHomeTask.getParent()); - // This should call to {@link TaskDisplayArea#removeStackReferenceIfNeeded}. + // This should call to {@link TaskDisplayArea#removeRootTaskReferenceIfNeeded}. homeActivity.removeImmediately(); assertNull(mDefaultTaskDisplayArea.getRootHomeTask()); } @Test - public void testStackInheritsDisplayWindowingMode() { + public void testRootTaskInheritsDisplayWindowingMode() { final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -276,7 +451,7 @@ public class ActivityStackTests extends WindowTestsBase { } @Test - public void testStackOverridesDisplayWindowingMode() { + public void testRootTaskOverridesDisplayWindowingMode() { final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -358,95 +533,92 @@ public class ActivityStackTests extends WindowTestsBase { } @Test - public void testMoveStackToBackIncludingParent() { + public void testMoveRootTaskToBackIncludingParent() { final TaskDisplayArea taskDisplayArea = addNewDisplayContentAt(DisplayContent.POSITION_TOP) .getDefaultTaskDisplayArea(); - final Task stack1 = createStackForShouldBeVisibleTest(taskDisplayArea, + final Task rootTask1 = createTaskForShouldBeVisibleTest(taskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */, true /* twoLevelTask */); - final Task stack2 = createStackForShouldBeVisibleTest(taskDisplayArea, + final Task rootTask2 = createTaskForShouldBeVisibleTest(taskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */, true /* twoLevelTask */); - // Do not move display to back because there is still another stack. - stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask()); - verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */); + // Do not move display to back because there is still another root task. + rootTask2.moveToBack("testMoveRootTaskToBackIncludingParent", rootTask2.getTopMostTask()); + verify(rootTask2).positionChildAtBottom(any(), eq(false) /* includingParents */); - // Also move display to back because there is only one stack left. - taskDisplayArea.removeRootTask(stack1); - stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask()); - verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */); + // Also move display to back because there is only one root task left. + taskDisplayArea.removeRootTask(rootTask1); + rootTask2.moveToBack("testMoveRootTaskToBackIncludingParent", rootTask2.getTopMostTask()); + verify(rootTask2).positionChildAtBottom(any(), eq(true) /* includingParents */); } @Test public void testShouldBeVisible_Fullscreen() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - // Add an activity to the pinned stack so it isn't considered empty for visibility check. + // Add an activity to the pinned root task so it isn't considered empty for visibility + // check. final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) - .setTask(pinnedStack) + .setTask(pinnedRootTask) .build(); - assertTrue(homeStack.shouldBeVisible(null /* starting */)); - assertTrue(pinnedStack.shouldBeVisible(null /* starting */)); - - final Task fullscreenStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack - // should be visible since it is always on-top. - doReturn(false).when(fullscreenStack).isTranslucent(any()); - assertFalse(homeStack.shouldBeVisible(null /* starting */)); - assertTrue(pinnedStack.shouldBeVisible(null /* starting */)); - assertTrue(fullscreenStack.shouldBeVisible(null /* starting */)); + assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); + assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */)); - // Home stack should be visible behind a translucent fullscreen stack. - doReturn(true).when(fullscreenStack).isTranslucent(any()); - assertTrue(homeStack.shouldBeVisible(null /* starting */)); - assertTrue(pinnedStack.shouldBeVisible(null /* starting */)); + final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + // Home root task shouldn't be visible behind an opaque fullscreen root task, but pinned + // root task should be visible since it is always on-top. + doReturn(false).when(fullscreenRootTask).isTranslucent(any()); + assertFalse(homeRootTask.shouldBeVisible(null /* starting */)); + assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */)); + assertTrue(fullscreenRootTask.shouldBeVisible(null /* starting */)); + + // Home root task should be visible behind a translucent fullscreen root task. + doReturn(true).when(fullscreenRootTask).isTranslucent(any()); + assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); + assertTrue(pinnedRootTask.shouldBeVisible(null /* starting */)); } @Test public void testShouldBeVisible_SplitScreen() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - // Home stack should always be fullscreen for this test. - doReturn(false).when(homeStack).supportsSplitScreenWindowingMode(); - final Task splitScreenPrimary = - createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + // Home root task should always be fullscreen for this test. + doReturn(false).when(homeRootTask).supportsSplitScreenWindowingMode(); + final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task splitScreenSecondary = - createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - // Home stack shouldn't be visible if both halves of split-screen are opaque. + // Home root task shouldn't be visible if both halves of split-screen are opaque. doReturn(false).when(splitScreenPrimary).isTranslucent(any()); doReturn(false).when(splitScreenSecondary).isTranslucent(any()); - assertFalse(homeStack.shouldBeVisible(null /* starting */)); + assertFalse(homeRootTask.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, splitScreenSecondary.getVisibility(null /* starting */)); - // Home stack should be visible if one of the halves of split-screen is translucent. + // Home root task should be visible if one of the halves of split-screen is translucent. doReturn(true).when(splitScreenPrimary).isTranslucent(any()); - assertTrue(homeStack.shouldBeVisible(null /* starting */)); + assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - homeStack.getVisibility(null /* starting */)); + homeRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, splitScreenSecondary.getVisibility(null /* starting */)); - final Task splitScreenSecondary2 = - createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task splitScreenSecondary2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); // First split-screen secondary shouldn't be visible behind another opaque split-split // secondary. @@ -468,18 +640,17 @@ public class ActivityStackTests extends WindowTestsBase { assertEquals(TASK_VISIBILITY_VISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); - final Task assistantStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, - true /* onTop */); + final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); - // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack. - doReturn(false).when(assistantStack).isTranslucent(any()); - assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + // Split-screen root tasks shouldn't be visible behind an opaque fullscreen root task. + doReturn(false).when(assistantRootTask).isTranslucent(any()); + assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, - assistantStack.getVisibility(null /* starting */)); + assistantRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_INVISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_INVISIBLE, @@ -487,14 +658,14 @@ public class ActivityStackTests extends WindowTestsBase { assertEquals(TASK_VISIBILITY_INVISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); - // Split-screen stacks should be visible behind a translucent fullscreen stack. - doReturn(true).when(assistantStack).isTranslucent(any()); - assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + // Split-screen root tasks should be visible behind a translucent fullscreen root task. + doReturn(true).when(assistantRootTask).isTranslucent(any()); + assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, - assistantStack.getVisibility(null /* starting */)); + assistantRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitScreenPrimary.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, @@ -502,20 +673,20 @@ public class ActivityStackTests extends WindowTestsBase { assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitScreenSecondary2.getVisibility(null /* starting */)); - // Assistant stack shouldn't be visible behind translucent split-screen stack, + // Assistant root task shouldn't be visible behind translucent split-screen root task, // unless it is configured to show on top of everything. - doReturn(false).when(assistantStack).isTranslucent(any()); + doReturn(false).when(assistantRootTask).isTranslucent(any()); doReturn(true).when(splitScreenPrimary).isTranslucent(any()); doReturn(true).when(splitScreenSecondary2).isTranslucent(any()); splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen"); splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen"); if (isAssistantOnTop()) { - assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, - assistantStack.getVisibility(null /* starting */)); + assistantRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_INVISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_INVISIBLE, @@ -523,11 +694,11 @@ public class ActivityStackTests extends WindowTestsBase { assertEquals(TASK_VISIBILITY_INVISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); } else { - assertFalse(assistantStack.shouldBeVisible(null /* starting */)); + assertFalse(assistantRootTask.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); assertEquals(TASK_VISIBILITY_INVISIBLE, - assistantStack.getVisibility(null /* starting */)); + assistantRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_INVISIBLE, @@ -539,35 +710,33 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testGetVisibility_MultiLevel() { - final Task homeStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, - true /* onTop */); - final Task splitPrimary = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, - ACTIVITY_TYPE_UNDEFINED, true /* onTop */); - final Task splitSecondary = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, - ACTIVITY_TYPE_UNDEFINED, true /* onTop */); - - doReturn(false).when(homeStack).isTranslucent(any()); + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */); + final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */); + final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */); + + doReturn(false).when(homeRootTask).isTranslucent(any()); doReturn(false).when(splitPrimary).isTranslucent(any()); doReturn(false).when(splitSecondary).isTranslucent(any()); // Re-parent home to split secondary. - homeStack.reparent(splitSecondary, POSITION_TOP); + homeRootTask.reparent(splitSecondary, POSITION_TOP); // Current tasks should be visible. assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */)); // Home task should still be visible even though it is a child of another visible task. - assertEquals(TASK_VISIBILITY_VISIBLE, homeStack.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */)); // Add fullscreen translucent task that partially occludes split tasks - final Task translucentStack = createStandardStackForVisibilityTest( + final Task translucentRootTask = createStandardRootTaskForVisibilityTest( WINDOWING_MODE_FULLSCREEN, true /* translucent */); // Fullscreen translucent task should be visible - assertEquals(TASK_VISIBILITY_VISIBLE, translucentStack.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_VISIBLE, + translucentRootTask.getVisibility(null /* starting */)); // Split tasks should be visible behind translucent assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitPrimary.getVisibility(null /* starting */)); @@ -576,415 +745,400 @@ public class ActivityStackTests extends WindowTestsBase { // Home task should be visible behind translucent since its parent is visible behind // translucent. assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - homeStack.getVisibility(null /* starting */)); + homeRootTask.getVisibility(null /* starting */)); // Hide split-secondary splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */); // Home split secondary and home task should be invisible. assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, homeStack.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */)); } @Test public void testGetVisibility_FullscreenBehindTranslucent() { - final Task bottomStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task bottomRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - final Task translucentStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task translucentRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - bottomStack.getVisibility(null /* starting */)); + bottomRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, - translucentStack.getVisibility(null /* starting */)); + translucentRootTask.getVisibility(null /* starting */)); } @Test public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() { - final Task bottomStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task bottomRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - final Task translucentStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task translucentRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - final Task opaqueStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task opaqueRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_INVISIBLE, - translucentStack.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */)); + translucentRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */)); } @Test public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() { - final Task bottomStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task bottomRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - final Task opaqueStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task opaqueRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - final Task translucentStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task translucentRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - assertEquals(TASK_VISIBILITY_INVISIBLE, bottomStack.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - opaqueStack.getVisibility(null /* starting */)); + opaqueRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, - translucentStack.getVisibility(null /* starting */)); + translucentRootTask.getVisibility(null /* starting */)); } @Test public void testGetVisibility_FullscreenTranslucentBehindTranslucent() { - final Task bottomTranslucentStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task bottomTranslucentRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - final Task translucentStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task translucentRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - bottomTranslucentStack.getVisibility(null /* starting */)); + bottomTranslucentRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, - translucentStack.getVisibility(null /* starting */)); + translucentRootTask.getVisibility(null /* starting */)); } @Test public void testGetVisibility_FullscreenTranslucentBehindOpaque() { - final Task bottomTranslucentStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task bottomTranslucentRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - final Task opaqueStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task opaqueRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); assertEquals(TASK_VISIBILITY_INVISIBLE, - bottomTranslucentStack.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, opaqueStack.getVisibility(null /* starting */)); + bottomTranslucentRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */)); } @Test public void testGetVisibility_FullscreenBehindTranslucentAndPip() { - final Task bottomStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task bottomRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - final Task translucentStack = - createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, + final Task translucentRootTask = + createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - bottomStack.getVisibility(null /* starting */)); + bottomRootTask.getVisibility(null /* starting */)); assertEquals(TASK_VISIBILITY_VISIBLE, - translucentStack.getVisibility(null /* starting */)); - // Add an activity to the pinned stack so it isn't considered empty for visibility check. + translucentRootTask.getVisibility(null /* starting */)); + // Add an activity to the pinned root task so it isn't considered empty for visibility + // check. final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) - .setTask(pinnedStack) + .setTask(pinnedRootTask) .build(); - assertEquals(TASK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */)); + assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */)); } @Test public void testShouldBeVisible_Finishing() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity(); + ActivityRecord topRunningHomeActivity = homeRootTask.topRunningActivity(); if (topRunningHomeActivity == null) { topRunningHomeActivity = new ActivityBuilder(mAtm) - .setTask(homeStack) + .setTask(homeRootTask) .build(); } - final Task translucentStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - doReturn(true).when(translucentStack).isTranslucent(any()); + final Task translucentRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + doReturn(true).when(translucentRootTask).isTranslucent(any()); - assertTrue(homeStack.shouldBeVisible(null /* starting */)); - assertTrue(translucentStack.shouldBeVisible(null /* starting */)); + assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); + assertTrue(translucentRootTask.shouldBeVisible(null /* starting */)); topRunningHomeActivity.finishing = true; final ActivityRecord topRunningTranslucentActivity = - translucentStack.topRunningActivity(); + translucentRootTask.topRunningActivity(); topRunningTranslucentActivity.finishing = true; - // Home stack should be visible even there are no running activities. - assertTrue(homeStack.shouldBeVisible(null /* starting */)); + // Home root task should be visible even there are no running activities. + assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); // Home should be visible if we are starting an activity within it. - assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */)); - // The translucent stack shouldn't be visible since its activity marked as finishing. - assertFalse(translucentStack.shouldBeVisible(null /* starting */)); + assertTrue(homeRootTask.shouldBeVisible(topRunningHomeActivity /* starting */)); + // The translucent root task shouldn't be visible since its activity marked as finishing. + assertFalse(translucentRootTask.shouldBeVisible(null /* starting */)); } @Test - public void testShouldBeVisible_FullscreenBehindTranslucentInHomeStack() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testShouldBeVisible_FullscreenBehindTranslucentInHomeRootTask() { + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setParentTask(homeStack) - .setCreateTask(true) - .build(); + .setParentTask(homeRootTask) + .setCreateTask(true) + .build(); final Task task = firstActivity.getTask(); final ActivityRecord secondActivity = new ActivityBuilder(mAtm) .setTask(task) .build(); doReturn(false).when(secondActivity).occludesParent(); - homeStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + homeRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, false /* preserveWindows */); assertTrue(firstActivity.shouldBeVisible()); } @Test - public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindFullscreen() { + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final Task fullscreenStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); + final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - doReturn(false).when(homeStack).isTranslucent(any()); - doReturn(false).when(fullscreenStack).isTranslucent(any()); + doReturn(false).when(homeRootTask).isTranslucent(any()); + doReturn(false).when(fullscreenRootTask).isTranslucent(any()); - // Ensure that we don't move the home stack if it is already behind the top fullscreen stack - int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack); - assertEquals(fullscreenStack, getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); - assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack)); + // Ensure that we don't move the home root task if it is already behind the top fullscreen + // root task. + int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask); + assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask); + assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask)); } @Test - public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindTranslucent() { + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final Task fullscreenStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, + final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - doReturn(false).when(homeStack).isTranslucent(any()); - doReturn(true).when(fullscreenStack).isTranslucent(any()); + doReturn(false).when(homeRootTask).isTranslucent(any()); + doReturn(true).when(fullscreenRootTask).isTranslucent(any()); - // Ensure that we don't move the home stack if it is already behind the top fullscreen stack - int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack); - assertEquals(fullscreenStack, getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); - assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack)); + // Ensure that we don't move the home root task if it is already behind the top fullscreen + // root task. + int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask); + assertEquals(fullscreenRootTask, getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask); + assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask)); } @Test - public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() { - final Task fullscreenStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeOnTop() { + final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - doReturn(false).when(homeStack).isTranslucent(any()); - doReturn(false).when(fullscreenStack).isTranslucent(any()); + doReturn(false).when(homeRootTask).isTranslucent(any()); + doReturn(false).when(fullscreenRootTask).isTranslucent(any()); - // Ensure we don't move the home stack if it is already on top - int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack); - assertNull(getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); - assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack)); + // Ensure we don't move the home root task if it is already on top + int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask); + assertNull(getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask); + assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask)); } @Test - public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreen() { + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - doReturn(false).when(homeStack).isTranslucent(any()); - doReturn(false).when(fullscreenStack1).isTranslucent(any()); - doReturn(false).when(fullscreenStack2).isTranslucent(any()); + doReturn(false).when(homeRootTask).isTranslucent(any()); + doReturn(false).when(fullscreenRootTask1).isTranslucent(any()); + doReturn(false).when(fullscreenRootTask2).isTranslucent(any()); - // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the - // pinned stack - assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); - assertEquals(fullscreenStack2, getRootTaskAbove(homeStack)); + // Ensure that we move the home root task behind the bottom most fullscreen root task, + // ignoring the pinned root task. + assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask); + assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask)); } @Test public void - testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreenAndTranslucent() { + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); + final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - doReturn(false).when(homeStack).isTranslucent(any()); - doReturn(false).when(fullscreenStack1).isTranslucent(any()); - doReturn(true).when(fullscreenStack2).isTranslucent(any()); + doReturn(false).when(homeRootTask).isTranslucent(any()); + doReturn(false).when(fullscreenRootTask1).isTranslucent(any()); + doReturn(true).when(fullscreenRootTask2).isTranslucent(any()); - // Ensure that we move the home stack behind the bottom most non-translucent fullscreen - // stack - assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); - assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); + // Ensure that we move the home root task behind the bottom most non-translucent fullscreen + // root task. + assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeRootTask); + assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask)); } @Test - public void testMoveHomeStackBehindStack_BehindHomeStack() { - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testMoveHomeRootTaskBehindRootTask_BehindHomeRootTask() { + final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - doReturn(false).when(homeStack).isTranslucent(any()); - doReturn(false).when(fullscreenStack1).isTranslucent(any()); - doReturn(false).when(fullscreenStack2).isTranslucent(any()); + doReturn(false).when(homeRootTask).isTranslucent(any()); + doReturn(false).when(fullscreenRootTask1).isTranslucent(any()); + doReturn(false).when(fullscreenRootTask2).isTranslucent(any()); - // Ensure we don't move the home stack behind itself - int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack); - mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, homeStack); - assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack)); + // Ensure we don't move the home root task behind itself + int homeRootTaskIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, homeRootTask); + assertEquals(homeRootTaskIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeRootTask)); } @Test - public void testMoveHomeStackBehindStack() { - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task fullscreenStack3 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task fullscreenStack4 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testMoveHomeRootTaskBehindRootTask() { + final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task fullscreenRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task fullscreenRootTask3 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task fullscreenRootTask4 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack1); - assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2); - assertEquals(fullscreenStack2, getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack4); - assertEquals(fullscreenStack4, getRootTaskAbove(homeStack)); - mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2); - assertEquals(fullscreenStack2, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask1); + assertEquals(fullscreenRootTask1, getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask2); + assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask4); + assertEquals(fullscreenRootTask4, getRootTaskAbove(homeRootTask)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeRootTask, fullscreenRootTask2); + assertEquals(fullscreenRootTask2, getRootTaskAbove(homeRootTask)); } @Test public void testSetAlwaysOnTop() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - assertEquals(pinnedStack, getRootTaskAbove(homeStack)); + assertEquals(pinnedRootTask, getRootTaskAbove(homeRootTask)); - final Task alwaysOnTopStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - alwaysOnTopStack.setAlwaysOnTop(true); - assertTrue(alwaysOnTopStack.isAlwaysOnTop()); - // Ensure (non-pinned) always on top stack is put below pinned stack. - assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack)); + final Task alwaysOnTopRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); + alwaysOnTopRootTask.setAlwaysOnTop(true); + assertTrue(alwaysOnTopRootTask.isAlwaysOnTop()); + // Ensure (non-pinned) always on top root task is put below pinned root task. + assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask)); - final Task nonAlwaysOnTopStack = createStackForShouldBeVisibleTest( + final Task nonAlwaysOnTopRootTask = createTaskForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - // Ensure non always on top stack is put below always on top stacks. - assertEquals(alwaysOnTopStack, getRootTaskAbove(nonAlwaysOnTopStack)); - - final Task alwaysOnTopStack2 = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - alwaysOnTopStack2.setAlwaysOnTop(true); - assertTrue(alwaysOnTopStack2.isAlwaysOnTop()); - // Ensure newly created always on top stack is placed above other all always on top stacks. - assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2)); - - alwaysOnTopStack2.setAlwaysOnTop(false); - // Ensure, when always on top is turned off for a stack, the stack is put just below all - // other always on top stacks. - assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2)); - alwaysOnTopStack2.setAlwaysOnTop(true); + // Ensure non always on top root task is put below always on top root tasks. + assertEquals(alwaysOnTopRootTask, getRootTaskAbove(nonAlwaysOnTopRootTask)); + + final Task alwaysOnTopRootTask2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); + alwaysOnTopRootTask2.setAlwaysOnTop(true); + assertTrue(alwaysOnTopRootTask2.isAlwaysOnTop()); + // Ensure newly created always on top root task is placed above other all always on top + // root tasks. + assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask2)); + + alwaysOnTopRootTask2.setAlwaysOnTop(false); + // Ensure, when always on top is turned off for a root task, the root task is put just below + // all other always on top root tasks. + assertEquals(alwaysOnTopRootTask, getRootTaskAbove(alwaysOnTopRootTask2)); + alwaysOnTopRootTask2.setAlwaysOnTop(true); // Ensure always on top state changes properly when windowing mode changes. - alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertFalse(alwaysOnTopStack2.isAlwaysOnTop()); - assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2)); - alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertTrue(alwaysOnTopStack2.isAlwaysOnTop()); - assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2)); + alwaysOnTopRootTask2.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertFalse(alwaysOnTopRootTask2.isAlwaysOnTop()); + assertEquals(alwaysOnTopRootTask, getRootTaskAbove(alwaysOnTopRootTask2)); + alwaysOnTopRootTask2.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertTrue(alwaysOnTopRootTask2.isAlwaysOnTop()); + assertEquals(pinnedRootTask, getRootTaskAbove(alwaysOnTopRootTask2)); } @Test public void testSplitScreenMoveToFront() { - final Task splitScreenPrimary = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task splitScreenSecondary = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task assistantStack = createStackForShouldBeVisibleTest( - mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, - true /* onTop */); + final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); doReturn(false).when(splitScreenPrimary).isTranslucent(any()); doReturn(false).when(splitScreenSecondary).isTranslucent(any()); - doReturn(false).when(assistantStack).isTranslucent(any()); + doReturn(false).when(assistantRootTask).isTranslucent(any()); assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); - assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); splitScreenSecondary.moveToFront("testSplitScreenMoveToFront"); if (isAssistantOnTop()) { assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); - assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); } else { assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); - assertFalse(assistantStack.shouldBeVisible(null /* starting */)); + assertFalse(assistantRootTask.shouldBeVisible(null /* starting */)); } } - private Task createStandardStackForVisibilityTest(int windowingMode, + private Task createStandardRootTaskForVisibilityTest(int windowingMode, boolean translucent) { - final Task stack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + final Task rootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); - doReturn(translucent).when(stack).isTranslucent(any()); - return stack; + doReturn(translucent).when(rootTask).isTranslucent(any()); + return rootTask; } - private Task createStackForShouldBeVisibleTest( + private Task createTaskForShouldBeVisibleTest( TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) { - return createStackForShouldBeVisibleTest(taskDisplayArea, + return createTaskForShouldBeVisibleTest(taskDisplayArea, windowingMode, activityType, onTop, false /* twoLevelTask */); } @SuppressWarnings("TypeParameterUnusedInFormals") - private Task createStackForShouldBeVisibleTest(TaskDisplayArea taskDisplayArea, + private Task createTaskForShouldBeVisibleTest(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) { final Task task; if (activityType == ACTIVITY_TYPE_HOME) { @@ -1157,20 +1311,20 @@ public class ActivityStackTests extends WindowTestsBase { } @Test - public void testWontFinishHomeStackImmediately() { - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, + public void testWontFinishHomeRootTaskImmediately() { + final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - ActivityRecord activity = homeStack.topRunningActivity(); + ActivityRecord activity = homeRootTask.topRunningActivity(); if (activity == null) { activity = new ActivityBuilder(mAtm) - .setParentTask(homeStack) + .setParentTask(homeRootTask) .setCreateTask(true) .build(); } - // Home stack should not be destroyed immediately. - final ActivityRecord activity1 = finishTopActivity(homeStack); + // Home root task should not be destroyed immediately. + final ActivityRecord activity1 = finishTopActivity(homeRootTask); assertEquals(FINISHING, activity1.getState()); } @@ -1178,30 +1332,28 @@ public class ActivityStackTests extends WindowTestsBase { public void testFinishCurrentActivity() { // Create 2 activities on a new display. final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP); - final Task stack1 = createStackForShouldBeVisibleTest( - display.getDefaultTaskDisplayArea(), + final Task rootTask1 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(), WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task stack2 = createStackForShouldBeVisibleTest( - display.getDefaultTaskDisplayArea(), + final Task rootTask2 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(), WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - // There is still an activity1 in stack1 so the activity2 should be added to finishing list - // that will be destroyed until idle. - stack2.getTopNonFinishingActivity().mVisibleRequested = true; - final ActivityRecord activity2 = finishTopActivity(stack2); + // There is still an activity1 in rootTask1 so the activity2 should be added to finishing + // list that will be destroyed until idle. + rootTask2.getTopNonFinishingActivity().mVisibleRequested = true; + final ActivityRecord activity2 = finishTopActivity(rootTask2); assertEquals(STOPPING, activity2.getState()); assertThat(mSupervisor.mStoppingActivities).contains(activity2); // The display becomes empty. Since there is no next activity to be idle, the activity // should be destroyed immediately with updating configuration to restore original state. - final ActivityRecord activity1 = finishTopActivity(stack1); + final ActivityRecord activity1 = finishTopActivity(rootTask1); assertEquals(DESTROYING, activity1.getState()); verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */, eq(display.mDisplayId), anyBoolean(), anyBoolean()); } - private ActivityRecord finishTopActivity(Task stack) { - final ActivityRecord activity = stack.topRunningActivity(); + private ActivityRecord finishTopActivity(Task task) { + final ActivityRecord activity = task.topRunningActivity(); assertNotNull(activity); activity.setState(STOPPED, "finishTopActivity"); activity.makeFinishingLocked(); @@ -1214,24 +1366,24 @@ public class ActivityStackTests extends WindowTestsBase { // When focused activity and keyguard is going away, we should not sleep regardless // of the display state, but keyguard-going-away should only take effects on default // display since there is no keyguard on secondary displays (yet). - verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, + verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/, true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); - verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, + verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/, true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */); - // When not the focused stack, defer to display sleeping state. - verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/, + // When not the focused root task, defer to display sleeping state. + verifyShouldSleepActivities(false /* focusedRootTask */, true /*keyguardGoingAway*/, true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); // If keyguard is going away, defer to the display sleeping state. - verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, + verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/, true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); - verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, + verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/, false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); } @Test - public void testStackOrderChangedOnRemoveStack() { + public void testRootTaskOrderChangedOnRemoveRootTask() { final Task task = new TaskBuilder(mSupervisor).build(); RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); @@ -1244,7 +1396,7 @@ public class ActivityStackTests extends WindowTestsBase { } @Test - public void testStackOrderChangedOnAddPositionStack() { + public void testRootTaskOrderChangedOnAddPositionRootTask() { final Task task = new TaskBuilder(mSupervisor).build(); mDefaultTaskDisplayArea.removeRootTask(task); @@ -1260,14 +1412,14 @@ public class ActivityStackTests extends WindowTestsBase { } @Test - public void testStackOrderChangedOnPositionStack() { + public void testRootTaskOrderChangedOnPositionRootTask() { RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); try { - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( + final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); - mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1, + mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenRootTask1, false /*includingParents*/); } finally { mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener); @@ -1443,7 +1595,7 @@ public class ActivityStackTests extends WindowTestsBase { com.android.internal.R.bool.config_assistantOnTopOfDream); } - private void verifyShouldSleepActivities(boolean focusedStack, + private void verifyShouldSleepActivities(boolean focusedRootTask, boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, boolean expected) { final Task task = new TaskBuilder(mSupervisor).build(); @@ -1454,13 +1606,13 @@ public class ActivityStackTests extends WindowTestsBase { task.mDisplayContent = display; doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); doReturn(displaySleeping).when(display).isSleeping(); - doReturn(focusedStack).when(task).isFocusedRootTaskOnDisplay(); + doReturn(focusedRootTask).when(task).isFocusedRootTaskOnDisplay(); assertEquals(expected, task.shouldSleepActivities()); } private static class RootTaskOrderChangedListener - implements OnRootTaskOrderChangedListener { + implements TaskDisplayArea.OnRootTaskOrderChangedListener { public boolean mChanged = false; @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 1b114c194bfb..20bced252764 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -16,42 +16,93 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.TYPE_VIRTUAL; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; +import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE; import static com.android.server.wm.Task.ActivityState.FINISHING; import static com.android.server.wm.Task.ActivityState.PAUSED; import static com.android.server.wm.Task.ActivityState.PAUSING; import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.Task.ActivityState.STOPPING; +import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.refEq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.content.ComponentName; +import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; import android.platform.test.annotations.Presubmit; +import android.util.MergedConfiguration; +import android.util.Pair; -import androidx.test.filters.SmallTest; +import androidx.test.filters.MediumTest; +import com.android.internal.app.ResolverActivity; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + /** - * Tests for RootWindowContainer. + * Tests for {@link RootWindowContainer}. * * Build/Install/Run: * atest WmTests:RootWindowContainerTests */ -@SmallTest +@MediumTest @Presubmit @RunWith(WindowTestRunner.class) public class RootWindowContainerTests extends WindowTestsBase { + @Before + public void setUp() throws Exception { + doNothing().when(mAtm).updateSleepIfNeededLocked(); + } + @Test public void testUpdateDefaultDisplayWindowingModeOnSettingsRetrieved() { assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, @@ -166,5 +217,860 @@ public class RootWindowContainerTests extends WindowTestsBase { assertFalse(task.hasChild()); assertFalse(wpc.hasActivities()); } + + /** + * This test ensures that we do not try to restore a task based off an invalid task id. We + * should expect {@code null} to be returned in this case. + */ + @Test + public void testRestoringInvalidTask() { + mRootWindowContainer.getDefaultDisplay().removeAllTasks(); + Task task = mRootWindowContainer.anyTaskForId(0 /*taskId*/, + MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); + assertNull(task); + } + + /** + * This test ensures that an existing task in the pinned root task is moved to the fullscreen + * activity root task when a new task is added. + */ + @Test + public void testReplacingTaskInPinnedRootTask() { + Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(fullscreenTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(fullscreenTask).build(); + + fullscreenTask.moveToFront("testReplacingTaskInPinnedRootTask"); + + // Ensure full screen root task has both tasks. + ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity); + + // Move first activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove"); + + final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); + Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); + // Ensure a task has moved over. + ensureTaskPlacement(pinnedRootTask, firstActivity); + ensureTaskPlacement(fullscreenTask, secondActivity); + + // Move second activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove"); + + // Need to get root tasks again as a new instance might have been created. + pinnedRootTask = taskDisplayArea.getRootPinnedTask(); + fullscreenTask = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); + // Ensure root tasks have swapped tasks. + ensureTaskPlacement(pinnedRootTask, secondActivity); + ensureTaskPlacement(fullscreenTask, firstActivity); + } + + @Test + public void testMovingBottomMostRootTaskActivityToPinnedRootTask() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(fullscreenTask).build(); + final Task task = firstActivity.getTask(); + + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(fullscreenTask).build(); + + fullscreenTask.moveTaskToBack(task); + + // Ensure full screen task has both tasks. + ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity); + assertEquals(task.getTopMostActivity(), secondActivity); + firstActivity.setState(STOPPED, "testMovingBottomMostRootTaskActivityToPinnedRootTask"); + + + // Move first activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove"); + + assertTrue(firstActivity.mRequestForceTransition); + } + + private static void ensureTaskPlacement(Task task, ActivityRecord... activities) { + final ArrayList<ActivityRecord> taskActivities = new ArrayList<>(); + + task.forAllActivities((Consumer<ActivityRecord>) taskActivities::add, false); + + assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + taskActivities, + taskActivities.size(), activities != null ? activities.length : 0); + + if (activities == null) { + return; + } + + for (ActivityRecord activity : activities) { + assertTrue(taskActivities.contains(activity)); + } + } + + @Test + public void testApplySleepTokens() { + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final KeyguardController keyguard = mSupervisor.getKeyguardController(); + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(display) + .setOnTop(false) + .build(); + + // Make sure we wake and resume in the case the display is turning on and the keyguard is + // not showing. + verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedTask */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + true /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // showing. + verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedTask */, + true /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // not showing as unfocused. + verifySleepTokenBehavior(display, keyguard, task, true /*displaySleeping*/, + false /* displayShouldSleep */, false /* isFocusedTask */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Should not do anything if the display state hasn't changed. + verifySleepTokenBehavior(display, keyguard, task, false /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedTask */, + false /* keyguardShowing */, false /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + } + + private void verifySleepTokenBehavior(DisplayContent display, KeyguardController keyguard, + Task task, boolean displaySleeping, boolean displayShouldSleep, + boolean isFocusedTask, boolean keyguardShowing, boolean expectWakeFromSleep, + boolean expectResumeTopActivity) { + reset(task); + + doReturn(displayShouldSleep).when(display).shouldSleep(); + doReturn(displaySleeping).when(display).isSleeping(); + doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); + + doReturn(isFocusedTask).when(task).isFocusedRootTaskOnDisplay(); + doReturn(isFocusedTask ? task : null).when(display).getFocusedRootTask(); + TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea(); + doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask(); + mRootWindowContainer.applySleepTokens(true); + verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); + verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( + null /* target */, null /* targetOptions */); + } + + @Test + public void testAwakeFromSleepingWithAppConfiguration() { + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.moveFocusableActivityToTop("test"); + assertTrue(activity.getRootTask().isFocusedRootTaskOnDisplay()); + ActivityRecordTests.setRotatedScreenOrientationSilently(activity); + + final Configuration rotatedConfig = new Configuration(); + display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation() + .rotationForOrientation(activity.getOrientation(), display.getRotation())); + assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation); + // Assume the activity was shown in different orientation. For example, the top activity is + // landscape and the portrait lockscreen is shown. + activity.setLastReportedConfiguration( + new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig)); + activity.setState(Task.ActivityState.STOPPED, "sleep"); + + display.setIsSleeping(true); + doReturn(false).when(display).shouldSleep(); + // Allow to resume when awaking. + setBooted(mAtm); + mRootWindowContainer.applySleepTokens(true); + + // The display orientation should be changed by the activity so there is no relaunch. + verify(activity, never()).relaunchActivityLocked(anyBoolean()); + assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation); + } + + /** + * Verifies that removal of activity with task and root task is done correctly. + */ + @Test + public void testRemovingRootTaskOnAppCrash() { + final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer + .getDefaultTaskDisplayArea(); + final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount(); + final Task rootTask = defaultTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build(); + + assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount()); + + // Let's pretend that the app has crashed. + firstActivity.app.setThread(null); + mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); + + // Verify that the root task was removed. + assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount()); + } + + /** + * Verifies that removal of activities with task and root task is done correctly when there are + * several task display areas. + */ + @Test + public void testRemovingRootTaskOnAppCrash_multipleDisplayAreas() { + final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer + .getDefaultTaskDisplayArea(); + final int originalRootTaskCount = defaultTaskDisplayArea.getRootTaskCount(); + final Task rootTask = defaultTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(rootTask).build(); + assertEquals(originalRootTaskCount + 1, defaultTaskDisplayArea.getRootTaskCount()); + + final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); + final Task secondRootTask = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + new ActivityBuilder(mAtm).setTask(secondRootTask).setUseProcess(firstActivity.app).build(); + assertEquals(1, secondTaskDisplayArea.getRootTaskCount()); + + // Let's pretend that the app has crashed. + firstActivity.app.setThread(null); + mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); + + // Verify that the root tasks were removed. + assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount()); + assertEquals(0, secondTaskDisplayArea.getRootTaskCount()); + } + + @Test + public void testFocusability() { + final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer + .getDefaultTaskDisplayArea(); + final Task task = defaultTaskDisplayArea.createRootTask( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); + + // Created tasks are focusable by default. + assertTrue(task.isTopActivityFocusable()); + assertTrue(activity.isFocusable()); + + // If the task is made unfocusable, its activities should inherit that. + task.setFocusable(false); + assertFalse(task.isTopActivityFocusable()); + assertFalse(activity.isFocusable()); + + final Task pinnedTask = defaultTaskDisplayArea.createRootTask( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) + .setTask(pinnedTask).build(); + + // We should not be focusable when in pinned mode + assertFalse(pinnedTask.isTopActivityFocusable()); + assertFalse(pinnedActivity.isFocusable()); + + // Add flag forcing focusability. + pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; + + // Task with FLAG_ALWAYS_FOCUSABLE should be focusable. + assertTrue(pinnedTask.isTopActivityFocusable()); + assertTrue(pinnedActivity.isFocusable()); + } + + /** + * Verify that split-screen primary root task will be chosen if activity is launched that + * targets split-screen secondary, but a matching existing instance is found on top of + * split-screen primary root task. + */ + @Test + public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { + // Create primary split-screen root task with a task and an activity. + final Task primaryRootTask = mRootWindowContainer.getDefaultTaskDisplayArea() + .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryRootTask).build(); + final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build(); + + // Find a launch root task for the top activity in split-screen primary, while requesting + // split-screen secondary. + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); + final Task result = + mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */); + + // Assert that the primary root task is returned. + assertEquals(primaryRootTask, result); + } + + /** + * Verify that home root task would be moved to front when the top activity is Recents. + */ + @Test + public void testFindTaskToMoveToFrontWhenRecentsOnTop() { + // Create root task/task on default display. + final Task targetRootTask = new TaskBuilder(mSupervisor) + .setCreateActivity(true) + .setOnTop(false) + .build(); + final Task targetTask = targetRootTask.getBottomMostTask(); + + // Create Recents on top of the display. + final Task rootTask = new TaskBuilder(mSupervisor) + .setCreateActivity(true) + .setActivityType(ACTIVITY_TYPE_RECENTS) + .build(); + + final String reason = "findTaskToMoveToFront"; + mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, + false); + + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason)); + } + + /** + * Verify that home root task won't be moved to front if the top activity on other display is + * Recents. + */ + @Test + public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { + // Create tasks on default display. + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build(); + + // Create Recents on secondary display. + final TestDisplayContent secondDisplay = addNewDisplayContentAt( + DisplayContent.POSITION_TOP); + final Task rootTask = secondDisplay.getDefaultTaskDisplayArea() + .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); + new ActivityBuilder(mAtm).setTask(rootTask).build(); + + final String reason = "findTaskToMoveToFront"; + mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, + false); + + verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason)); + } + + /** + * Verify if a root task is not at the topmost position, it should be able to resume its + * activity if the root task is the top focused. + */ + @Test + public void testResumeActivityWhenNonTopmostRootTaskIsTopFocused() { + // Create a root task at bottom. + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */)); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); + taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/); + + // Assume the task is not at the topmost position (e.g. behind always-on-top root tasks) + // but it is the current top focused task. + assertFalse(rootTask.isTopRootTaskInDisplayArea()); + doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); + + // Use the task as target to resume. + mRootWindowContainer.resumeFocusedTasksTopActivities( + rootTask, activity, null /* targetOptions */); + + // Verify the target task should resume its activity. + verify(rootTask, times(1)).resumeTopActivityUncheckedLocked( + eq(activity), eq(null /* targetOptions */)); + } + + /** + * Verify that home activity will be started on a display even if another display has a + * focusable activity. + */ + @Test + public void testResumeFocusedRootTasksStartsHomeActivity_NoActivities() { + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + taskDisplayArea.getRootHomeTask().removeIfPossible(); + taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + + doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); + + mAtm.setBooted(true); + + // Trigger resume on all displays + mRootWindowContainer.resumeFocusedTasksTopActivities(); + + // Verify that home activity was started on the default display + verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); + } + + /** + * Verify that home activity will be started on a display even if another display has a + * focusable activity. + */ + @Test + public void testResumeFocusedRootTasksStartsHomeActivity_ActivityOnSecondaryScreen() { + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + taskDisplayArea.getRootHomeTask().removeIfPossible(); + taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + + // Create an activity on secondary display. + final TestDisplayContent secondDisplay = addNewDisplayContentAt( + DisplayContent.POSITION_TOP); + final Task rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + new ActivityBuilder(mAtm).setTask(rootTask).build(); + + doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); + + mAtm.setBooted(true); + + // Trigger resume on all displays + mRootWindowContainer.resumeFocusedTasksTopActivities(); + + // Verify that home activity was started on the default display + verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); + } + + /** + * Verify that a lingering transition is being executed in case the activity to be resumed is + * already resumed + */ + @Test + public void testResumeActivityLingeringTransition() { + // Create a root task at top. + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */)); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(rootTask).setOnTop(true).build(); + activity.setState(Task.ActivityState.RESUMED, "test"); + + // Assume the task is at the topmost position + assertTrue(rootTask.isTopRootTaskInDisplayArea()); + + // Use the task as target to resume. + mRootWindowContainer.resumeFocusedTasksTopActivities(); + + // Verify the lingering app transition is being executed because it's already resumed + verify(rootTask, times(1)).executeAppTransition(any()); + } + + @Test + public void testResumeActivityLingeringTransition_notExecuted() { + // Create a root task at bottom. + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final Task rootTask = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */)); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(rootTask).setOnTop(true).build(); + activity.setState(Task.ActivityState.RESUMED, "test"); + taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/); + + // Assume the task is at the topmost position + assertFalse(rootTask.isTopRootTaskInDisplayArea()); + doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); + + // Use the task as target to resume. + mRootWindowContainer.resumeFocusedTasksTopActivities(); + + // Verify the lingering app transition is being executed because it's already resumed + verify(rootTask, never()).executeAppTransition(any()); + } + + /** + * Tests that home activities can be started on the displays that supports system decorations. + */ + @Test + public void testStartHomeOnAllDisplays() { + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + mockResolveSecondaryHomeActivity(); + + // Create secondary displays. + final TestDisplayContent secondDisplay = + new TestDisplayContent.Builder(mAtm, 1000, 1500) + .setSystemDecorations(true).build(); + + doReturn(true).when(mRootWindowContainer) + .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); + doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), + anyBoolean()); + + mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome"); + + assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome()); + assertNotNull(secondDisplay.getTopRootTask()); + assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome()); + } + + /** + * Tests that home activities won't be started before booting when display added. + */ + @Test + public void testNotStartHomeBeforeBoot() { + final int displayId = 1; + final boolean isBooting = mAtm.mAmInternal.isBooting(); + final boolean isBooted = mAtm.mAmInternal.isBooted(); + try { + mAtm.mAmInternal.setBooting(false); + mAtm.mAmInternal.setBooted(false); + mRootWindowContainer.onDisplayAdded(displayId); + verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); + } finally { + mAtm.mAmInternal.setBooting(isBooting); + mAtm.mAmInternal.setBooted(isBooted); + } + } + + /** + * Tests whether home can be started if being instrumented. + */ + @Test + public void testCanStartHomeWhenInstrumented() { + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + final WindowProcessController app = mock(WindowProcessController.class); + doReturn(app).when(mAtm).getProcessController(any(), anyInt()); + + // Can not start home if we don't want to start home while home is being instrumented. + doReturn(true).when(app).isInstrumenting(); + final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer + .getDefaultTaskDisplayArea(); + assertFalse(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, + false /* allowInstrumenting*/)); + + // Can start home for other cases. + assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, + true /* allowInstrumenting*/)); + + doReturn(false).when(app).isInstrumenting(); + assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, + false /* allowInstrumenting*/)); + assertTrue(mRootWindowContainer.canStartHomeOnDisplayArea(info, defaultTaskDisplayArea, + true /* allowInstrumenting*/)); + } + + /** + * Tests that secondary home activity should not be resolved if device is still locked. + */ + @Test + public void testStartSecondaryHomeOnDisplayWithUserKeyLocked() { + // Create secondary displays. + final TestDisplayContent secondDisplay = + new TestDisplayContent.Builder(mAtm, 1000, 1500) + .setSystemDecorations(true).build(); + + // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false. + final int currentUser = mRootWindowContainer.mCurrentUser; + mRootWindowContainer.mCurrentUser = -1; + + mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", + secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); + + try { + verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any()); + } finally { + mRootWindowContainer.mCurrentUser = currentUser; + } + } + + /** + * Tests that secondary home activity should not be resolved if display does not support system + * decorations. + */ + @Test + public void testStartSecondaryHomeOnDisplayWithoutSysDecorations() { + // Create secondary displays. + final TestDisplayContent secondDisplay = + new TestDisplayContent.Builder(mAtm, 1000, 1500) + .setSystemDecorations(false).build(); + + mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "testStartSecondaryHome", + secondDisplay.mDisplayId, true /* allowInstrumenting */, true /* fromHomeKey */); + + verify(mRootWindowContainer, never()).resolveSecondaryHomeActivity(anyInt(), any()); + } + + /** + * Tests that when starting {@link #ResolverActivity} for home, it should use the standard + * activity type (in a new root task) so the order of back stack won't be broken. + */ + @Test + public void testStartResolverActivityForHome() { + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.packageName = "android"; + info.name = ResolverActivity.class.getName(); + doReturn(info).when(mRootWindowContainer).resolveHomeActivity(anyInt(), any()); + + mRootWindowContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY); + final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity(); + + assertEquals(info, resolverActivity.info); + assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType()); + } + + /** + * Tests that secondary home should be selected if primary home not set. + */ + @Test + public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() { + // Setup: primary home not set. + final Intent primaryHomeIntent = mAtm.getHomeIntent(); + final ActivityInfo aInfoPrimary = new ActivityInfo(); + aInfoPrimary.name = ResolverActivity.class.getName(); + doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), + refEq(primaryHomeIntent)); + // Setup: set secondary home. + mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); + + // Run the test. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer + .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); + assertEquals(aInfoSecondary.name, resolvedInfo.first.name); + assertEquals(aInfoSecondary.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); + } + + /** + * Tests that the default secondary home activity is always picked when it is in forced by + * config_useSystemProvidedLauncherForSecondary. + */ + @Test + public void testResolveSecondaryHomeActivityForced() { + // SetUp: set primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + // SetUp: set secondary home and force it. + mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */); + final Intent secondaryHomeIntent = + mAtm.getSecondaryHomeIntent(null /* preferredPackage */); + final List<ResolveInfo> resolutions = new ArrayList<>(); + final ResolveInfo resolveInfo = new ResolveInfo(); + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); + resolveInfo.activityInfo = aInfoSecondary; + resolutions.add(resolveInfo); + doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), + refEq(secondaryHomeIntent)); + doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), + anyBoolean()); + + // Run the test. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer + .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); + assertEquals(aInfoSecondary.name, resolvedInfo.first.name); + assertEquals(aInfoSecondary.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); + } + + /** + * Tests that secondary home should be selected if primary home not support secondary displays + * or there is no matched activity in the same package as selected primary home. + */ + @Test + public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() { + // Setup: there is no matched activity in the same package as selected primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + final List<ResolveInfo> resolutions = new ArrayList<>(); + doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); + // Setup: set secondary home. + mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); + + // Run the test. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer + .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); + assertEquals(aInfoSecondary.name, resolvedInfo.first.name); + assertEquals(aInfoSecondary.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); + } + /** + * Tests that primary home activity should be selected if it already support secondary displays. + */ + @Test + public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() { + // SetUp: set primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + // SetUp: put primary home info on 2nd item + final List<ResolveInfo> resolutions = new ArrayList<>(); + final ResolveInfo infoFake1 = new ResolveInfo(); + infoFake1.activityInfo = new ActivityInfo(); + infoFake1.activityInfo.name = "fakeActivity1"; + infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); + infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; + final ResolveInfo infoFake2 = new ResolveInfo(); + final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */); + infoFake2.activityInfo = aInfoPrimary; + resolutions.add(infoFake1); + resolutions.add(infoFake2); + doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); + + doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), + anyBoolean()); + + // Run the test. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer + .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); + assertEquals(aInfoPrimary.name, resolvedInfo.first.name); + assertEquals(aInfoPrimary.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); + } + + /** + * Tests that the first one that matches should be selected if there are multiple activities. + */ + @Test + public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { + // SetUp: set primary home. + mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); + // Setup: prepare two eligible activity info. + final List<ResolveInfo> resolutions = new ArrayList<>(); + final ResolveInfo infoFake1 = new ResolveInfo(); + infoFake1.activityInfo = new ActivityInfo(); + infoFake1.activityInfo.name = "fakeActivity1"; + infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); + infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; + final ResolveInfo infoFake2 = new ResolveInfo(); + infoFake2.activityInfo = new ActivityInfo(); + infoFake2.activityInfo.name = "fakeActivity2"; + infoFake2.activityInfo.applicationInfo = new ApplicationInfo(); + infoFake2.activityInfo.applicationInfo.packageName = "fakePackage2"; + resolutions.add(infoFake1); + resolutions.add(infoFake2); + doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); + + doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(), + anyBoolean()); + + // Use the first one of matched activities in the same package as selected primary home. + final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer + .resolveSecondaryHomeActivity(0 /* userId */, mock(TaskDisplayArea.class)); + + assertEquals(infoFake1.activityInfo.applicationInfo.packageName, + resolvedInfo.first.applicationInfo.packageName); + assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name); + } + + /** + * Test that {@link RootWindowContainer#getLaunchRootTask} with the real caller id will get the + * expected root task when requesting the activity launch on the secondary display. + */ + @Test + public void testGetLaunchRootTaskWithRealCallerId() { + // Create a non-system owned virtual display. + final TestDisplayContent secondaryDisplay = + new TestDisplayContent.Builder(mAtm, 1000, 1500) + .setType(TYPE_VIRTUAL).setOwnerUid(100).build(); + + // Create an activity with specify the original launch pid / uid. + final ActivityRecord r = new ActivityBuilder(mAtm).setLaunchedFromPid(200) + .setLaunchedFromUid(200).build(); + + // Simulate ActivityStarter to find a launch root task for requesting the activity to launch + // on the secondary display with realCallerId. + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(secondaryDisplay.mDisplayId); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId, + 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info); + final Task result = mRootWindowContainer.getLaunchRootTask(r, options, + null /* task */, true /* onTop */, null, 300 /* test realCallerPid */, + 300 /* test realCallerUid */); + + // Assert that the root task is returned as expected. + assertNotNull(result); + assertEquals("The display ID of the root task should same as secondary display ", + secondaryDisplay.mDisplayId, result.getDisplayId()); + } + + @Test + public void testGetValidLaunchRootTaskOnDisplayWithCandidateRootTask() { + // Create a root task with an activity on secondary display. + final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300, + 600).build(); + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(secondaryDisplay).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); + + // Make sure the root task is valid and can be reused on default display. + final Task rootTask = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea( + mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, + null /* options */, null /* launchParams */); + assertEquals(task, rootTask); + } + + @Test + public void testSwitchUser_missingHomeRootTask() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + doReturn(fullscreenTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); + + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + Task rootHomeTask = taskDisplayArea.getRootHomeTask(); + if (rootHomeTask != null) { + rootHomeTask.removeImmediately(); + } + assertNull(taskDisplayArea.getRootHomeTask()); + + int currentUser = mRootWindowContainer.mCurrentUser; + int otherUser = currentUser + 1; + + mRootWindowContainer.switchUser(otherUser, null); + + assertNotNull(taskDisplayArea.getRootHomeTask()); + assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask()); + } + + /** + * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity + * info for test cases. + * + * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use + * secondary home intent. + * @param forceSystemProvided Indicate to force using system provided home activity. + */ + private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) { + ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome); + Intent targetIntent; + if (primaryHome) { + targetIntent = mAtm.getHomeIntent(); + } else { + Resources resources = mContext.getResources(); + spyOn(resources); + doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString( + com.android.internal.R.string.config_secondaryHomePackage); + doReturn(forceSystemProvided).when(resources).getBoolean( + com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); + targetIntent = mAtm.getSecondaryHomeIntent(null /* preferredPackage */); + } + doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(), + refEq(targetIntent)); + } + + /** + * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent + * activity info for test cases. + */ + private void mockResolveSecondaryHomeActivity() { + final Intent secondaryHomeIntent = mAtm + .getSecondaryHomeIntent(null /* preferredPackage */); + final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false); + doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) + .resolveSecondaryHomeActivity(anyInt(), any()); + } + + private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) { + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity"; + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = + primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage"; + return aInfo; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index eba5634e4355..92d4edec85f4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -28,6 +28,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; @@ -35,13 +36,16 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.Task.ActivityState.RESUMED; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -56,8 +60,6 @@ import androidx.test.filters.SmallTest; import com.android.server.wm.LaunchParamsController.LaunchParams; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -72,35 +74,17 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class TaskDisplayAreaTests extends WindowTestsBase { - private Task mPinnedTask; - - @Before - public void setUp() throws Exception { - mPinnedTask = createTaskStackOnDisplay( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent); - // Stack should contain visible app window to be considered visible. - assertFalse(mPinnedTask.isVisible()); - final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent); - mPinnedTask.addChild(pinnedApp, 0 /* addPos */); - assertTrue(mPinnedTask.isVisible()); - } - - @After - public void tearDown() throws Exception { - mPinnedTask.removeImmediately(); - } - @Test public void getOrCreateLaunchRootRespectsResolvedWindowingMode() { - final Task rootTask = createTaskStackOnDisplay( - WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); rootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); taskDisplayArea.setLaunchRootTask( rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD}); - final Task candidateRootTask = createTaskStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task candidateRootTask = createTask( + mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); final LaunchParams launchParams = new LaunchParams(); launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM; @@ -113,15 +97,15 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test public void getOrCreateLaunchRootUsesActivityOptionsWindowingMode() { - final Task rootTask = createTaskStackOnDisplay( - WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); rootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); taskDisplayArea.setLaunchRootTask( rootTask, new int[]{WINDOWING_MODE_FREEFORM}, new int[]{ACTIVITY_TYPE_STANDARD}); - final Task candidateRootTask = createTaskStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task candidateRootTask = createTask( + mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); @@ -134,9 +118,8 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() { - final Task stack = createTaskStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); task.addChild(activity, 0 /* addPos */); final TaskDisplayArea taskDisplayArea = activity.getDisplayArea(); @@ -152,87 +135,103 @@ public class TaskDisplayAreaTests extends WindowTestsBase { } @Test - public void testStackPositionChildAt() { - // Test that always-on-top stack can't be moved to position other than top. - final Task stack1 = createTaskStackOnDisplay(mDisplayContent); - final Task stack2 = createTaskStackOnDisplay(mDisplayContent); - - final WindowContainer taskStackContainer = stack1.getParent(); - - final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1); - final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask); - assertThat(pinnedStackPos).isGreaterThan(stack2Pos); - assertThat(stack2Pos).isGreaterThan(stack1Pos); - - taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedTask, false); - assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); - assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); - - taskStackContainer.positionChildAt(1, mPinnedTask, false); - assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); - assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); + public void testRootTaskPositionChildAt() { + Task pinnedTask = createTask( + mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); + // Root task should contain visible app window to be considered visible. + assertFalse(pinnedTask.isVisible()); + final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent); + pinnedTask.addChild(pinnedApp, 0 /* addPos */); + assertTrue(pinnedTask.isVisible()); + + // Test that always-on-top root task can't be moved to position other than top. + final Task rootTask1 = createTask(mDisplayContent); + final Task rootTask2 = createTask(mDisplayContent); + + final WindowContainer taskContainer = rootTask1.getParent(); + + final int rootTask1Pos = taskContainer.mChildren.indexOf(rootTask1); + final int rootTask2Pos = taskContainer.mChildren.indexOf(rootTask2); + final int pinnedTaskPos = taskContainer.mChildren.indexOf(pinnedTask); + assertThat(pinnedTaskPos).isGreaterThan(rootTask2Pos); + assertThat(rootTask2Pos).isGreaterThan(rootTask1Pos); + + taskContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, pinnedTask, false); + assertEquals(taskContainer.mChildren.get(rootTask1Pos), rootTask1); + assertEquals(taskContainer.mChildren.get(rootTask2Pos), rootTask2); + assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask); + + taskContainer.positionChildAt(1, pinnedTask, false); + assertEquals(taskContainer.mChildren.get(rootTask1Pos), rootTask1); + assertEquals(taskContainer.mChildren.get(rootTask2Pos), rootTask2); + assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask); } @Test - public void testStackPositionBelowPinnedStack() { - // Test that no stack can be above pinned stack. - final Task stack1 = createTaskStackOnDisplay(mDisplayContent); + public void testRootTaskPositionBelowPinnedRootTask() { + Task pinnedTask = createTask( + mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); + // Root task should contain visible app window to be considered visible. + assertFalse(pinnedTask.isVisible()); + final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent); + pinnedTask.addChild(pinnedApp, 0 /* addPos */); + assertTrue(pinnedTask.isVisible()); + + // Test that no root task can be above pinned root task. + final Task rootTask1 = createTask(mDisplayContent); - final WindowContainer taskStackContainer = stack1.getParent(); + final WindowContainer taskContainer = rootTask1.getParent(); - final int stackPos = taskStackContainer.mChildren.indexOf(stack1); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask); - assertThat(pinnedStackPos).isGreaterThan(stackPos); + final int rootTaskPos = taskContainer.mChildren.indexOf(rootTask1); + final int pinnedTaskPos = taskContainer.mChildren.indexOf(pinnedTask); + assertThat(pinnedTaskPos).isGreaterThan(rootTaskPos); - taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false); - assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); + taskContainer.positionChildAt(WindowContainer.POSITION_TOP, rootTask1, false); + assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1); + assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask); - taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false); - assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); + taskContainer.positionChildAt(taskContainer.mChildren.size() - 1, rootTask1, false); + assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1); + assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask); } @Test - public void testDisplayPositionWithPinnedStack() { - // Make sure the display is trusted display which capable to move the stack to top. + public void testDisplayPositionWithPinnedRootTask() { + // Make sure the display is trusted display which capable to move the root task to top. spyOn(mDisplayContent); doReturn(true).when(mDisplayContent).isTrusted(); - // Allow child stack to move to top. + // Allow child root task to move to top. mDisplayContent.mDontMoveToTop = false; - // The display contains pinned stack that was added in {@link #setUp}. - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + // The display contains pinned root task that was added in {@link #setUp}. + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); // Add another display at top. mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), false /* includingParents */); // Move the task of {@code mDisplayContent} to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); - final int indexOfDisplayWithPinnedStack = mWm.mRoot.mChildren.indexOf(mDisplayContent); + rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + final int indexOfDisplayWithPinnedRootTask = mWm.mRoot.mChildren.indexOf(mDisplayContent); assertEquals("The testing DisplayContent should be moved to top with task", - mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack); + mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedRootTask); } @Test public void testMovingChildTaskOnTop() { - // Make sure the display is trusted display which capable to move the stack to top. + // Make sure the display is trusted display which capable to move the root task to top. spyOn(mDisplayContent); doReturn(true).when(mDisplayContent).isTrusted(); - // Allow child stack to move to top. + // Allow child root task to move to top. mDisplayContent.mDontMoveToTop = false; - // The display contains pinned stack that was added in {@link #setUp}. - Task stack = createTaskStackOnDisplay(mDisplayContent); - Task task = createTaskInStack(stack, 0 /* userId */); + // The display contains pinned root task that was added in {@link #setUp}. + Task rootTask = createTask(mDisplayContent); + Task task = createTaskInRootTask(rootTask, 0 /* userId */); // Add another display at top. mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), @@ -243,7 +242,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); // Move the task of {@code mDisplayContent} to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); // Ensure that original display ({@code mDisplayContent}) is now on the top. assertEquals("The testing DisplayContent should be moved to top with task", @@ -252,16 +251,16 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test public void testDontMovingChildTaskOnTop() { - // Make sure the display is trusted display which capable to move the stack to top. + // Make sure the display is trusted display which capable to move the root task to top. spyOn(mDisplayContent); doReturn(true).when(mDisplayContent).isTrusted(); - // Allow child stack to move to top. + // Allow child root task to move to top. mDisplayContent.mDontMoveToTop = true; - // The display contains pinned stack that was added in {@link #setUp}. - Task stack = createTaskStackOnDisplay(mDisplayContent); - Task task = createTaskInStack(stack, 0 /* userId */); + // The display contains pinned root task that was added in {@link #setUp}. + Task rootTask = createTask(mDisplayContent); + Task task = createTaskInRootTask(rootTask, 0 /* userId */); // Add another display at top. mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), @@ -272,7 +271,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); // Try moving the task of {@code mDisplayContent} to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not // on the top. @@ -282,8 +281,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test public void testReuseTaskAsRootTask() { - final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task candidateTask = createTask(mDisplayContent); final int type = ACTIVITY_TYPE_STANDARD; assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask, true /* reuseCandidate */); @@ -312,7 +310,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { } @Test - public void testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange() { + public void testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange() { final RootWindowContainer rootWindowContainer = mWm.mAtmService.mRootWindowContainer; final TaskDisplayArea defaultTaskDisplayArea = rootWindowContainer.getDefaultTaskDisplayArea(); @@ -330,7 +328,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { ActivityRecord primarySplitActivity = primarySplitTask.getTopNonFinishingActivity(); assertNotNull(primarySplitActivity); primarySplitActivity.setState(RESUMED, - "testGetOrientation_nonResizableHomeStackWithHomeActivityPendingVisibilityChange"); + "testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange"); ActivityRecord homeActivity = rootHomeTask.getTopNonFinishingActivity(); if (homeActivity == null) { @@ -350,14 +348,14 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); - final Task firstStack = firstTaskDisplayArea.createRootTask( + final Task firstRootTask = firstTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task secondStack = secondTaskDisplayArea.createRootTask( + final Task secondRootTask = secondTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(firstStack).build(); + .setTask(firstRootTask).build(); final ActivityRecord secondActivity = new ActivityBuilder(mAtm) - .setTask(secondStack).build(); + .setTask(secondRootTask).build(); // Activity on TDA1 is focused mDisplayContent.setFocusedApp(firstActivity); @@ -384,14 +382,14 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); - final Task firstStack = firstTaskDisplayArea.createRootTask( + final Task firstRootTask = firstTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task secondStack = secondTaskDisplayArea.createRootTask( + final Task secondRootTask = secondTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(firstStack).build(); + .setTask(firstRootTask).build(); final ActivityRecord secondActivity = new ActivityBuilder(mAtm) - .setTask(secondStack).build(); + .setTask(secondRootTask).build(); firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); @@ -411,9 +409,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test public void testIgnoreOrientationRequest() { final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task stack = taskDisplayArea.createRootTask( + final Task task = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); mDisplayContent.setFocusedApp(activity); activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); @@ -428,7 +426,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test @UseTestDisplay public void testRemove_reparentToDefault() { - final Task task = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTask(mDisplayContent); final TaskDisplayArea displayArea = task.getDisplayArea(); displayArea.remove(); assertTrue(displayArea.isRemoved()); @@ -442,8 +440,8 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test @UseTestDisplay - public void testRemove_stackCreatedByOrganizer() { - final Task task = createTaskStackOnDisplay(mDisplayContent); + public void testRemove_rootTaskCreatedByOrganizer() { + final Task task = createTask(mDisplayContent); task.mCreatedByOrganizer = true; final TaskDisplayArea displayArea = task.getDisplayArea(); displayArea.remove(); @@ -464,4 +462,221 @@ public class TaskDisplayAreaTests extends WindowTestsBase { null /* activityOptions */); assertEquals(reuseCandidate, rootTask == candidateTask); } + + @Test + public void testGetOrCreateRootHomeTask_defaultDisplay() { + TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea(); + + // Remove the current home root task if it exists so a new one can be created below. + Task homeTask = defaultTaskDisplayArea.getRootHomeTask(); + if (homeTask != null) { + defaultTaskDisplayArea.removeChild(homeTask); + } + assertNull(defaultTaskDisplayArea.getRootHomeTask()); + + assertNotNull(defaultTaskDisplayArea.getOrCreateRootHomeTask()); + } + + @Test + public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() { + DisplayContent display = createNewDisplay(); + doReturn(true).when(display).supportsSystemDecorations(); + + // Remove the current home root task if it exists so a new one can be created below. + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + Task homeTask = taskDisplayArea.getRootHomeTask(); + if (homeTask != null) { + taskDisplayArea.removeChild(homeTask); + } + assertNull(taskDisplayArea.getRootHomeTask()); + + assertNotNull(taskDisplayArea.getOrCreateRootHomeTask()); + } + + @Test + public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() { + DisplayContent display = createNewDisplay(); + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + doReturn(false).when(display).supportsSystemDecorations(); + + assertNull(taskDisplayArea.getRootHomeTask()); + assertNull(taskDisplayArea.getOrCreateRootHomeTask()); + } + + @Test + public void testGetOrCreateRootHomeTask_untrustedDisplay() { + DisplayContent display = createNewDisplay(); + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + doReturn(false).when(display).isTrusted(); + + assertNull(taskDisplayArea.getRootHomeTask()); + assertNull(taskDisplayArea.getOrCreateRootHomeTask()); + } + + @Test + public void testGetOrCreateRootHomeTask_dontMoveToTop() { + DisplayContent display = createNewDisplay(); + display.mDontMoveToTop = true; + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + + assertNull(taskDisplayArea.getRootHomeTask()); + assertNull(taskDisplayArea.getOrCreateRootHomeTask()); + } + + @Test + public void testLastFocusedRootTaskIsUpdatedWhenMovingRootTask() { + // Create a root task at bottom. + final TaskDisplayArea taskDisplayAreas = + mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea(); + final Task rootTask = + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); + final Task prevFocusedRootTask = taskDisplayAreas.getFocusedRootTask(); + + rootTask.moveToFront("moveRootTaskToFront"); + // After moving the root task to front, the previous focused should be the last focused. + assertTrue(rootTask.isFocusedRootTaskOnDisplay()); + assertEquals(prevFocusedRootTask, taskDisplayAreas.getLastFocusedRootTask()); + + rootTask.moveToBack("moveRootTaskToBack", null /* task */); + // After moving the root task to back, the root task should be the last focused. + assertEquals(rootTask, taskDisplayAreas.getLastFocusedRootTask()); + } + + /** + * This test simulates the picture-in-picture menu activity launches an activity to fullscreen + * root task. The fullscreen root task should be the top focused for resuming correctly. + */ + @Test + public void testFullscreenRootTaskCanBeFocusedWhenFocusablePinnedRootTaskExists() { + // Create a pinned root task and move to front. + final Task pinnedRootTask = mRootWindowContainer.getDefaultTaskDisplayArea() + .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); + final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor) + .setParentTask(pinnedRootTask).build(); + new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE) + .setTask(pinnedTask).build(); + pinnedRootTask.moveToFront("movePinnedRootTaskToFront"); + + // The focused root task should be the pinned root task. + assertTrue(pinnedRootTask.isFocusedRootTaskOnDisplay()); + + // Create a fullscreen root task and move to front. + final Task fullscreenRootTask = createTaskWithActivity( + mRootWindowContainer.getDefaultTaskDisplayArea(), + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true); + fullscreenRootTask.moveToFront("moveFullscreenRootTaskToFront"); + + // The focused root task should be the fullscreen root task. + assertTrue(fullscreenRootTask.isFocusedRootTaskOnDisplay()); + } + + /** + * Test {@link TaskDisplayArea#mPreferredTopFocusableRootTask} will be cleared when + * the root task is removed or moved to back, and the focused root task will be according to + * z-order. + */ + @Test + public void testRootTaskShouldNotBeFocusedAfterMovingToBackOrRemoving() { + // Create a display which only contains 2 root task. + final DisplayContent display = addNewDisplayContentAt(POSITION_TOP); + final Task rootTask1 = createTaskWithActivity(display.getDefaultTaskDisplayArea(), + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */); + final Task rootTask2 = createTaskWithActivity(display.getDefaultTaskDisplayArea(), + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */); + + // Put rootTask1 and rootTask2 on top. + rootTask1.moveToFront("moveRootTask1ToFront"); + rootTask2.moveToFront("moveRootTask2ToFront"); + assertTrue(rootTask2.isFocusedRootTaskOnDisplay()); + + // rootTask1 should be focused after moving rootTask2 to back. + rootTask2.moveToBack("moveRootTask2ToBack", null /* task */); + assertTrue(rootTask1.isFocusedRootTaskOnDisplay()); + + // rootTask2 should be focused after removing rootTask1. + rootTask1.getDisplayArea().removeRootTask(rootTask1); + assertTrue(rootTask2.isFocusedRootTaskOnDisplay()); + } + + /** + * This test enforces that alwaysOnTop root task is placed at proper position. + */ + @Test + public void testAlwaysOnTopRootTaskLocation() { + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final Task alwaysOnTopRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(alwaysOnTopRootTask).build(); + alwaysOnTopRootTask.setAlwaysOnTop(true); + taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopRootTask, + false /* includingParents */); + assertTrue(alwaysOnTopRootTask.isAlwaysOnTop()); + // Ensure always on top state is synced to the children of the root task. + assertTrue(alwaysOnTopRootTask.getTopNonFinishingActivity().isAlwaysOnTop()); + assertEquals(alwaysOnTopRootTask, taskDisplayArea.getTopRootTask()); + + final Task pinnedRootTask = taskDisplayArea.createRootTask( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + assertEquals(pinnedRootTask, taskDisplayArea.getRootPinnedTask()); + assertEquals(pinnedRootTask, taskDisplayArea.getTopRootTask()); + + final Task anotherAlwaysOnTopRootTask = taskDisplayArea.createRootTask( + WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); + anotherAlwaysOnTopRootTask.setAlwaysOnTop(true); + taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopRootTask, + false /* includingParents */); + assertTrue(anotherAlwaysOnTopRootTask.isAlwaysOnTop()); + int topPosition = taskDisplayArea.getRootTaskCount() - 1; + // Ensure the new alwaysOnTop root task is put below the pinned root task, but on top of the + // existing alwaysOnTop root task. + assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask)); + + final Task nonAlwaysOnTopRootTask = taskDisplayArea.createRootTask( + WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); + assertEquals(taskDisplayArea, nonAlwaysOnTopRootTask.getDisplayArea()); + topPosition = taskDisplayArea.getRootTaskCount() - 1; + // Ensure the non-alwaysOnTop root task is put below the three alwaysOnTop root tasks, but + // above the existing other non-alwaysOnTop root tasks. + assertEquals(topPosition - 3, getTaskIndexOf(taskDisplayArea, nonAlwaysOnTopRootTask)); + + anotherAlwaysOnTopRootTask.setAlwaysOnTop(false); + taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopRootTask, + false /* includingParents */); + assertFalse(anotherAlwaysOnTopRootTask.isAlwaysOnTop()); + // Ensure, when always on top is turned off for a root task, the root task is put just below + // all other always on top root tasks. + assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask)); + anotherAlwaysOnTopRootTask.setAlwaysOnTop(true); + + // Ensure always on top state changes properly when windowing mode changes. + anotherAlwaysOnTopRootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertFalse(anotherAlwaysOnTopRootTask.isAlwaysOnTop()); + assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask)); + anotherAlwaysOnTopRootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertTrue(anotherAlwaysOnTopRootTask.isAlwaysOnTop()); + assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopRootTask)); + + final Task dreamRootTask = taskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */); + assertEquals(taskDisplayArea, dreamRootTask.getDisplayArea()); + assertTrue(dreamRootTask.isAlwaysOnTop()); + topPosition = taskDisplayArea.getRootTaskCount() - 1; + // Ensure dream shows above all activities, including PiP + assertEquals(dreamRootTask, taskDisplayArea.getTopRootTask()); + assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, pinnedRootTask)); + + final Task assistRootTask = taskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); + assertEquals(taskDisplayArea, assistRootTask.getDisplayArea()); + assertFalse(assistRootTask.isAlwaysOnTop()); + topPosition = taskDisplayArea.getRootTaskCount() - 1; + + // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream + // is false and on top of everything when true. + final boolean isAssistantOnTop = mContext.getResources() + .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream); + assertEquals(isAssistantOnTop ? topPosition : topPosition - 4, + getTaskIndexOf(taskDisplayArea, assistRootTask)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index ed5729400a39..de4c40df1c79 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -403,8 +403,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { public void testOverridesDisplayAreaWithStandardTypeAndFullscreenMode() { final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay, mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); - final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, secondaryDisplayArea); + final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); launchRoot.mCreatedByOrganizer = true; secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN }, @@ -419,8 +419,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { public void testOverridesDisplayAreaWithHomeTypeAndFullscreenMode() { final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay, mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); - final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, secondaryDisplayArea); + final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); launchRoot.mCreatedByOrganizer = true; mActivity.setActivityType(ACTIVITY_TYPE_HOME); @@ -438,8 +438,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { WINDOWING_MODE_FREEFORM); final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(freeformDisplay, mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); - final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, secondaryDisplayArea); + final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); launchRoot.mCreatedByOrganizer = true; secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM }, @@ -455,8 +455,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayArea() { final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay, mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); - final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, secondaryDisplayArea); + final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); launchRoot.mCreatedByOrganizer = true; secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN }, @@ -481,8 +481,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0, DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height()); - final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, secondaryDisplayArea); + final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); launchRoot.mCreatedByOrganizer = true; secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM }, new int[] { ACTIVITY_TYPE_STANDARD }); @@ -512,8 +512,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); secondaryDisplayArea.setBounds(DISPLAY_BOUNDS.width() / 2, 0, DISPLAY_BOUNDS.width(), DISPLAY_BOUNDS.height()); - final Task launchRoot = createTaskStackOnTaskDisplayArea(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, secondaryDisplayArea); + final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); launchRoot.mCreatedByOrganizer = true; secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FREEFORM }, new int[] { ACTIVITY_TYPE_STANDARD }); @@ -1687,16 +1687,16 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { } private ActivityRecord createSourceActivity(TestDisplayContent display) { - final Task stack = display.getDefaultTaskDisplayArea() + final Task rootTask = display.getDefaultTaskDisplayArea() .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); - return new ActivityBuilder(mAtm).setTask(stack).build(); + return new ActivityBuilder(mAtm).setTask(rootTask).build(); } private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) { - final Task stack = display.getDefaultTaskDisplayArea() + final Task rootTask = display.getDefaultTaskDisplayArea() .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); - stack.setWindowingMode(WINDOWING_MODE_FREEFORM); - final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); + rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); // Just work around the unnecessary adjustments for bounds. task.getWindowConfiguration().setBounds(bounds); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java deleted file mode 100644 index d853b930af11..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ /dev/null @@ -1,1161 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; -import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.util.DisplayMetrics.DENSITY_DEFAULT; -import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_90; -import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE; - -import static com.google.common.truth.Truth.assertThat; - -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.same; - -import android.app.ActivityManager; -import android.app.TaskInfo; -import android.app.WindowConfiguration; -import android.content.ComponentName; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.IBinder; -import android.platform.test.annotations.Presubmit; -import android.util.DisplayMetrics; -import android.util.TypedXmlPullParser; -import android.util.TypedXmlSerializer; -import android.util.Xml; -import android.view.DisplayInfo; - -import androidx.test.filters.MediumTest; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; - -/** - * Tests for exercising {@link Task}. - * - * Build/Install/Run: - * atest WmTests:TaskRecordTests - */ -@MediumTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class TaskRecordTests extends WindowTestsBase { - - private static final String TASK_TAG = "task"; - - private Rect mParentBounds; - - @Before - public void setUp() throws Exception { - mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/); - removeGlobalMinSizeRestriction(); - } - - @Test - public void testRestoreWindowedTask() throws Exception { - final Task expected = createTask(64); - expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100); - - final byte[] serializedBytes = serializeToBytes(expected); - final Task actual = restoreFromBytes(serializedBytes); - assertEquals(expected.mTaskId, actual.mTaskId); - assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds); - } - - /** Ensure we have no chance to modify the original intent. */ - @Test - public void testCopyBaseIntentForTaskInfo() { - final Task task = createTask(1); - task.setTaskDescription(new ActivityManager.TaskDescription()); - final TaskInfo info = task.getTaskInfo(); - - // The intent of info should be a copy so assert that they are different instances. - assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent()))); - } - - @Test - public void testReturnsToHomeStack() throws Exception { - final Task task = createTask(1); - spyOn(task); - doReturn(true).when(task).hasChild(); - assertFalse(task.returnsToHomeRootTask()); - task.intent = null; - assertFalse(task.returnsToHomeRootTask()); - task.intent = new Intent(); - assertFalse(task.returnsToHomeRootTask()); - task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME); - assertTrue(task.returnsToHomeRootTask()); - } - - /** Ensures that empty bounds cause appBounds to inherit from parent. */ - @Test - public void testAppBounds_EmptyBounds() { - final Rect emptyBounds = new Rect(); - testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds, - mParentBounds); - } - - /** Ensures that bounds on freeform stacks are not clipped. */ - @Test - public void testAppBounds_FreeFormBounds() { - final Rect freeFormBounds = new Rect(mParentBounds); - freeFormBounds.offset(10, 10); - testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds, - freeFormBounds); - } - - /** Ensures that fully contained bounds are not clipped. */ - @Test - public void testAppBounds_ContainedBounds() { - final Rect insetBounds = new Rect(mParentBounds); - insetBounds.inset(5, 5, 5, 5); - testStackBoundsConfiguration( - WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds); - } - - @Test - public void testFitWithinBounds() { - final Rect parentBounds = new Rect(10, 10, 200, 200); - TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); - Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); - final Configuration parentConfig = stack.getConfiguration(); - parentConfig.windowConfiguration.setBounds(parentBounds); - parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT; - - // check top and left - Rect reqBounds = new Rect(-190, -190, 0, 0); - task.setBounds(reqBounds); - // Make sure part of it is exposed - assertTrue(task.getBounds().right > parentBounds.left); - assertTrue(task.getBounds().bottom > parentBounds.top); - // Should still be more-or-less in that corner - assertTrue(task.getBounds().left <= parentBounds.left); - assertTrue(task.getBounds().top <= parentBounds.top); - - assertEquals(reqBounds.width(), task.getBounds().width()); - assertEquals(reqBounds.height(), task.getBounds().height()); - - // check bottom and right - reqBounds = new Rect(210, 210, 400, 400); - task.setBounds(reqBounds); - // Make sure part of it is exposed - assertTrue(task.getBounds().left < parentBounds.right); - assertTrue(task.getBounds().top < parentBounds.bottom); - // Should still be more-or-less in that corner - assertTrue(task.getBounds().right >= parentBounds.right); - assertTrue(task.getBounds().bottom >= parentBounds.bottom); - - assertEquals(reqBounds.width(), task.getBounds().width()); - assertEquals(reqBounds.height(), task.getBounds().height()); - } - - /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */ - @Test - public void testBoundsOnModeChangeFreeformToFullscreen() { - DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay(); - Task stack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true) - .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); - Task task = stack.getBottomMostTask(); - task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); - DisplayInfo info = new DisplayInfo(); - display.mDisplay.getDisplayInfo(info); - final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight); - final Rect freeformBounds = new Rect(fullScreenBounds); - freeformBounds.inset((int) (freeformBounds.width() * 0.2), - (int) (freeformBounds.height() * 0.2)); - task.setBounds(freeformBounds); - - assertEquals(freeformBounds, task.getBounds()); - - // FULLSCREEN inherits bounds - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertEquals(fullScreenBounds, task.getBounds()); - assertEquals(freeformBounds, task.mLastNonFullscreenBounds); - - // FREEFORM restores bounds - stack.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(freeformBounds, task.getBounds()); - } - - /** - * Tests that a task with forced orientation has orientation-consistent bounds within the - * parent. - */ - @Test - public void testFullscreenBoundsForcedOrientation() { - final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); - final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); - final DisplayContent display = new TestDisplayContent.Builder(mAtm, - fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); - assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); - // Fix the display orientation to landscape which is the natural rotation (0) for the test - // display. - final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); - dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); - dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); - - final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - final Task task = stack.getBottomMostTask(); - final ActivityRecord root = task.getTopNonFinishingActivity(); - - assertEquals(fullScreenBounds, task.getBounds()); - - // Setting app to fixed portrait fits within parent - root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - assertEquals(root, task.getRootActivity()); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); - // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds. - assertThat(task.getBounds().height()).isLessThan(task.getBounds().width()); - assertEquals(fullScreenBounds, task.getBounds()); - - // Top activity gets used - final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(stack) - .build(); - assertEquals(top, task.getTopNonFinishingActivity()); - top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); - assertEquals(task.getBounds().width(), fullScreenBounds.width()); - - // Setting app to unspecified restores - top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED); - assertEquals(fullScreenBounds, task.getBounds()); - - // Setting app to fixed landscape and changing display - top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - // Fix the display orientation to portrait which is 90 degrees for the test display. - dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90); - - // Fixed orientation request should be resolved on activity level. Task fills display - // bounds. - assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); - assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); - assertEquals(fullScreenBoundsPort, task.getBounds()); - - // in FREEFORM, no constraint - final Rect freeformBounds = new Rect(display.getBounds()); - freeformBounds.inset((int) (freeformBounds.width() * 0.2), - (int) (freeformBounds.height() * 0.2)); - stack.setWindowingMode(WINDOWING_MODE_FREEFORM); - task.setBounds(freeformBounds); - assertEquals(freeformBounds, task.getBounds()); - - // FULLSCREEN letterboxes bounds on activity level, no constraint on task level. - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); - assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); - assertEquals(fullScreenBoundsPort, task.getBounds()); - - // FREEFORM restores bounds as before - stack.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(freeformBounds, task.getBounds()); - } - - @Test - public void testReportsOrientationRequestInLetterboxForOrientation() { - final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); - final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); - final DisplayContent display = new TestDisplayContent.Builder(mAtm, - fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); - assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); - // Fix the display orientation to landscape which is the natural rotation (0) for the test - // display. - final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); - dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); - dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); - - final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - final Task task = stack.getBottomMostTask(); - ActivityRecord root = task.getTopNonFinishingActivity(); - - assertEquals(fullScreenBounds, task.getBounds()); - - // Setting app to fixed portrait fits within parent on activity level. Task fills parent. - root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - assertThat(root.getBounds().width()).isLessThan(root.getBounds().height()); - assertEquals(task.getBounds(), fullScreenBounds); - - assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation()); - } - - @Test - public void testIgnoresForcedOrientationWhenParentHandles() { - final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); - DisplayContent display = new TestDisplayContent.Builder( - mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); - - display.getRequestedOverrideConfiguration().orientation = - Configuration.ORIENTATION_LANDSCAPE; - display.onRequestedOverrideConfigurationChanged( - display.getRequestedOverrideConfiguration()); - Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - Task task = stack.getBottomMostTask(); - ActivityRecord root = task.getTopNonFinishingActivity(); - - final WindowContainer parentWindowContainer = - new WindowContainer(mSystemServicesTestRule.getWindowManagerService()); - spyOn(parentWindowContainer); - parentWindowContainer.setBounds(fullScreenBounds); - doReturn(parentWindowContainer).when(task).getParent(); - doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea(); - doReturn(stack).when(task).getRootTask(); - doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant(); - - // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the - // bounds because its parent says it will handle it at a later time. - root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - assertEquals(root, task.getRootActivity()); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); - assertEquals(fullScreenBounds, task.getBounds()); - } - - @Test - public void testComputeConfigResourceOverrides() { - final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920); - TestDisplayContent display = new TestDisplayContent.Builder( - mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); - final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); - final Configuration inOutConfig = new Configuration(); - final Configuration parentConfig = new Configuration(); - final int longSide = 1200; - final int shortSide = 600; - final Rect parentBounds = new Rect(0, 0, 250, 500); - final Rect parentAppBounds = new Rect(0, 0, 250, 480); - parentConfig.windowConfiguration.setBounds(parentBounds); - parentConfig.windowConfiguration.setAppBounds(parentAppBounds); - parentConfig.densityDpi = 400; - parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200 - parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100 - parentConfig.windowConfiguration.setRotation(ROTATION_0); - - // By default, the input bounds will fill parent. - task.computeConfigResourceOverrides(inOutConfig, parentConfig); - - assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); - assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); - assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds()); - assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation); - - // If bounds are overridden, config properties should be made to match. Surface hierarchy - // will crop for policy. - inOutConfig.setToDefaults(); - final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide); - inOutConfig.windowConfiguration.setBounds(largerPortraitBounds); - task.computeConfigResourceOverrides(inOutConfig, parentConfig); - // The override bounds are beyond the parent, the out appBounds should not be intersected - // by parent appBounds. - assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds()); - assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160); - assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160); - - inOutConfig.setToDefaults(); - // Landscape bounds. - final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide); - inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds); - - // Setup the display with a top stable inset. The later assertion will ensure the inset is - // excluded from screenHeightDp. - final int statusBarHeight = 100; - final DisplayPolicy policy = display.getDisplayPolicy(); - doAnswer(invocationOnMock -> { - final Rect insets = invocationOnMock.<Rect>getArgument(0); - insets.top = statusBarHeight; - return null; - }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0)); - - // Without limiting to be inside the parent bounds, the out screen size should keep relative - // to the input bounds. - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); - final ActivityRecord.CompatDisplayInsets compatIntsets = - new ActivityRecord.CompatDisplayInsets( - display, activity, /* fixedOrientationBounds= */ null); - task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets); - - assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); - assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi, - inOutConfig.screenHeightDp); - assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi, - inOutConfig.screenWidthDp); - assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation); - } - - @Test - public void testComputeConfigResourceLayoutOverrides() { - final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500); - TestDisplayContent display = new TestDisplayContent.Builder( - mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); - final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); - final Configuration inOutConfig = new Configuration(); - final Configuration parentConfig = new Configuration(); - final Rect nonLongBounds = new Rect(0, 0, 1000, 1250); - parentConfig.windowConfiguration.setBounds(fullScreenBounds); - parentConfig.windowConfiguration.setAppBounds(fullScreenBounds); - parentConfig.densityDpi = 400; - parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi; - parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi; - parentConfig.windowConfiguration.setRotation(ROTATION_0); - - // Set BOTH screenW/H to an override value - inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi; - inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi; - task.computeConfigResourceOverrides(inOutConfig, parentConfig); - - // screenLayout should honor override when both screenW/H are set. - assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0); - } - - @Test - public void testComputeNestedConfigResourceOverrides() { - final Task task = new TaskBuilder(mSupervisor).build(); - assertTrue(task.getResolvedOverrideBounds().isEmpty()); - int origScreenH = task.getConfiguration().screenHeightDp; - Configuration stackConfig = new Configuration(); - stackConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration()); - stackConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - - // Set bounds on stack (not task) and verify that the task resource configuration changes - // despite it's override bounds being empty. - Rect bounds = new Rect(task.getRootTask().getBounds()); - bounds.bottom = (int) (bounds.bottom * 0.6f); - stackConfig.windowConfiguration.setBounds(bounds); - task.getRootTask().onRequestedOverrideConfigurationChanged(stackConfig); - assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp); - } - - @Test - public void testFullScreenTaskNotAdjustedByMinimalSize() { - final Task fullscreenTask = new TaskBuilder(mSupervisor).build(); - final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds()); - final ActivityInfo aInfo = new ActivityInfo(); - aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */, - 0 /* height */, 0 /* heightFraction */, 0 /* gravity */, - originalTaskBounds.width() * 2 /* minWidth */, - originalTaskBounds.height() * 2 /* minHeight */); - fullscreenTask.setMinDimensions(aInfo); - fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration()); - - assertEquals(originalTaskBounds, fullscreenTask.getBounds()); - } - - @Test - public void testInsetDisregardedWhenFreeformOverlapsNavBar() { - TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); - Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - DisplayInfo displayInfo = new DisplayInfo(); - mAtm.mContext.getDisplay().getDisplayInfo(displayInfo); - final int displayHeight = displayInfo.logicalHeight; - final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); - final Configuration inOutConfig = new Configuration(); - final Configuration parentConfig = new Configuration(); - final int longSide = 1200; - final int shortSide = 600; - parentConfig.densityDpi = 400; - parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px - parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px - parentConfig.windowConfiguration.setRotation(ROTATION_0); - - final int longSideDp = 480; // longSide / density = 1200 / 400 * 160 - final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160 - final int screenLayout = parentConfig.screenLayout - & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK); - final int reducedScreenLayout = - Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp); - - // Portrait bounds overlapping with navigation bar, without insets. - final Rect freeformBounds = new Rect(0, - displayHeight - 10 - longSide, - shortSide, - displayHeight - 10); - inOutConfig.windowConfiguration.setBounds(freeformBounds); - // Set to freeform mode to verify bug fix. - inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - - task.computeConfigResourceOverrides(inOutConfig, parentConfig); - - // screenW/H should not be effected by parent since overridden and freeform - assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi, - inOutConfig.screenWidthDp); - assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi, - inOutConfig.screenHeightDp); - assertEquals(reducedScreenLayout, inOutConfig.screenLayout); - - inOutConfig.setToDefaults(); - // Landscape bounds overlapping with navigtion bar, without insets. - freeformBounds.set(0, - displayHeight - 10 - shortSide, - longSide, - displayHeight - 10); - inOutConfig.windowConfiguration.setBounds(freeformBounds); - inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - - task.computeConfigResourceOverrides(inOutConfig, parentConfig); - - assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi, - inOutConfig.screenWidthDp); - assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi, - inOutConfig.screenHeightDp); - assertEquals(reducedScreenLayout, inOutConfig.screenLayout); - } - - /** Ensures that the alias intent won't have target component resolved. */ - @Test - public void testTaskIntentActivityAlias() { - final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity"; - final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity"; - final ComponentName aliasComponent = - new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName); - final ComponentName targetComponent = - new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName); - - final Intent intent = new Intent(); - intent.setComponent(aliasComponent); - final ActivityInfo info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME; - info.targetActivity = targetClassName; - - final Task task = new Task.Builder(mAtm) - .setTaskId(1) - .setActivityInfo(info) - .setIntent(intent) - .build(); - assertEquals("The alias activity component should be saved in task intent.", aliasClassName, - task.intent.getComponent().getClassName()); - - ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent( - aliasComponent).setTargetActivity(targetClassName).build(); - assertEquals("Should be the same intent filter.", true, - task.isSameIntentFilter(aliasActivity)); - - ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent( - targetComponent).build(); - assertEquals("Should be the same intent filter.", true, - task.isSameIntentFilter(targetActivity)); - - ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build(); - assertEquals("Should not be the same intent filter.", false, - task.isSameIntentFilter(defaultActivity)); - } - - /** Test that root activity index is reported correctly for several activities in the task. */ - @Test - public void testFindRootIndex() { - final Task task = getTestTask(); - // Add an extra activity on top of the root one - new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals("The root activity in the task must be reported.", task.getChildAt(0), - task.getRootActivity( - true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); - } - - /** - * Test that root activity index is reported correctly for several activities in the task when - * the activities on the bottom are finishing. - */ - @Test - public void testFindRootIndex_finishing() { - final Task task = getTestTask(); - // Add extra two activities and mark the two on the bottom as finishing. - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.finishing = true; - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - activity1.finishing = true; - new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals("The first non-finishing activity in the task must be reported.", - task.getChildAt(2), task.getRootActivity( - true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); - } - - /** - * Test that root activity index is reported correctly for several activities in the task when - * looking for the 'effective root'. - */ - @Test - public void testFindRootIndex_effectiveRoot() { - final Task task = getTestTask(); - // Add an extra activity on top of the root one - new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals("The root activity in the task must be reported.", - task.getChildAt(0), task.getRootActivity( - false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); - } - - /** - * Test that root activity index is reported correctly when looking for the 'effective root' in - * case when bottom activities are relinquishing task identity or finishing. - */ - @Test - public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() { - final Task task = getTestTask(); - // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and - // one above as finishing. - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - activity1.finishing = true; - new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals("The first non-finishing activity and non-relinquishing task identity " - + "must be reported.", task.getChildAt(2), task.getRootActivity( - false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); - } - - /** - * Test that root activity index is reported correctly when looking for the 'effective root' - * for the case when there is only a single activity that also has relinquishTaskIdentity set. - */ - @Test - public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() { - final Task task = getTestTask(); - // Set relinquishTaskIdentity for the only activity in the task - task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - - assertEquals("The root activity in the task must be reported.", - task.getChildAt(0), task.getRootActivity( - false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); - } - - /** - * Test that the topmost activity index is reported correctly when looking for the - * 'effective root' for the case when all activities have relinquishTaskIdentity set. - */ - @Test - public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() { - final Task task = getTestTask(); - // Set relinquishTaskIdentity for all activities in the task - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - - assertEquals("The topmost activity in the task must be reported.", - task.getChildAt(task.getChildCount() - 1), task.getRootActivity( - false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); - } - - /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */ - @Test - public void testGetRootActivity() { - final Task task = getTestTask(); - // Add an extra activity on top of the root one - new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals("The root activity in the task must be reported.", - task.getBottomMostActivity(), task.getRootActivity()); - } - - /** - * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}. - */ - @Test - public void testGetRootActivity_finishing() { - final Task task = getTestTask(); - // Add an extra activity on top of the root one - new ActivityBuilder(mAtm).setTask(task).build(); - // Mark the root as finishing - task.getBottomMostActivity().finishing = true; - - assertEquals("The first non-finishing activity in the task must be reported.", - task.getChildAt(1), task.getRootActivity()); - } - - /** - * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}. - */ - @Test - public void testGetRootActivity_relinquishTaskIdentity() { - final Task task = getTestTask(); - // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY. - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - // Add an extra activity on top of the root one. - new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals("The root activity in the task must be reported.", - task.getBottomMostActivity(), task.getRootActivity()); - } - - /** - * Test that no activity is reported in {@link Task#getRootActivity()} when all activities - * in the task are finishing. - */ - @Test - public void testGetRootActivity_allFinishing() { - final Task task = getTestTask(); - // Mark the bottom-most activity as finishing. - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.finishing = true; - // Add an extra activity on top of the root one and mark it as finishing - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - activity1.finishing = true; - - assertNull("No activity must be reported if all are finishing", task.getRootActivity()); - } - - /** - * Test that first non-finishing activity is the root of task. - */ - @Test - public void testIsRootActivity() { - final Task task = getTestTask(); - // Mark the bottom-most activity as finishing. - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.finishing = true; - // Add an extra activity on top of the root one. - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - - assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask()); - assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask()); - } - - /** - * Test that if all activities in the task are finishing, then the one on the bottom is the - * root of task. - */ - @Test - public void testIsRootActivity_allFinishing() { - final Task task = getTestTask(); - // Mark the bottom-most activity as finishing. - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.finishing = true; - // Add an extra activity on top of the root one and mark it as finishing - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - activity1.finishing = true; - - assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask()); - assertFalse("Finishing activity on top must not be the root of task", - activity1.isRootOfTask()); - } - - /** - * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}. - */ - @Test - public void testGetTaskForActivity() { - final Task task0 = getTestTask(); - final ActivityRecord activity0 = task0.getBottomMostActivity(); - - final Task task1 = getTestTask(); - final ActivityRecord activity1 = task1.getBottomMostActivity(); - - assertEquals(task0.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */)); - assertEquals(task1.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */)); - } - - /** - * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing - * activity. - */ - @Test - public void testGetTaskForActivity_onlyRoot_finishing() { - final Task task = getTestTask(); - // Make the current root activity finishing - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.finishing = true; - // Add an extra activity on top - this will be the new root - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - // Add one more on top - final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals(task.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); - assertEquals(task.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); - assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, - ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); - } - - /** - * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that - * relinquishes task identity. - */ - @Test - public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() { - final Task task = getTestTask(); - // Make the current root activity relinquish task identity - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - // Add an extra activity on top - this will be the new root - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - // Add one more on top - final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals(task.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); - assertEquals(task.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); - assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, - ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); - } - - /** - * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root - * entries. - */ - @Test - public void testGetTaskForActivity_notOnlyRoot() { - final Task task = getTestTask(); - // Mark the bottom-most activity as finishing. - final ActivityRecord activity0 = task.getBottomMostActivity(); - activity0.finishing = true; - - // Add an extra activity on top of the root one and make it relinquish task identity - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; - - // Add one more activity on top - final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); - - assertEquals(task.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */)); - assertEquals(task.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */)); - assertEquals(task.mTaskId, - ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */)); - } - - /** - * Test {@link Task#updateEffectiveIntent()}. - */ - @Test - public void testUpdateEffectiveIntent() { - // Test simple case with a single activity. - final Task task = getTestTask(); - final ActivityRecord activity0 = task.getBottomMostActivity(); - - spyOn(task); - task.updateEffectiveIntent(); - verify(task).setIntent(eq(activity0)); - } - - /** - * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This - * should make the task use the second activity when updating the intent. - */ - @Test - public void testUpdateEffectiveIntent_rootFinishing() { - // Test simple case with a single activity. - final Task task = getTestTask(); - final ActivityRecord activity0 = task.getBottomMostActivity(); - // Mark the bottom-most activity as finishing. - activity0.finishing = true; - // Add an extra activity on top of the root one - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - - spyOn(task); - task.updateEffectiveIntent(); - verify(task).setIntent(eq(activity1)); - } - - /** - * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or - * relinquishing task identity. In this case the root activity should still be used when - * updating the intent (legacy behavior). - */ - @Test - public void testUpdateEffectiveIntent_allFinishing() { - // Test simple case with a single activity. - final Task task = getTestTask(); - final ActivityRecord activity0 = task.getBottomMostActivity(); - // Mark the bottom-most activity as finishing. - activity0.finishing = true; - // Add an extra activity on top of the root one and make it relinquish task identity - final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); - activity1.finishing = true; - - // Task must still update the intent using the root activity (preserving legacy behavior). - spyOn(task); - task.updateEffectiveIntent(); - verify(task).setIntent(eq(activity0)); - } - - @Test - public void testSaveLaunchingStateWhenConfigurationChanged() { - LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; - spyOn(persister); - - final Task task = getTestTask(); - task.setHasBeenVisible(false); - task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); - task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - - task.setHasBeenVisible(true); - task.onConfigurationChanged(task.getParent().getConfiguration()); - - verify(persister).saveTask(task, task.getDisplayContent()); - } - - @Test - public void testSaveLaunchingStateWhenClearingParent() { - LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; - spyOn(persister); - - final Task task = getTestTask(); - task.setHasBeenVisible(false); - task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); - task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - final DisplayContent oldDisplay = task.getDisplayContent(); - - LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams(); - params.mWindowingMode = WINDOWING_MODE_UNDEFINED; - persister.getLaunchParams(task, null, params); - assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode); - - task.setHasBeenVisible(true); - task.removeImmediately(); - - verify(persister).saveTask(task, oldDisplay); - - persister.getLaunchParams(task, null, params); - assertEquals(WINDOWING_MODE_FULLSCREEN, params.mWindowingMode); - } - - @Test - public void testNotSaveLaunchingStateNonFreeformDisplay() { - LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; - spyOn(persister); - - final Task task = getTestTask(); - task.setHasBeenVisible(false); - task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - - task.setHasBeenVisible(true); - task.onConfigurationChanged(task.getParent().getConfiguration()); - - verify(persister, never()).saveTask(same(task), any()); - } - - @Test - public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() { - LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; - spyOn(persister); - - final Task task = getTestTask(); - task.setHasBeenVisible(false); - task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); - task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED); - - task.setHasBeenVisible(true); - task.onConfigurationChanged(task.getParent().getConfiguration()); - - verify(persister, never()).saveTask(same(task), any()); - } - - @Test - public void testNotSaveLaunchingStateForNonLeafTask() { - LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; - spyOn(persister); - - final Task task = getTestTask(); - task.setHasBeenVisible(false); - task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); - task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - - final Task leafTask = createTaskInStack(task, 0 /* userId */); - - leafTask.setHasBeenVisible(true); - task.setHasBeenVisible(true); - task.onConfigurationChanged(task.getParent().getConfiguration()); - - verify(persister, never()).saveTask(same(task), any()); - verify(persister).saveTask(same(leafTask), any()); - } - - @Test - public void testNotSpecifyOrientationByFloatingTask() { - final Task task = new TaskBuilder(mSupervisor) - .setCreateActivity(true).setCreateParentTask(true).build(); - final ActivityRecord activity = task.getTopMostActivity(); - final WindowContainer<?> parentContainer = task.getParent(); - final TaskDisplayArea taskDisplayArea = task.getDisplayArea(); - activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation()); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); - - task.setWindowingMode(WINDOWING_MODE_PINNED); - - // TDA returns the last orientation when child returns UNSET - assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation()); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); - } - - @Test - public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() { - final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( - mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", - FEATURE_VENDOR_FIRST); - final Task firstStack = firstTaskDisplayArea.createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task secondStack = secondTaskDisplayArea.createRootTask( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(firstStack).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm) - .setTask(secondStack).build(); - firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - - // Activity on TDA1 is focused - mDisplayContent.setFocusedApp(firstActivity); - - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); - assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); - - // No focused app, TDA1 is still recorded as last focused. - mDisplayContent.setFocusedApp(null); - - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); - assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); - - // Activity on TDA2 is focused - mDisplayContent.setFocusedApp(secondActivity); - - assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation()); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation()); - } - - @Test - public void testNotifyOrientationChangeCausedByConfigurationChange() { - final Task task = getTestTask(); - final ActivityRecord activity = task.getTopMostActivity(); - final DisplayContent display = task.getDisplayContent(); - display.setWindowingMode(WINDOWING_MODE_FREEFORM); - - activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); - verify(display).onDescendantOrientationChanged(same(task)); - reset(display); - - display.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation()); - verify(display).onDescendantOrientationChanged(same(task)); - } - - private Task getTestTask() { - final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); - return stack.getBottomMostTask(); - } - - private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, - Rect expectedConfigBounds) { - - TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); - Task stack = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); - - final Configuration parentConfig = stack.getConfiguration(); - parentConfig.windowConfiguration.setAppBounds(parentBounds); - task.setBounds(bounds); - - task.resolveOverrideConfiguration(parentConfig); - // Assert that both expected and actual are null or are equal to each other - assertEquals(expectedConfigBounds, - task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds()); - } - - private byte[] serializeToBytes(Task r) throws Exception { - try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - final TypedXmlSerializer serializer = Xml.newFastSerializer(); - serializer.setOutput(os, "UTF-8"); - serializer.startDocument(null, true); - serializer.startTag(null, TASK_TAG); - r.saveToXml(serializer); - serializer.endTag(null, TASK_TAG); - serializer.endDocument(); - - os.flush(); - return os.toByteArray(); - } - } - - private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException { - try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) { - final TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(reader); - assertEquals(XmlPullParser.START_TAG, parser.next()); - assertEquals(TASK_TAG, parser.getName()); - return Task.restoreFromXml(parser, mAtm.mTaskSupervisor); - } - } - - private Task createTask(int taskId) { - return new Task.Builder(mAtm) - .setTaskId(taskId) - .setIntent(new Intent()) - .setRealActivity(ActivityBuilder.getDefaultComponent()) - .setEffectiveUid(10050) - .buildInner(); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java deleted file mode 100644 index e58c162ceadd..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; -import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.clearInvocations; - -import android.app.WindowConfiguration; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for the {@link Task} class. - * - * Build/Install/Run: - * atest WmTests:TaskStackTests - */ -@SmallTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class TaskStackTests extends WindowTestsBase { - - @Test - public void testStackPositionChildAt() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task1 = createTaskInStack(stack, 0 /* userId */); - final Task task2 = createTaskInStack(stack, 1 /* userId */); - - // Current user task should be moved to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */); - assertEquals(stack.mChildren.get(0), task2); - assertEquals(stack.mChildren.get(1), task1); - - // Non-current user won't be moved to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */); - assertEquals(stack.mChildren.get(0), task2); - assertEquals(stack.mChildren.get(1), task1); - - // Non-leaf task should be moved to top regardless of the user id. - createTaskInStack(task2, 0 /* userId */); - createTaskInStack(task2, 1 /* userId */); - stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */); - assertEquals(stack.mChildren.get(0), task1); - assertEquals(stack.mChildren.get(1), task2); - } - - @Test - public void testClosingAppDifferentTaskOrientation() { - final ActivityRecord activity1 = createActivityRecord(mDisplayContent); - activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - - final ActivityRecord activity2 = createActivityRecord(mDisplayContent); - activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); - - final WindowContainer parent = activity1.getTask().getParent(); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation()); - mDisplayContent.mClosingApps.add(activity2); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parent.getOrientation()); - } - - @Test - public void testMoveTaskToBackDifferentTaskOrientation() { - final ActivityRecord activity1 = createActivityRecord(mDisplayContent); - activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - - final ActivityRecord activity2 = createActivityRecord(mDisplayContent); - activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); - - final WindowContainer parent = activity1.getTask().getParent(); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, parent.getOrientation()); - } - - @Test - public void testStackRemoveImmediately() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - assertEquals(stack, task.getRootTask()); - - // Remove stack and check if its child is also removed. - stack.removeImmediately(); - assertNull(stack.getDisplayContent()); - assertNull(task.getParent()); - } - - @Test - public void testRemoveContainer() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - - assertNotNull(stack); - assertNotNull(task); - stack.removeIfPossible(); - // Assert that the container was removed. - assertNull(stack.getParent()); - assertEquals(0, stack.getChildCount()); - assertNull(stack.getDisplayContent()); - assertNull(task.getDisplayContent()); - assertNull(task.getParent()); - } - - @Test - public void testRemoveContainer_deferRemoval() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - - // Stack removal is deferred if one of its child is animating. - doReturn(true).when(stack).hasWindowsAlive(); - doReturn(stack).when(task).getAnimatingContainer( - eq(TRANSITION | CHILDREN), anyInt()); - - stack.removeIfPossible(); - // For the case of deferred removal the task controller will still be connected to the its - // container until the stack window container is removed. - assertNotNull(stack.getParent()); - assertNotEquals(0, stack.getChildCount()); - assertNotNull(task); - - stack.removeImmediately(); - // After removing, the task will be isolated. - assertNull(task.getParent()); - assertEquals(0, task.getChildCount()); - } - - @Test - public void testReparent() { - // Create first stack on primary display. - final Task stack1 = createTaskStackOnDisplay(mDisplayContent); - final Task task1 = createTaskInStack(stack1, 0 /* userId */); - - // Create second display and put second stack on it. - final DisplayContent dc = createNewDisplay(); - final Task stack2 = createTaskStackOnDisplay(dc); - - // Reparent - clearInvocations(task1); // reset the number of onDisplayChanged for task. - stack1.reparent(dc.getDefaultTaskDisplayArea(), true /* onTop */); - assertEquals(dc, stack1.getDisplayContent()); - final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1); - final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2); - assertEquals(stack1PositionInParent, stack2PositionInParent + 1); - verify(task1, times(1)).onDisplayChanged(any()); - } - - @Test - public void testStackOutset() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final int stackOutset = 10; - spyOn(stack); - doReturn(stackOutset).when(stack).getTaskOutset(); - doReturn(true).when(stack).inMultiWindowMode(); - - // Mock the resolved override windowing mode to non-fullscreen - final WindowConfiguration windowConfiguration = - stack.getResolvedOverrideConfiguration().windowConfiguration; - spyOn(windowConfiguration); - doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) - .when(windowConfiguration).getWindowingMode(); - - // Prevent adjust task dimensions - doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any()); - - final Rect stackBounds = new Rect(200, 200, 800, 1000); - // Update surface position and size by the given bounds. - stack.setBounds(stackBounds); - - assertEquals(stackBounds.width() + 2 * stackOutset, stack.getLastSurfaceSize().x); - assertEquals(stackBounds.height() + 2 * stackOutset, stack.getLastSurfaceSize().y); - assertEquals(stackBounds.left - stackOutset, stack.getLastSurfacePosition().x); - assertEquals(stackBounds.top - stackOutset, stack.getLastSurfacePosition().y); - } - - @Test - public void testActivityAndTaskGetsProperType() { - final Task task1 = new TaskBuilder(mSupervisor).build(); - ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent); - - // First activity should become standard - task1.addChild(activity1, 0); - assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity1.getActivityType()); - assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType()); - - // Second activity should also become standard - ActivityRecord activity2 = createNonAttachedActivityRecord(mDisplayContent); - task1.addChild(activity2, WindowContainer.POSITION_TOP); - assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, activity2.getActivityType()); - assertEquals(WindowConfiguration.ACTIVITY_TYPE_STANDARD, task1.getActivityType()); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index dca6b089d66b..2389d2d6e8d6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -16,20 +16,39 @@ package com.android.server.wm; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; +import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_90; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; import static com.google.common.truth.Truth.assertThat; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -39,18 +58,45 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import android.app.ActivityManager; +import android.app.TaskInfo; import android.app.WindowConfiguration; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.util.DisplayMetrics; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; +import android.util.Xml; +import android.view.DisplayInfo; -import androidx.test.filters.SmallTest; +import androidx.test.filters.MediumTest; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; /** * Test class for {@link Task}. @@ -58,15 +104,25 @@ import org.junit.runner.RunWith; * Build/Install/Run: * atest WmTests:TaskTests */ -@SmallTest +@MediumTest @Presubmit @RunWith(WindowTestRunner.class) public class TaskTests extends WindowTestsBase { + private static final String TASK_TAG = "task"; + + private Rect mParentBounds; + + @Before + public void setUp() throws Exception { + mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/); + removeGlobalMinSizeRestriction(); + } + @Test public void testRemoveContainer() { - final Task stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final Task taskController1 = createTask(mDisplayContent); + final Task task = createTaskInRootTask(taskController1, 0 /* userId */); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); task.removeIfPossible(); @@ -78,8 +134,8 @@ public class TaskTests extends WindowTestsBase { @Test public void testRemoveContainer_deferRemoval() { - final Task stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final Task taskController1 = createTask(mDisplayContent); + final Task task = createTaskInRootTask(taskController1, 0 /* userId */); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); doReturn(true).when(task).shouldDeferRemoval(); @@ -99,14 +155,14 @@ public class TaskTests extends WindowTestsBase { @Test public void testReparent() { - final Task stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); - final Task stackController2 = createTaskStackOnDisplay(mDisplayContent); - final Task task2 = createTaskInStack(stackController2, 0 /* userId */); + final Task taskController1 = createTask(mDisplayContent); + final Task task = createTaskInRootTask(taskController1, 0 /* userId */); + final Task taskController2 = createTask(mDisplayContent); + final Task task2 = createTaskInRootTask(taskController2, 0 /* userId */); boolean gotException = false; try { - task.reparent(stackController1, 0, false/* moveParents */, "testReparent"); + task.reparent(taskController1, 0, false/* moveParents */, "testReparent"); } catch (IllegalArgumentException e) { gotException = true; } @@ -118,29 +174,29 @@ public class TaskTests extends WindowTestsBase { } catch (Exception e) { gotException = true; } - assertTrue("Should not be able to reparent to a stack that doesn't exist", gotException); + assertTrue("Should not be able to reparent to a task that doesn't exist", gotException); - task.reparent(stackController2, 0, false/* moveParents */, "testReparent"); - assertEquals(stackController2, task.getParent()); + task.reparent(taskController2, 0, false/* moveParents */, "testReparent"); + assertEquals(taskController2, task.getParent()); assertEquals(0, task.getParent().mChildren.indexOf(task)); assertEquals(1, task2.getParent().mChildren.indexOf(task2)); } @Test public void testReparent_BetweenDisplays() { - // Create first stack on primary display. - final Task stack1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack1, 0 /* userId */); - assertEquals(mDisplayContent, stack1.getDisplayContent()); + // Create first task on primary display. + final Task rootTask1 = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask1, 0 /* userId */); + assertEquals(mDisplayContent, rootTask1.getDisplayContent()); - // Create second display and put second stack on it. + // Create second display and put second task on it. final DisplayContent dc = createNewDisplay(); - final Task stack2 = createTaskStackOnDisplay(dc); - final Task task2 = createTaskInStack(stack2, 0 /* userId */); + final Task rootTask2 = createTask(dc); + final Task task2 = createTaskInRootTask(rootTask2, 0 /* userId */); // Reparent and check state clearInvocations(task); // reset the number of onDisplayChanged for task. - task.reparent(stack2, 0, false /* moveParents */, "testReparent_BetweenDisplays"); - assertEquals(stack2, task.getParent()); + task.reparent(rootTask2, 0, false /* moveParents */, "testReparent_BetweenDisplays"); + assertEquals(rootTask2, task.getParent()); assertEquals(0, task.getParent().mChildren.indexOf(task)); assertEquals(1, task2.getParent().mChildren.indexOf(task2)); verify(task, times(1)).onDisplayChanged(any()); @@ -148,8 +204,8 @@ public class TaskTests extends WindowTestsBase { @Test public void testBounds() { - final Task stack1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack1, 0 /* userId */); + final Task rootTask1 = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask1, 0 /* userId */); // Check that setting bounds also updates surface position task.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -159,9 +215,9 @@ public class TaskTests extends WindowTestsBase { } @Test - public void testIsInStack() { - final Task task1 = createTaskStackOnDisplay(mDisplayContent); - final Task task2 = createTaskStackOnDisplay(mDisplayContent); + public void testIsInTask() { + final Task task1 = createTask(mDisplayContent); + final Task task2 = createTask(mDisplayContent); final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task1); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task2); assertEquals(activity1, task1.isInTask(activity1)); @@ -170,7 +226,7 @@ public class TaskTests extends WindowTestsBase { @Test public void testRemoveChildForOverlayTask() { - final Task task = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTask(mDisplayContent); final int taskId = task.mTaskId; final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task); @@ -193,10 +249,10 @@ public class TaskTests extends WindowTestsBase { @Test public void testSwitchUser() { - final Task rootTask = createTaskStackOnDisplay(mDisplayContent); - final Task childTask = createTaskInStack(rootTask, 0 /* userId */); - final Task leafTask1 = createTaskInStack(childTask, 10 /* userId */); - final Task leafTask2 = createTaskInStack(childTask, 0 /* userId */); + final Task rootTask = createTask(mDisplayContent); + final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */); + final Task leafTask1 = createTaskInRootTask(childTask, 10 /* userId */); + final Task leafTask2 = createTaskInRootTask(childTask, 0 /* userId */); assertEquals(1, rootTask.getChildCount()); assertEquals(leafTask2, childTask.getTopChild()); @@ -208,9 +264,9 @@ public class TaskTests extends WindowTestsBase { @Test public void testEnsureActivitiesVisible() { - final Task rootTask = createTaskStackOnDisplay(mDisplayContent); - final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */); - final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */); + final Task rootTask = createTask(mDisplayContent); + final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */); + final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */); final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask1); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask2); @@ -233,7 +289,7 @@ public class TaskTests extends WindowTestsBase { @Test public void testResolveNonResizableTaskWindowingMode() { - final Task task = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTask(mDisplayContent); Configuration parentConfig = task.getParent().getConfiguration(); parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); doReturn(false).when(task).isResizeable(); @@ -264,10 +320,10 @@ public class TaskTests extends WindowTestsBase { @Test public void testHandlesOrientationChangeFromDescendant() { - final Task rootTask = createTaskStackOnDisplay(WINDOWING_MODE_MULTI_WINDOW, - ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */); - final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */); + final Task rootTask = createTask(mDisplayContent, + WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */); + final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */); leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME); leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD); @@ -281,7 +337,7 @@ public class TaskTests extends WindowTestsBase { @Test public void testAlwaysOnTop() { - final Task task = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTask(mDisplayContent); task.setAlwaysOnTop(true); task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); assertTrue(task.isAlwaysOnTop()); @@ -289,4 +345,1053 @@ public class TaskTests extends WindowTestsBase { task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */); assertFalse(task.isAlwaysOnTop()); } + + @Test + public void testRestoreWindowedTask() throws Exception { + final Task expected = createTask(64); + expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100); + + final byte[] serializedBytes = serializeToBytes(expected); + final Task actual = restoreFromBytes(serializedBytes); + assertEquals(expected.mTaskId, actual.mTaskId); + assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds); + } + + /** Ensure we have no chance to modify the original intent. */ + @Test + public void testCopyBaseIntentForTaskInfo() { + final Task task = createTask(1); + task.setTaskDescription(new ActivityManager.TaskDescription()); + final TaskInfo info = task.getTaskInfo(); + + // The intent of info should be a copy so assert that they are different instances. + Assert.assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent()))); + } + + @Test + public void testReturnsToHomeRootTask() throws Exception { + final Task task = createTask(1); + spyOn(task); + doReturn(true).when(task).hasChild(); + assertFalse(task.returnsToHomeRootTask()); + task.intent = null; + assertFalse(task.returnsToHomeRootTask()); + task.intent = new Intent(); + assertFalse(task.returnsToHomeRootTask()); + task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME); + assertTrue(task.returnsToHomeRootTask()); + } + + /** Ensures that empty bounds cause appBounds to inherit from parent. */ + @Test + public void testAppBounds_EmptyBounds() { + final Rect emptyBounds = new Rect(); + testRootTaskBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds, + mParentBounds); + } + + /** Ensures that bounds on freeform root tasks are not clipped. */ + @Test + public void testAppBounds_FreeFormBounds() { + final Rect freeFormBounds = new Rect(mParentBounds); + freeFormBounds.offset(10, 10); + testRootTaskBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds, + freeFormBounds); + } + + /** Ensures that fully contained bounds are not clipped. */ + @Test + public void testAppBounds_ContainedBounds() { + final Rect insetBounds = new Rect(mParentBounds); + insetBounds.inset(5, 5, 5, 5); + testRootTaskBoundsConfiguration( + WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds); + } + + @Test + public void testFitWithinBounds() { + final Rect parentBounds = new Rect(10, 10, 200, 200); + TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); + Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + final Configuration parentConfig = rootTask.getConfiguration(); + parentConfig.windowConfiguration.setBounds(parentBounds); + parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT; + + // check top and left + Rect reqBounds = new Rect(-190, -190, 0, 0); + task.setBounds(reqBounds); + // Make sure part of it is exposed + assertTrue(task.getBounds().right > parentBounds.left); + assertTrue(task.getBounds().bottom > parentBounds.top); + // Should still be more-or-less in that corner + assertTrue(task.getBounds().left <= parentBounds.left); + assertTrue(task.getBounds().top <= parentBounds.top); + + assertEquals(reqBounds.width(), task.getBounds().width()); + assertEquals(reqBounds.height(), task.getBounds().height()); + + // check bottom and right + reqBounds = new Rect(210, 210, 400, 400); + task.setBounds(reqBounds); + // Make sure part of it is exposed + assertTrue(task.getBounds().left < parentBounds.right); + assertTrue(task.getBounds().top < parentBounds.bottom); + // Should still be more-or-less in that corner + assertTrue(task.getBounds().right >= parentBounds.right); + assertTrue(task.getBounds().bottom >= parentBounds.bottom); + + assertEquals(reqBounds.width(), task.getBounds().width()); + assertEquals(reqBounds.height(), task.getBounds().height()); + } + + /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */ + @Test + public void testBoundsOnModeChangeFreeformToFullscreen() { + DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay(); + Task rootTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true) + .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); + Task task = rootTask.getBottomMostTask(); + task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); + DisplayInfo info = new DisplayInfo(); + display.mDisplay.getDisplayInfo(info); + final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight); + final Rect freeformBounds = new Rect(fullScreenBounds); + freeformBounds.inset((int) (freeformBounds.width() * 0.2), + (int) (freeformBounds.height() * 0.2)); + task.setBounds(freeformBounds); + + assertEquals(freeformBounds, task.getBounds()); + + // FULLSCREEN inherits bounds + rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertEquals(fullScreenBounds, task.getBounds()); + assertEquals(freeformBounds, task.mLastNonFullscreenBounds); + + // FREEFORM restores bounds + rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(freeformBounds, task.getBounds()); + } + + /** + * Tests that a task with forced orientation has orientation-consistent bounds within the + * parent. + */ + @Test + public void testFullscreenBoundsForcedOrientation() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); + final DisplayContent display = new TestDisplayContent.Builder(mAtm, + fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); + // Fix the display orientation to landscape which is the natural rotation (0) for the test + // display. + final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); + dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); + + final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); + final Task task = rootTask.getBottomMostTask(); + final ActivityRecord root = task.getTopNonFinishingActivity(); + + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed portrait fits within parent + root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertEquals(root, task.getRootActivity()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); + // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds. + assertThat(task.getBounds().height()).isLessThan(task.getBounds().width()); + assertEquals(fullScreenBounds, task.getBounds()); + + // Top activity gets used + final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(rootTask) + .build(); + assertEquals(top, task.getTopNonFinishingActivity()); + top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); + assertEquals(task.getBounds().width(), fullScreenBounds.width()); + + // Setting app to unspecified restores + top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED); + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed landscape and changing display + top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + // Fix the display orientation to portrait which is 90 degrees for the test display. + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90); + + // Fixed orientation request should be resolved on activity level. Task fills display + // bounds. + assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); + assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); + assertEquals(fullScreenBoundsPort, task.getBounds()); + + // in FREEFORM, no constraint + final Rect freeformBounds = new Rect(display.getBounds()); + freeformBounds.inset((int) (freeformBounds.width() * 0.2), + (int) (freeformBounds.height() * 0.2)); + rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + task.setBounds(freeformBounds); + assertEquals(freeformBounds, task.getBounds()); + + // FULLSCREEN letterboxes bounds on activity level, no constraint on task level. + rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); + assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); + assertEquals(fullScreenBoundsPort, task.getBounds()); + + // FREEFORM restores bounds as before + rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(freeformBounds, task.getBounds()); + } + + @Test + public void testReportsOrientationRequestInLetterboxForOrientation() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); + final DisplayContent display = new TestDisplayContent.Builder(mAtm, + fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); + // Fix the display orientation to landscape which is the natural rotation (0) for the test + // display. + final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); + dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); + + final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); + final Task task = rootTask.getBottomMostTask(); + ActivityRecord root = task.getTopNonFinishingActivity(); + + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed portrait fits within parent on activity level. Task fills parent. + root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertThat(root.getBounds().width()).isLessThan(root.getBounds().height()); + assertEquals(task.getBounds(), fullScreenBounds); + + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation()); + } + + @Test + public void testIgnoresForcedOrientationWhenParentHandles() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + DisplayContent display = new TestDisplayContent.Builder( + mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); + + display.getRequestedOverrideConfiguration().orientation = + Configuration.ORIENTATION_LANDSCAPE; + display.onRequestedOverrideConfigurationChanged( + display.getRequestedOverrideConfiguration()); + Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); + Task task = rootTask.getBottomMostTask(); + ActivityRecord root = task.getTopNonFinishingActivity(); + + final WindowContainer parentWindowContainer = + new WindowContainer(mSystemServicesTestRule.getWindowManagerService()); + spyOn(parentWindowContainer); + parentWindowContainer.setBounds(fullScreenBounds); + doReturn(parentWindowContainer).when(task).getParent(); + doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea(); + doReturn(rootTask).when(task).getRootTask(); + doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant(); + + // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the + // bounds because its parent says it will handle it at a later time. + root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertEquals(root, task.getRootActivity()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); + assertEquals(fullScreenBounds, task.getBounds()); + } + + @Test + public void testComputeConfigResourceOverrides() { + final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920); + TestDisplayContent display = new TestDisplayContent.Builder( + mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); + final Configuration inOutConfig = new Configuration(); + final Configuration parentConfig = new Configuration(); + final int longSide = 1200; + final int shortSide = 600; + final Rect parentBounds = new Rect(0, 0, 250, 500); + final Rect parentAppBounds = new Rect(0, 0, 250, 480); + parentConfig.windowConfiguration.setBounds(parentBounds); + parentConfig.windowConfiguration.setAppBounds(parentAppBounds); + parentConfig.densityDpi = 400; + parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200 + parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100 + parentConfig.windowConfiguration.setRotation(ROTATION_0); + + // By default, the input bounds will fill parent. + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); + assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); + assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds()); + assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation); + + // If bounds are overridden, config properties should be made to match. Surface hierarchy + // will crop for policy. + inOutConfig.setToDefaults(); + final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide); + inOutConfig.windowConfiguration.setBounds(largerPortraitBounds); + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + // The override bounds are beyond the parent, the out appBounds should not be intersected + // by parent appBounds. + assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds()); + assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160); + assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160); + + inOutConfig.setToDefaults(); + // Landscape bounds. + final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide); + inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds); + + // Setup the display with a top stable inset. The later assertion will ensure the inset is + // excluded from screenHeightDp. + final int statusBarHeight = 100; + final DisplayPolicy policy = display.getDisplayPolicy(); + doAnswer(invocationOnMock -> { + final Rect insets = invocationOnMock.<Rect>getArgument(0); + insets.top = statusBarHeight; + return null; + }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0)); + + // Without limiting to be inside the parent bounds, the out screen size should keep relative + // to the input bounds. + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord.CompatDisplayInsets compatIntsets = + new ActivityRecord.CompatDisplayInsets( + display, activity, /* fixedOrientationBounds= */ null); + task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets); + + assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); + assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi, + inOutConfig.screenHeightDp); + assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi, + inOutConfig.screenWidthDp); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation); + } + + @Test + public void testComputeConfigResourceLayoutOverrides() { + final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500); + TestDisplayContent display = new TestDisplayContent.Builder( + mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); + final Configuration inOutConfig = new Configuration(); + final Configuration parentConfig = new Configuration(); + final Rect nonLongBounds = new Rect(0, 0, 1000, 1250); + parentConfig.windowConfiguration.setBounds(fullScreenBounds); + parentConfig.windowConfiguration.setAppBounds(fullScreenBounds); + parentConfig.densityDpi = 400; + parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi; + parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi; + parentConfig.windowConfiguration.setRotation(ROTATION_0); + + // Set BOTH screenW/H to an override value + inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi; + inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi; + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + // screenLayout should honor override when both screenW/H are set. + assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0); + } + + @Test + public void testComputeNestedConfigResourceOverrides() { + final Task task = new TaskBuilder(mSupervisor).build(); + assertTrue(task.getResolvedOverrideBounds().isEmpty()); + int origScreenH = task.getConfiguration().screenHeightDp; + Configuration rootTaskConfig = new Configuration(); + rootTaskConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration()); + rootTaskConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + + // Set bounds on root task (not task) and verify that the task resource configuration + // changes despite it's override bounds being empty. + Rect bounds = new Rect(task.getRootTask().getBounds()); + bounds.bottom = (int) (bounds.bottom * 0.6f); + rootTaskConfig.windowConfiguration.setBounds(bounds); + task.getRootTask().onRequestedOverrideConfigurationChanged(rootTaskConfig); + assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp); + } + + @Test + public void testFullScreenTaskNotAdjustedByMinimalSize() { + final Task fullscreenTask = new TaskBuilder(mSupervisor).build(); + final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds()); + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */, + 0 /* height */, 0 /* heightFraction */, 0 /* gravity */, + originalTaskBounds.width() * 2 /* minWidth */, + originalTaskBounds.height() * 2 /* minHeight */); + fullscreenTask.setMinDimensions(aInfo); + fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration()); + + assertEquals(originalTaskBounds, fullscreenTask.getBounds()); + } + + @Test + public void testInsetDisregardedWhenFreeformOverlapsNavBar() { + TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); + Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + DisplayInfo displayInfo = new DisplayInfo(); + mAtm.mContext.getDisplay().getDisplayInfo(displayInfo); + final int displayHeight = displayInfo.logicalHeight; + final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + final Configuration inOutConfig = new Configuration(); + final Configuration parentConfig = new Configuration(); + final int longSide = 1200; + final int shortSide = 600; + parentConfig.densityDpi = 400; + parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px + parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px + parentConfig.windowConfiguration.setRotation(ROTATION_0); + + final int longSideDp = 480; // longSide / density = 1200 / 400 * 160 + final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160 + final int screenLayout = parentConfig.screenLayout + & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK); + final int reducedScreenLayout = + Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp); + + // Portrait bounds overlapping with navigation bar, without insets. + final Rect freeformBounds = new Rect(0, + displayHeight - 10 - longSide, + shortSide, + displayHeight - 10); + inOutConfig.windowConfiguration.setBounds(freeformBounds); + // Set to freeform mode to verify bug fix. + inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + // screenW/H should not be effected by parent since overridden and freeform + assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi, + inOutConfig.screenWidthDp); + assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi, + inOutConfig.screenHeightDp); + assertEquals(reducedScreenLayout, inOutConfig.screenLayout); + + inOutConfig.setToDefaults(); + // Landscape bounds overlapping with navigtion bar, without insets. + freeformBounds.set(0, + displayHeight - 10 - shortSide, + longSide, + displayHeight - 10); + inOutConfig.windowConfiguration.setBounds(freeformBounds); + inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi, + inOutConfig.screenWidthDp); + assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi, + inOutConfig.screenHeightDp); + assertEquals(reducedScreenLayout, inOutConfig.screenLayout); + } + + /** Ensures that the alias intent won't have target component resolved. */ + @Test + public void testTaskIntentActivityAlias() { + final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity"; + final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity"; + final ComponentName aliasComponent = + new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName); + final ComponentName targetComponent = + new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName); + + final Intent intent = new Intent(); + intent.setComponent(aliasComponent); + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME; + info.targetActivity = targetClassName; + + final Task task = new Task.Builder(mAtm) + .setTaskId(1) + .setActivityInfo(info) + .setIntent(intent) + .build(); + assertEquals("The alias activity component should be saved in task intent.", aliasClassName, + task.intent.getComponent().getClassName()); + + ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent( + aliasComponent).setTargetActivity(targetClassName).build(); + assertEquals("Should be the same intent filter.", true, + task.isSameIntentFilter(aliasActivity)); + + ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent( + targetComponent).build(); + assertEquals("Should be the same intent filter.", true, + task.isSameIntentFilter(targetActivity)); + + ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build(); + assertEquals("Should not be the same intent filter.", false, + task.isSameIntentFilter(defaultActivity)); + } + + /** Test that root activity index is reported correctly for several activities in the task. */ + @Test + public void testFindRootIndex() { + final Task task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", task.getChildAt(0), + task.getRootActivity( + true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); + } + + /** + * Test that root activity index is reported correctly for several activities in the task when + * the activities on the bottom are finishing. + */ + @Test + public void testFindRootIndex_finishing() { + final Task task = getTestTask(); + // Add extra two activities and mark the two on the bottom as finishing. + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.finishing = true; + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.finishing = true; + new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals("The first non-finishing activity in the task must be reported.", + task.getChildAt(2), task.getRootActivity( + true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); + } + + /** + * Test that root activity index is reported correctly for several activities in the task when + * looking for the 'effective root'. + */ + @Test + public void testFindRootIndex_effectiveRoot() { + final Task task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", + task.getChildAt(0), task.getRootActivity( + false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); + } + + /** + * Test that root activity index is reported correctly when looking for the 'effective root' in + * case when bottom activities are relinquishing task identity or finishing. + */ + @Test + public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() { + final Task task = getTestTask(); + // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and + // one above as finishing. + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.finishing = true; + new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals("The first non-finishing activity and non-relinquishing task identity " + + "must be reported.", task.getChildAt(2), task.getRootActivity( + false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); + } + + /** + * Test that root activity index is reported correctly when looking for the 'effective root' + * for the case when there is only a single activity that also has relinquishTaskIdentity set. + */ + @Test + public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() { + final Task task = getTestTask(); + // Set relinquishTaskIdentity for the only activity in the task + task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + assertEquals("The root activity in the task must be reported.", + task.getChildAt(0), task.getRootActivity( + false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); + } + + /** + * Test that the topmost activity index is reported correctly when looking for the + * 'effective root' for the case when all activities have relinquishTaskIdentity set. + */ + @Test + public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() { + final Task task = getTestTask(); + // Set relinquishTaskIdentity for all activities in the task + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + assertEquals("The topmost activity in the task must be reported.", + task.getChildAt(task.getChildCount() - 1), task.getRootActivity( + false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); + } + + /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */ + @Test + public void testGetRootActivity() { + final Task task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", + task.getBottomMostActivity(), task.getRootActivity()); + } + + /** + * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}. + */ + @Test + public void testGetRootActivity_finishing() { + final Task task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mAtm).setTask(task).build(); + // Mark the root as finishing + task.getBottomMostActivity().finishing = true; + + assertEquals("The first non-finishing activity in the task must be reported.", + task.getChildAt(1), task.getRootActivity()); + } + + /** + * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}. + */ + @Test + public void testGetRootActivity_relinquishTaskIdentity() { + final Task task = getTestTask(); + // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY. + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + // Add an extra activity on top of the root one. + new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", + task.getBottomMostActivity(), task.getRootActivity()); + } + + /** + * Test that no activity is reported in {@link Task#getRootActivity()} when all activities + * in the task are finishing. + */ + @Test + public void testGetRootActivity_allFinishing() { + final Task task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.finishing = true; + // Add an extra activity on top of the root one and mark it as finishing + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.finishing = true; + + assertNull("No activity must be reported if all are finishing", task.getRootActivity()); + } + + /** + * Test that first non-finishing activity is the root of task. + */ + @Test + public void testIsRootActivity() { + final Task task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.finishing = true; + // Add an extra activity on top of the root one. + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + + assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask()); + assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask()); + } + + /** + * Test that if all activities in the task are finishing, then the one on the bottom is the + * root of task. + */ + @Test + public void testIsRootActivity_allFinishing() { + final Task task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.finishing = true; + // Add an extra activity on top of the root one and mark it as finishing + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.finishing = true; + + assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask()); + assertFalse("Finishing activity on top must not be the root of task", + activity1.isRootOfTask()); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}. + */ + @Test + public void testGetTaskForActivity() { + final Task task0 = getTestTask(); + final ActivityRecord activity0 = task0.getBottomMostActivity(); + + final Task task1 = getTestTask(); + final ActivityRecord activity1 = task1.getBottomMostActivity(); + + assertEquals(task0.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */)); + assertEquals(task1.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */)); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing + * activity. + */ + @Test + public void testGetTaskForActivity_onlyRoot_finishing() { + final Task task = getTestTask(); + // Make the current root activity finishing + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.finishing = true; + // Add an extra activity on top - this will be the new root + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + // Add one more on top + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals(task.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); + assertEquals(task.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); + assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, + ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that + * relinquishes task identity. + */ + @Test + public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() { + final Task task = getTestTask(); + // Make the current root activity relinquish task identity + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + // Add an extra activity on top - this will be the new root + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + // Add one more on top + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals(task.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); + assertEquals(task.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); + assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, + ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root + * entries. + */ + @Test + public void testGetTaskForActivity_notOnlyRoot() { + final Task task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getBottomMostActivity(); + activity0.finishing = true; + + // Add an extra activity on top of the root one and make it relinquish task identity + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + // Add one more activity on top + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); + + assertEquals(task.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */)); + assertEquals(task.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */)); + assertEquals(task.mTaskId, + ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */)); + } + + /** + * Test {@link Task#updateEffectiveIntent()}. + */ + @Test + public void testUpdateEffectiveIntent() { + // Test simple case with a single activity. + final Task task = getTestTask(); + final ActivityRecord activity0 = task.getBottomMostActivity(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity0)); + } + + /** + * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This + * should make the task use the second activity when updating the intent. + */ + @Test + public void testUpdateEffectiveIntent_rootFinishing() { + // Test simple case with a single activity. + final Task task = getTestTask(); + final ActivityRecord activity0 = task.getBottomMostActivity(); + // Mark the bottom-most activity as finishing. + activity0.finishing = true; + // Add an extra activity on top of the root one + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity1)); + } + + /** + * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or + * relinquishing task identity. In this case the root activity should still be used when + * updating the intent (legacy behavior). + */ + @Test + public void testUpdateEffectiveIntent_allFinishing() { + // Test simple case with a single activity. + final Task task = getTestTask(); + final ActivityRecord activity0 = task.getBottomMostActivity(); + // Mark the bottom-most activity as finishing. + activity0.finishing = true; + // Add an extra activity on top of the root one and make it relinquish task identity + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); + activity1.finishing = true; + + // Task must still update the intent using the root activity (preserving legacy behavior). + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity0)); + } + + @Test + public void testSaveLaunchingStateWhenConfigurationChanged() { + LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; + spyOn(persister); + + final Task task = getTestTask(); + task.setHasBeenVisible(false); + task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); + task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + task.setHasBeenVisible(true); + task.onConfigurationChanged(task.getParent().getConfiguration()); + + verify(persister).saveTask(task, task.getDisplayContent()); + } + + @Test + public void testSaveLaunchingStateWhenClearingParent() { + LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; + spyOn(persister); + + final Task task = getTestTask(); + task.setHasBeenVisible(false); + task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final DisplayContent oldDisplay = task.getDisplayContent(); + + LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams(); + params.mWindowingMode = WINDOWING_MODE_UNDEFINED; + persister.getLaunchParams(task, null, params); + assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode); + + task.setHasBeenVisible(true); + task.removeImmediately(); + + verify(persister).saveTask(task, oldDisplay); + + persister.getLaunchParams(task, null, params); + assertEquals(WINDOWING_MODE_FULLSCREEN, params.mWindowingMode); + } + + @Test + public void testNotSaveLaunchingStateNonFreeformDisplay() { + LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; + spyOn(persister); + + final Task task = getTestTask(); + task.setHasBeenVisible(false); + task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + task.setHasBeenVisible(true); + task.onConfigurationChanged(task.getParent().getConfiguration()); + + Mockito.verify(persister, never()).saveTask(same(task), any()); + } + + @Test + public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() { + LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; + spyOn(persister); + + final Task task = getTestTask(); + task.setHasBeenVisible(false); + task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); + task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED); + + task.setHasBeenVisible(true); + task.onConfigurationChanged(task.getParent().getConfiguration()); + + Mockito.verify(persister, never()).saveTask(same(task), any()); + } + + @Test + public void testNotSaveLaunchingStateForNonLeafTask() { + LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; + spyOn(persister); + + final Task task = getTestTask(); + task.setHasBeenVisible(false); + task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); + task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + final Task leafTask = createTaskInRootTask(task, 0 /* userId */); + + leafTask.setHasBeenVisible(true); + task.setHasBeenVisible(true); + task.onConfigurationChanged(task.getParent().getConfiguration()); + + Mockito.verify(persister, never()).saveTask(same(task), any()); + verify(persister).saveTask(same(leafTask), any()); + } + + @Test + public void testNotSpecifyOrientationByFloatingTask() { + final Task task = new TaskBuilder(mSupervisor) + .setCreateActivity(true).setCreateParentTask(true).build(); + final ActivityRecord activity = task.getTopMostActivity(); + final WindowContainer<?> parentContainer = task.getParent(); + final TaskDisplayArea taskDisplayArea = task.getDisplayArea(); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); + + task.setWindowingMode(WINDOWING_MODE_PINNED); + + // TDA returns the last orientation when child returns UNSET + assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); + } + + @Test + public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() { + final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( + mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", + FEATURE_VENDOR_FIRST); + final Task firstRootTask = firstTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final Task secondRootTask = secondTaskDisplayArea.createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm) + .setTask(firstRootTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setTask(secondRootTask).build(); + firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + + // Activity on TDA1 is focused + mDisplayContent.setFocusedApp(firstActivity); + + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); + + // No focused app, TDA1 is still recorded as last focused. + mDisplayContent.setFocusedApp(null); + + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); + + // Activity on TDA2 is focused + mDisplayContent.setFocusedApp(secondActivity); + + assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation()); + } + + @Test + public void testNotifyOrientationChangeCausedByConfigurationChange() { + final Task task = getTestTask(); + final ActivityRecord activity = task.getTopMostActivity(); + final DisplayContent display = task.getDisplayContent(); + display.setWindowingMode(WINDOWING_MODE_FREEFORM); + + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); + verify(display).onDescendantOrientationChanged(same(task)); + reset(display); + + display.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation()); + verify(display).onDescendantOrientationChanged(same(task)); + } + + private Task getTestTask() { + final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); + return task.getBottomMostTask(); + } + + private void testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, + Rect expectedConfigBounds) { + + TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); + Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); + + final Configuration parentConfig = rootTask.getConfiguration(); + parentConfig.windowConfiguration.setAppBounds(parentBounds); + task.setBounds(bounds); + + task.resolveOverrideConfiguration(parentConfig); + // Assert that both expected and actual are null or are equal to each other + assertEquals(expectedConfigBounds, + task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds()); + } + + private byte[] serializeToBytes(Task r) throws Exception { + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + final TypedXmlSerializer serializer = Xml.newFastSerializer(); + serializer.setOutput(os, "UTF-8"); + serializer.startDocument(null, true); + serializer.startTag(null, TASK_TAG); + r.saveToXml(serializer); + serializer.endTag(null, TASK_TAG); + serializer.endDocument(); + + os.flush(); + return os.toByteArray(); + } + } + + private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException { + try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) { + final TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(reader); + assertEquals(XmlPullParser.START_TAG, parser.next()); + assertEquals(TASK_TAG, parser.getName()); + return Task.restoreFromXml(parser, mAtm.mTaskSupervisor); + } + } + + private Task createTask(int taskId) { + return new Task.Builder(mAtm) + .setTaskId(taskId) + .setIntent(new Intent()) + .setRealActivity(ActivityBuilder.getDefaultComponent()) + .setEffectiveUid(10050) + .buildInner(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 79ef8680dfec..2dfb3a1a84bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -69,10 +69,8 @@ public class TransitionTests extends WindowTestsBase { ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; ArraySet<WindowContainer> participants = transition.mParticipants; - final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task oldTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task newTask = createTask(mDisplayContent); + final Task oldTask = createTask(mDisplayContent); final ActivityRecord closing = createActivityRecord(oldTask); final ActivityRecord opening = createActivityRecord(newTask); // Start states. @@ -128,12 +126,10 @@ public class TransitionTests extends WindowTestsBase { ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; ArraySet<WindowContainer> participants = transition.mParticipants; - final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task newNestedTask = createTaskInStack(newTask, 0); - final Task newNestedTask2 = createTaskInStack(newTask, 0); - final Task oldTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task newTask = createTask(mDisplayContent); + final Task newNestedTask = createTaskInRootTask(newTask, 0); + final Task newNestedTask2 = createTaskInRootTask(newTask, 0); + final Task oldTask = createTask(mDisplayContent); final ActivityRecord closing = createActivityRecord(oldTask); final ActivityRecord opening = createActivityRecord(newNestedTask); final ActivityRecord opening2 = createActivityRecord(newNestedTask2); @@ -179,11 +175,9 @@ public class TransitionTests extends WindowTestsBase { final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; ArraySet<WindowContainer> participants = transition.mParticipants; - final Task showTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task showNestedTask = createTaskInStack(showTask, 0); - final Task showTask2 = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task showTask = createTask(mDisplayContent); + final Task showNestedTask = createTaskInRootTask(showTask, 0); + final Task showTask2 = createTask(mDisplayContent); final DisplayArea tda = showTask.getDisplayArea(); final ActivityRecord showing = createActivityRecord(showNestedTask); final ActivityRecord showing2 = createActivityRecord(showTask2); @@ -231,12 +225,10 @@ public class TransitionTests extends WindowTestsBase { public void testCreateInfo_existenceChange() { final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); - final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task openTask = createTask(mDisplayContent); final ActivityRecord opening = createActivityRecord(openTask); opening.mVisibleRequested = false; // starts invisible - final Task closeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task closeTask = createTask(mDisplayContent); final ActivityRecord closing = createActivityRecord(closeTask); closing.mVisibleRequested = true; // starts visible @@ -268,8 +260,8 @@ public class TransitionTests extends WindowTestsBase { final Task[] tasks = new Task[taskCount]; for (int i = 0; i < taskCount; ++i) { // Each add goes on top, so at the end of this, task[9] should be on top - tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FREEFORM, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + tasks[i] = createTask(mDisplayContent, + WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); final ActivityRecord act = createActivityRecord(tasks[i]); // alternate so that the transition doesn't get promoted to the display area act.mVisibleRequested = (i % 2) == 0; // starts invisible @@ -305,8 +297,8 @@ public class TransitionTests extends WindowTestsBase { final Task[] tasks = new Task[taskCount]; for (int i = 0; i < taskCount; ++i) { // Each add goes on top, so at the end of this, task[9] should be on top - tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + tasks[i] = createTask(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); final ActivityRecord act = createActivityRecord(tasks[i]); // alternate so that the transition doesn't get promoted to the display area act.mVisibleRequested = (i % 2) == 0; // starts invisible @@ -353,15 +345,13 @@ public class TransitionTests extends WindowTestsBase { ArraySet<WindowContainer> participants = transition.mParticipants; ITaskOrganizer mockOrg = mock(ITaskOrganizer.class); - final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task openInOpenTask = createTaskInStack(openTask, 0); + final Task openTask = createTask(mDisplayContent); + final Task openInOpenTask = createTaskInRootTask(openTask, 0); final ActivityRecord openInOpen = createActivityRecord(openInOpenTask); - final Task changeTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task changeInChangeTask = createTaskInStack(changeTask, 0); - final Task openInChangeTask = createTaskInStack(changeTask, 0); + final Task changeTask = createTask(mDisplayContent); + final Task changeInChangeTask = createTaskInRootTask(changeTask, 0); + final Task openInChangeTask = createTaskInRootTask(changeTask, 0); final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask); final ActivityRecord openInChange = createActivityRecord(openInChangeTask); // set organizer for everything so that they all get added to transition info diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index b88173d5c1f0..6919c4cf6a7c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -811,18 +811,18 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testOnDisplayChanged() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); final DisplayContent newDc = createNewDisplay(); - stack.getDisplayArea().removeRootTask(stack); - newDc.getDefaultTaskDisplayArea().addChild(stack, POSITION_TOP); + rootTask.getDisplayArea().removeRootTask(rootTask); + newDc.getDefaultTaskDisplayArea().addChild(rootTask, POSITION_TOP); - verify(stack).onDisplayChanged(newDc); + verify(rootTask).onDisplayChanged(newDc); verify(task).onDisplayChanged(newDc); verify(activity).onDisplayChanged(newDc); - assertEquals(newDc, stack.mDisplayContent); + assertEquals(newDc, rootTask.mDisplayContent); assertEquals(newDc, task.mDisplayContent); assertEquals(newDc, activity.mDisplayContent); } @@ -854,21 +854,21 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testTaskCanApplyAnimation() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task); final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task); verifyWindowContainerApplyAnimation(task, activity1, activity2); } @Test - public void testStackCanApplyAnimation() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); + public void testRootTaskCanApplyAnimation() { + final Task rootTask = createTask(mDisplayContent); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, - createTaskInStack(stack, 0 /* userId */)); + createTaskInRootTask(rootTask, 0 /* userId */)); final ActivityRecord activity1 = createActivityRecord(mDisplayContent, - createTaskInStack(stack, 0 /* userId */)); - verifyWindowContainerApplyAnimation(stack, activity1, activity2); + createTaskInRootTask(rootTask, 0 /* userId */)); + verifyWindowContainerApplyAnimation(rootTask, activity1, activity2); } @Test @@ -878,21 +878,21 @@ public class WindowContainerTests extends WindowTestsBase { assertNull(windowContainer.getDisplayArea()); - // ActivityStack > WindowContainer - final Task activityStack = createTaskStackOnDisplay(mDisplayContent); - activityStack.addChild(windowContainer, 0); - activityStack.setParent(null); + // Task > WindowContainer + final Task task = createTask(mDisplayContent); + task.addChild(windowContainer, 0); + task.setParent(null); assertNull(windowContainer.getDisplayArea()); - assertNull(activityStack.getDisplayArea()); + assertNull(task.getDisplayArea()); - // TaskDisplayArea > ActivityStack > WindowContainer + // TaskDisplayArea > Task > WindowContainer final TaskDisplayArea taskDisplayArea = new TaskDisplayArea( mDisplayContent, mWm, "TaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER); - taskDisplayArea.addChild(activityStack, 0); + taskDisplayArea.addChild(task, 0); assertEquals(taskDisplayArea, windowContainer.getDisplayArea()); - assertEquals(taskDisplayArea, activityStack.getDisplayArea()); + assertEquals(taskDisplayArea, task.getDisplayArea()); assertEquals(taskDisplayArea, taskDisplayArea.getDisplayArea()); // DisplayArea @@ -986,8 +986,8 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testFreezeInsets() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final ActivityRecord activity = createActivityRecord(mDisplayContent, stack); + final Task task = createTask(mDisplayContent); + final ActivityRecord activity = createActivityRecord(mDisplayContent, task); final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); // Set visibility to false, verify the main window of the task will be set the frozen @@ -1002,8 +1002,8 @@ public class WindowContainerTests extends WindowTestsBase { @Test public void testFreezeInsetsStateWhenAppTransition() { - final Task stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 6d0e510ba626..baf072dbbc10 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -80,18 +80,18 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test - public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException { + public void testTaskFocusChange_rootTaskNotHomeType_focusChanges() throws RemoteException { DisplayContent display = createNewDisplay(); // Current focused window - Task focusedStack = createTaskStackOnDisplay( - WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display); - Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */); + Task focusedRootTask = createTask( + display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); + Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */); WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window"); mDisplayContent.mCurrentFocus = focusedWindow; // Tapped task - Task tappedStack = createTaskStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, display); - Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */); + Task tappedRootTask = createTask( + display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */); spyOn(mWm.mAtmService); mWm.handleTaskFocusChange(tappedTask); @@ -100,19 +100,19 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test - public void testTaskFocusChange_stackHomeTypeWithSameTaskDisplayArea_focusDoesNotChange() + public void testTaskFocusChange_rootTaskHomeTypeWithSameTaskDisplayArea_focusDoesNotChange() throws RemoteException { DisplayContent display = createNewDisplay(); // Current focused window - Task focusedStack = createTaskStackOnDisplay( - WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display); - Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */); + Task focusedRootTask = createTask( + display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); + Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */); WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window"); mDisplayContent.mCurrentFocus = focusedWindow; // Tapped home task - Task tappedStack = createTaskStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, display); - Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */); + Task tappedRootTask = createTask( + display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); + Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */); spyOn(mWm.mAtmService); mWm.handleTaskFocusChange(tappedTask); @@ -121,21 +121,21 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test - public void testTaskFocusChange_stackHomeTypeWithDifferentTaskDisplayArea_focusChanges() + public void testTaskFocusChange_rootTaskHomeTypeWithDifferentTaskDisplayArea_focusChanges() throws RemoteException { final DisplayContent display = createNewDisplay(); final TaskDisplayArea secondTda = createTaskDisplayArea( display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST); // Current focused window - Task focusedStack = createTaskStackOnDisplay( - WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display); - Task focusedTask = createTaskInStack(focusedStack, 0 /* userId */); + Task focusedRootTask = createTask( + display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); + Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */); WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window"); mDisplayContent.mCurrentFocus = focusedWindow; // Tapped home task on another task display area - Task tappedStack = createTaskStackOnTaskDisplayArea( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, secondTda); - Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */); + Task tappedRootTask = createTask(secondTda, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); + Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */); spyOn(mWm.mAtmService); mWm.handleTaskFocusChange(tappedTask); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 01c503e01326..e2b58bcb4bd5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -125,8 +125,8 @@ public class WindowOrganizerTests extends WindowTestsBase { return registerMockOrganizer(null); } - Task createTask(Task stack, boolean fakeDraw) { - final Task task = createTaskInStack(stack, 0); + Task createTask(Task rootTask, boolean fakeDraw) { + final Task task = createTaskInRootTask(rootTask, 0); if (fakeDraw) { task.setHasBeenVisible(true); @@ -134,13 +134,13 @@ public class WindowOrganizerTests extends WindowTestsBase { return task; } - Task createTask(Task stack) { + Task createTask(Task rootTask) { // Fake draw notifications for most of our tests. - return createTask(stack, true); + return createTask(rootTask, true); } - Task createStack() { - return createTaskStackOnDisplay(mDisplayContent); + Task createRootTask() { + return createTask(mDisplayContent); } @Before @@ -153,14 +153,14 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testAppearVanish() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - stack.removeImmediately(); + rootTask.removeImmediately(); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); @@ -169,21 +169,21 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testAppearWaitsForVisibility() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack, false); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask, false); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - stack.setHasBeenVisible(true); + rootTask.setHasBeenVisible(true); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); - assertTrue(stack.getHasBeenVisible()); + assertTrue(rootTask.getHasBeenVisible()); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - stack.removeImmediately(); + rootTask.removeImmediately(); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); @@ -192,108 +192,108 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testNoVanishedIfNoAppear() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack, false /* hasBeenVisible */); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask, false /* hasBeenVisible */); // In this test we skip making the Task visible, and verify // that even though a TaskOrganizer is set remove doesn't emit // a vanish callback, because we never emitted appear. - stack.setTaskOrganizer(organizer); + rootTask.setTaskOrganizer(organizer); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - stack.removeImmediately(); + rootTask.removeImmediately(); verify(organizer, never()).onTaskVanished(any()); } @Test public void testTaskNoDraw() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack, false /* fakeDraw */); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask, false /* fakeDraw */); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - assertTrue(stack.isOrganized()); + assertTrue(rootTask.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); - assertTaskVanished(organizer, false /* expectVanished */, stack); - assertFalse(stack.isOrganized()); + assertTaskVanished(organizer, false /* expectVanished */, rootTask); + assertFalse(rootTask.isOrganized()); } @Test public void testClearOrganizer() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - assertTrue(stack.isOrganized()); + assertTrue(rootTask.isOrganized()); - stack.setTaskOrganizer(null); + rootTask.setTaskOrganizer(null); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); - assertFalse(stack.isOrganized()); + assertFalse(rootTask.isOrganized()); } @Test public void testUnregisterOrganizer() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - assertTrue(stack.isOrganized()); + assertTrue(rootTask.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); - assertTaskVanished(organizer, true /* expectVanished */, stack); - assertFalse(stack.isOrganized()); + assertTaskVanished(organizer, true /* expectVanished */, rootTask); + assertFalse(rootTask.isOrganized()); } @Test public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException { - final Task stack = createStack(); - final Task task = createTask(stack); - final Task stack2 = createStack(); - final Task task2 = createTask(stack2); - final Task stack3 = createStack(); - final Task task3 = createTask(stack3); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final Task rootTask2 = createRootTask(); + final Task task2 = createTask(rootTask2); + final Task rootTask3 = createRootTask(); + final Task task3 = createTask(rootTask3); final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); // verify that tasks are returned and taskAppeared is not called - assertContainsTasks(existingTasks, stack, stack2, stack3); + assertContainsTasks(existingTasks, rootTask, rootTask2, rootTask3); verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); verify(organizer, times(0)).onTaskVanished(any()); - assertTrue(stack.isOrganized()); + assertTrue(rootTask.isOrganized()); // Now we replace the registration and verify the new organizer receives existing tasks final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); - assertContainsTasks(existingTasks2, stack, stack2, stack3); + assertContainsTasks(existingTasks2, rootTask, rootTask2, rootTask3); verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); verify(organizer2, times(0)).onTaskVanished(any()); // Removed tasks from the original organizer - assertTaskVanished(organizer, true /* expectVanished */, stack, stack2, stack3); - assertTrue(stack2.isOrganized()); + assertTaskVanished(organizer, true /* expectVanished */, rootTask, rootTask2, rootTask3); + assertTrue(rootTask2.isOrganized()); // Now we unregister the second one, the first one should automatically be reregistered // so we verify that it's now seeing changes. @@ -302,18 +302,18 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(3)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3); + assertTaskVanished(organizer2, true /* expectVanished */, rootTask, rootTask2, rootTask3); } @Test public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException { - final Task stack = createStack(); - final Task task = createTask(stack); - final Task stack2 = createStack(); - final Task task2 = createTask(stack2); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final Task rootTask2 = createRootTask(); + final Task task2 = createTask(rootTask2); ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); - assertContainsTasks(existingTasks, stack, stack2); + assertContainsTasks(existingTasks, rootTask, rootTask2); // Verify we don't get onTaskAppeared if we are returned the tasks verify(organizer, never()) @@ -323,21 +323,21 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testTaskTransaction() { removeGlobalMinSizeRestriction(); - final Task stack = new TaskBuilder(mSupervisor) + final Task rootTask = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); - final Task task = stack.getTopMostTask(); + final Task task = rootTask.getTopMostTask(); testTransaction(task); } @Test - public void testStackTransaction() { + public void testRootTaskTransaction() { removeGlobalMinSizeRestriction(); - final Task stack = new TaskBuilder(mSupervisor) + final Task rootTask = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); RootTaskInfo info = mWm.mAtmService.getRootTaskInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); - assertEquals(stack.mRemoteToken.toWindowContainerToken(), info.token); - testTransaction(stack); + assertEquals(rootTask.mRemoteToken.toWindowContainerToken(), info.token); + testTransaction(rootTask); } @Test @@ -357,9 +357,9 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testSetWindowingMode() { - final Task stack = new TaskBuilder(mSupervisor) + final Task rootTask = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); - testSetWindowingMode(stack); + testSetWindowingMode(rootTask); final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea"); displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -376,30 +376,30 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testSetActivityWindowingMode() { final ActivityRecord record = makePipableActivity(); - final Task stack = record.getRootTask(); + final Task rootTask = record.getRootTask(); final WindowContainerTransaction t = new WindowContainerTransaction(); - t.setWindowingMode(stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED); + t.setWindowingMode(rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_PINNED); t.setActivityWindowingMode( - stack.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN); + rootTask.mRemoteToken.toWindowContainerToken(), WINDOWING_MODE_FULLSCREEN); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); - assertEquals(WINDOWING_MODE_PINNED, stack.getWindowingMode()); + assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode()); } @Test public void testContainerFocusableChanges() { removeGlobalMinSizeRestriction(); - final Task stack = new TaskBuilder(mSupervisor) + final Task rootTask = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); - final Task task = stack.getTopMostTask(); + final Task task = rootTask.getTopMostTask(); WindowContainerTransaction t = new WindowContainerTransaction(); assertTrue(task.isFocusable()); - t.setFocusable(stack.mRemoteToken.toWindowContainerToken(), false); + t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), false); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertFalse(task.isFocusable()); - t.setFocusable(stack.mRemoteToken.toWindowContainerToken(), true); + t.setFocusable(rootTask.mRemoteToken.toWindowContainerToken(), true); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertTrue(task.isFocusable()); } @@ -407,25 +407,25 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testContainerHiddenChanges() { removeGlobalMinSizeRestriction(); - final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) + final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); WindowContainerTransaction t = new WindowContainerTransaction(); - assertTrue(stack.shouldBeVisible(null)); - t.setHidden(stack.mRemoteToken.toWindowContainerToken(), true); + assertTrue(rootTask.shouldBeVisible(null)); + t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), true); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); - assertFalse(stack.shouldBeVisible(null)); - t.setHidden(stack.mRemoteToken.toWindowContainerToken(), false); + assertFalse(rootTask.shouldBeVisible(null)); + t.setHidden(rootTask.mRemoteToken.toWindowContainerToken(), false); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); - assertTrue(stack.shouldBeVisible(null)); + assertTrue(rootTask.shouldBeVisible(null)); } @Test public void testSetIgnoreOrientationRequest_taskDisplayArea() { removeGlobalMinSizeRestriction(); final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task stack = taskDisplayArea.createRootTask( + final Task rootTask = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mDisplayContent.setFocusedApp(activity); activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); @@ -461,9 +461,9 @@ public class WindowOrganizerTests extends WindowTestsBase { public void testSetIgnoreOrientationRequest_displayContent() { removeGlobalMinSizeRestriction(); final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task stack = taskDisplayArea.createRootTask( + final Task rootTask = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build(); mDisplayContent.setFocusedApp(activity); activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); @@ -491,21 +491,21 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testOverrideConfigSize() { removeGlobalMinSizeRestriction(); - final Task stack = new TaskBuilder(mSupervisor) + final Task rootTask = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); - final Task task = stack.getTopMostTask(); + final Task task = rootTask.getTopMostTask(); WindowContainerTransaction t = new WindowContainerTransaction(); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); final int origScreenWDp = task.getConfiguration().screenHeightDp; final int origScreenHDp = task.getConfiguration().screenHeightDp; t = new WindowContainerTransaction(); // verify that setting config overrides on parent restricts children. - t.setScreenSizeDp(stack.mRemoteToken + t.setScreenSizeDp(rootTask.mRemoteToken .toWindowContainerToken(), origScreenWDp, origScreenHDp / 2); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(origScreenHDp / 2, task.getConfiguration().screenHeightDp); t = new WindowContainerTransaction(); - t.setScreenSizeDp(stack.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED, + t.setScreenSizeDp(rootTask.mRemoteToken.toWindowContainerToken(), SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(origScreenHDp, task.getConfiguration().screenHeightDp); @@ -572,14 +572,14 @@ public class WindowOrganizerTests extends WindowTestsBase { mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); RunningTaskInfo info1 = task.getTaskInfo(); - final Task stack = createTaskStackOnDisplay( - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); - assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode()); + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); + assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); + wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(info1.configuration.windowConfiguration.getWindowingMode(), - stack.getWindowingMode()); + rootTask.getWindowingMode()); // Info should reflect new membership List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent); @@ -591,14 +591,14 @@ public class WindowOrganizerTests extends WindowTestsBase { Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); Configuration c = new Configuration(task1.getRequestedOverrideConfiguration()); c.windowConfiguration.setBounds(newSize); - doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any()); + doNothing().when(rootTask).adjustForMinimalTaskDimensions(any(), any(), any()); task1.onRequestedOverrideConfigurationChanged(c); - assertEquals(newSize, stack.getBounds()); + assertEquals(newSize, rootTask.getBounds()); wct = new WindowContainerTransaction(); - wct.reparent(stack.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); + wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); - assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode()); + assertEquals(mDisplayContent.getWindowingMode(), rootTask.getWindowingMode()); infos = getTasksCreatedByOrganizer(mDisplayContent); info1 = infos.get(0).getTaskInfo(); assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); @@ -644,36 +644,39 @@ public class WindowOrganizerTests extends WindowTestsBase { lastReportedTiles.clear(); called[0] = false; - final Task stack = createTaskStackOnDisplay( - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask(); WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); + wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); lastReportedTiles.clear(); called[0] = false; - final Task stack2 = createTaskStackOnDisplay( - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent); + final Task rootTask2 = createTask( + mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME); wct = new WindowContainerTransaction(); - wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); + wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), + info1.token, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType); lastReportedTiles.clear(); called[0] = false; - task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */); + task1.positionChildAt(POSITION_TOP, rootTask, false /* includingParents */); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType); lastReportedTiles.clear(); called[0] = false; wct = new WindowContainerTransaction(); - wct.reparent(stack.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); - wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); + wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), + null, true /* onTop */); + wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), + null, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertTrue(called[0]); assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType); @@ -723,10 +726,10 @@ public class WindowOrganizerTests extends WindowTestsBase { final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( mDisplayContent.mDisplayId, null /* activityTypes */).size(); - final Task stack = createTaskStackOnDisplay( - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task stack2 = createTaskStackOnDisplay( - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent); + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); + final Task rootTask2 = createTask( + mDisplayContent, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME); // Check getRootTasks works List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks( @@ -735,8 +738,10 @@ public class WindowOrganizerTests extends WindowTestsBase { lastReportedTiles.clear(); WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reparent(stack.mRemoteToken.toWindowContainerToken(), info1.token, true /* onTop */); - wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info2.token, true /* onTop */); + wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(), + info1.token, true /* onTop */); + wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), + info2.token, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertFalse(lastReportedTiles.isEmpty()); assertEquals(ACTIVITY_TYPE_STANDARD, @@ -746,7 +751,8 @@ public class WindowOrganizerTests extends WindowTestsBase { lastReportedTiles.clear(); wct = new WindowContainerTransaction(); - wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), info1.token, false /* onTop */); + wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), + info1.token, false /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertFalse(lastReportedTiles.isEmpty()); // Standard should still be on top of tile 1, so no change there @@ -771,7 +777,7 @@ public class WindowOrganizerTests extends WindowTestsBase { lastReportedTiles.clear(); wct = new WindowContainerTransaction(); - wct.reorder(stack2.mRemoteToken.toWindowContainerToken(), true /* onTop */); + wct.reorder(rootTask2.mRemoteToken.toWindowContainerToken(), true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); // Home should now be on top. No change occurs in second tile, so not reported assertEquals(1, lastReportedTiles.size()); @@ -780,10 +786,10 @@ public class WindowOrganizerTests extends WindowTestsBase { // This just needs to not crash (ie. it should be possible to reparent to display twice) wct = new WindowContainerTransaction(); - wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); + wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); wct = new WindowContainerTransaction(); - wct.reparent(stack2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); + wct.reparent(rootTask2.mRemoteToken.toWindowContainerToken(), null, true /* onTop */); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); } @@ -799,8 +805,8 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithActivityChildren() { - final Task stackController1 = createStack(); - final Task task = createTask(stackController1); + final Task rootTaskController1 = createRootTask(); + final Task task = createTask(rootTaskController1); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); w.mActivityRecord.mVisibleRequested = true; @@ -926,11 +932,11 @@ public class WindowOrganizerTests extends WindowTestsBase { ChangeSavingOrganizer o = new ChangeSavingOrganizer(); mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o); - final Task stack = createStack(); - final Task task = createTask(stack); - final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); waitUntilHandlersIdle(); assertEquals("TestDescription", o.mChangedInfo.taskDescription.getLabel()); @@ -939,29 +945,29 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testPreventDuplicateAppear() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack, false /* fakeDraw */); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask, false /* fakeDraw */); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - stack.setTaskOrganizer(organizer); + rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + rootTask.setTaskOrganizer(organizer); // setHasBeenVisible was already called once by the set-up code. - stack.setHasBeenVisible(true); + rootTask.setHasBeenVisible(true); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - stack.setTaskOrganizer(null); + rootTask.setTaskOrganizer(null); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onTaskVanished(any()); - stack.setTaskOrganizer(organizer); + rootTask.setTaskOrganizer(organizer); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - stack.removeImmediately(); + rootTask.removeImmediately(); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)).onTaskVanished(any()); @@ -970,15 +976,15 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testInterceptBackPressedOnTaskRoot() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); - final ActivityRecord activity = createActivityRecord(stack.mDisplayContent, task); - final Task stack2 = createStack(); - final Task task2 = createTask(stack2); - final ActivityRecord activity2 = createActivityRecord(stack.mDisplayContent, task2); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task); + final Task rootTask2 = createRootTask(); + final Task task2 = createTask(rootTask2); + final ActivityRecord activity2 = createActivityRecord(rootTask.mDisplayContent, task2); - assertTrue(stack.isOrganized()); - assertTrue(stack2.isOrganized()); + assertTrue(rootTask.isOrganized()); + assertTrue(rootTask2.isOrganized()); // Verify a back pressed does not call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, @@ -989,7 +995,7 @@ public class WindowOrganizerTests extends WindowTestsBase { // Enable intercepting back mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( - stack.mRemoteToken.toWindowContainerToken(), true); + rootTask.mRemoteToken.toWindowContainerToken(), true); // Verify now that the back press does call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, @@ -1000,7 +1006,7 @@ public class WindowOrganizerTests extends WindowTestsBase { // Disable intercepting back mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( - stack.mRemoteToken.toWindowContainerToken(), false); + rootTask.mRemoteToken.toWindowContainerToken(), false); // Verify now that the back press no longer calls the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, @@ -1012,8 +1018,8 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithWindows() throws Exception { - final Task stackController = createStack(); - final Task task = createTask(stackController); + final Task rootTaskController = createRootTask(); + final Task task = createTask(rootTaskController); final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1"); final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2"); makeWindowVisible(w1); @@ -1077,7 +1083,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null); - final Task task1 = createStack(); + final Task task1 = createRootTask(); final Task task2 = createTask(rootTask, false /* fakeDraw */); WindowContainerTransaction wct = new WindowContainerTransaction(); wct.reparent(task1.mRemoteToken.toWindowContainerToken(), @@ -1090,19 +1096,19 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testAppearDeferThenInfoChange() { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); + final Task rootTask = createRootTask(); // Assume layout defer mWm.mWindowPlacerLocked.deferLayout(); - final Task task = createTask(stack); - final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + final Task task = createTask(rootTask); + final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); waitUntilHandlersIdle(); - ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); assertEquals(1, pendingEvents.size()); assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType); assertEquals("TestDescription", @@ -1112,35 +1118,35 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testAppearDeferThenVanish() { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); + final Task rootTask = createRootTask(); // Assume layout defer mWm.mWindowPlacerLocked.deferLayout(); - final Task task = createTask(stack); + final Task task = createTask(rootTask); - stack.removeImmediately(); + rootTask.removeImmediately(); waitUntilHandlersIdle(); - ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); assertEquals(0, pendingEvents.size()); } @Test public void testInfoChangeDeferMultiple() { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); - final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); // Assume layout defer mWm.mWindowPlacerLocked.deferLayout(); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); waitUntilHandlersIdle(); - ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); assertEquals(1, pendingEvents.size()); assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); assertEquals("TestDescription", @@ -1149,7 +1155,7 @@ public class WindowOrganizerTests extends WindowTestsBase { record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2")); waitUntilHandlersIdle(); - pendingEvents = getTaskPendingEvent(stack); + pendingEvents = getTaskPendingEvent(rootTask); assertEquals(1, pendingEvents.size()); assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); assertEquals("TestDescription2", @@ -1159,20 +1165,20 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testInfoChangDeferThenVanish() { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); - final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); // Assume layout defer mWm.mWindowPlacerLocked.deferLayout(); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); - stack.removeImmediately(); + rootTask.removeImmediately(); waitUntilHandlersIdle(); - ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); assertEquals(1, pendingEvents.size()); assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); assertEquals("TestDescription", @@ -1182,18 +1188,18 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testVanishDeferThenInfoChange() { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); - final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); // Assume layout defer mWm.mWindowPlacerLocked.deferLayout(); - stack.removeImmediately(); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + rootTask.removeImmediately(); + rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); waitUntilHandlersIdle(); - ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); assertEquals(1, pendingEvents.size()); assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); } @@ -1201,19 +1207,19 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testVanishDeferThenBackOnRoot() { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task stack = createStack(); - final Task task = createTask(stack); - final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final ActivityRecord record = createActivityRecord(rootTask.mDisplayContent, task); // Assume layout defer mWm.mWindowPlacerLocked.deferLayout(); - stack.removeImmediately(); + rootTask.removeImmediately(); mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token, new IRequestFinishCallback.Default()); waitUntilHandlersIdle(); - ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask); assertEquals(1, pendingEvents.size()); assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); } @@ -1264,7 +1270,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(); - final Task rootTask = createStack(); + final Task rootTask = createRootTask(); final Task task = createTask(rootTask); final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task); final ArgumentCaptor<RunningTaskInfo> infoCaptor = diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 5b5b1da327bd..bfbe203fb65e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -249,21 +249,22 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(appWindow.canBeImeTarget()); assertFalse(imeWindow.canBeImeTarget()); - // Simulate the window is in split screen primary stack and the current state is - // minimized and home stack is resizable, so that we should ignore input for the stack. + // Simulate the window is in split screen primary root task and the current state is + // minimized and home root task is resizable, so that we should ignore input for the + // root task. final DockedTaskDividerController controller = mDisplayContent.getDockedDividerController(); - final Task stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, - ACTIVITY_TYPE_STANDARD, mDisplayContent); + final Task rootTask = createTask(mDisplayContent, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD); spyOn(appWindow); spyOn(controller); - spyOn(stack); - stack.setFocusable(false); - doReturn(stack).when(appWindow).getRootTask(); + spyOn(rootTask); + rootTask.setFocusable(false); + doReturn(rootTask).when(appWindow).getRootTask(); // Make sure canBeImeTarget is false due to shouldIgnoreInput is true; assertFalse(appWindow.canBeImeTarget()); - assertTrue(stack.shouldIgnoreInput()); + assertTrue(rootTask.shouldIgnoreInput()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index b210dfb654ba..e9e0c99c3d93 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -429,35 +429,55 @@ class WindowTestsBase extends SystemServiceTestsBase { return newTaskDisplayArea; } - /** Creates a {@link Task} and adds it to the specified {@link DisplayContent}. */ - Task createTaskStackOnDisplay(DisplayContent dc) { - return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc); + /** + * Creates a {@link Task} with a simple {@link ActivityRecord} and adds to the given + * {@link TaskDisplayArea}. + */ + Task createTaskWithActivity(TaskDisplayArea taskDisplayArea, + int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) { + return createTask(taskDisplayArea, windowingMode, activityType, + onTop, true /* createActivity */, twoLevelTask); } - Task createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) { - return new TaskBuilder(dc.mAtmService.mTaskSupervisor) - .setDisplay(dc) - .setWindowingMode(windowingMode) - .setActivityType(activityType) - .setIntent(new Intent()) - .build(); + /** Creates a {@link Task} and adds to the given {@link DisplayContent}. */ + Task createTask(DisplayContent dc) { + return createTask(dc.getDefaultTaskDisplayArea(), + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + } + + Task createTask(DisplayContent dc, int windowingMode, int activityType) { + return createTask(dc.getDefaultTaskDisplayArea(), windowingMode, activityType); + } + + Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType) { + return createTask(taskDisplayArea, windowingMode, activityType, + true /* onTop */, false /* createActivity */, false /* twoLevelTask */); } - Task createTaskStackOnTaskDisplayArea(int windowingMode, int activityType, - TaskDisplayArea tda) { - return new TaskBuilder(tda.mDisplayContent.mAtmService.mTaskSupervisor) - .setTaskDisplayArea(tda) + /** Creates a {@link Task} and adds to the given {@link TaskDisplayArea}. */ + Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, + boolean onTop, boolean createActivity, boolean twoLevelTask) { + final TaskBuilder builder = new TaskBuilder(mSupervisor) + .setTaskDisplayArea(taskDisplayArea) .setWindowingMode(windowingMode) .setActivityType(activityType) - .setIntent(new Intent()) - .build(); + .setOnTop(onTop) + .setCreateActivity(createActivity); + if (twoLevelTask) { + return builder + .setCreateParentTask(true) + .build() + .getRootTask(); + } else { + return builder.build(); + } } - /** Creates a {@link Task} and adds it to the specified {@link Task}. */ - Task createTaskInStack(Task stack, int userId) { - final Task task = new TaskBuilder(stack.mTaskSupervisor) + /** Creates a {@link Task} and adds to the given root {@link Task}. */ + Task createTaskInRootTask(Task rootTask, int userId) { + final Task task = new TaskBuilder(rootTask.mTaskSupervisor) .setUserId(userId) - .setParentTask(stack) + .setParentTask(rootTask) .build(); return task; } @@ -485,7 +505,7 @@ class WindowTestsBase extends SystemServiceTestsBase { */ ActivityRecord createActivityRecord(DisplayContent dc, int windowingMode, int activityType) { - final Task task = createTaskStackOnDisplay(windowingMode, activityType, dc); + final Task task = createTask(dc, windowingMode, activityType); return createActivityRecord(dc, task); } @@ -517,7 +537,7 @@ class WindowTestsBase extends SystemServiceTestsBase { */ ActivityRecord createActivityRecordWithParentTask(DisplayContent dc, int windowingMode, int activityType) { - final Task task = createTaskStackOnDisplay(windowingMode, activityType, dc); + final Task task = createTask(dc, windowingMode, activityType); return createActivityRecordWithParentTask(task); } @@ -871,7 +891,7 @@ class WindowTestsBase extends SystemServiceTestsBase { .setParentTask(mParentTask).build(); } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask( mParentTask.getWindowingMode(), mParentTask.getActivityType())) { - // The stack can be the task root. + // The parent task can be the task root. mTask = mParentTask; } @@ -921,9 +941,9 @@ class WindowTestsBase extends SystemServiceTestsBase { doReturn(true).when(activity).fillsParent(); mTask.addChild(activity); if (mOnTop) { - // Move the task to front after activity added. - // Or {@link TaskDisplayArea#mPreferredTopFocusableStack} could be other stacks - // (e.g. home stack). + // Move the task to front after activity is added. + // Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be other + // root tasks (e.g. home root task). mTask.moveToFront("createActivity"); } // Make visible by default... @@ -1127,8 +1147,8 @@ class WindowTestsBase extends SystemServiceTestsBase { .build(); if (mOnTop) { // We move the task to front again in order to regain focus after activity - // added to the stack. Or {@link TaskDisplayArea#mPreferredTopFocusableStack} - // could be other stacks (e.g. home stack). + // is added. Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be + // other root tasks (e.g. home root task). task.moveToFront("createActivityTask"); } else { task.moveToBack("createActivityTask", null); |