| /* |
| * 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.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_RECENTS; |
| 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_LAUNCH_ADJACENT; |
| 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; |
| 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.ActivityRecord.State.RESUMED; |
| import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; |
| 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; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.verify; |
| |
| import android.app.ActivityOptions; |
| import android.platform.test.annotations.Presubmit; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import com.android.server.wm.LaunchParamsController.LaunchParams; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| /** |
| * Tests for the {@link TaskDisplayArea} container. |
| * |
| * Build/Install/Run: |
| * atest WmTests:TaskDisplayAreaTests |
| */ |
| @SmallTest |
| @Presubmit |
| @RunWith(WindowTestRunner.class) |
| public class TaskDisplayAreaTests extends WindowTestsBase { |
| |
| @Test |
| public void getLaunchRootTask_checksLaunchAdjacentFlagRoot() { |
| final Task rootTask = createTask( |
| mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |
| rootTask.mCreatedByOrganizer = true; |
| final Task adjacentRootTask = createTask( |
| mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |
| adjacentRootTask.mCreatedByOrganizer = true; |
| final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); |
| adjacentRootTask.setAdjacentTaskFragment(rootTask); |
| |
| taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); |
| Task actualRootTask = taskDisplayArea.getLaunchRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, null /* options */, |
| null /* sourceTask */, FLAG_ACTIVITY_LAUNCH_ADJACENT); |
| assertSame(adjacentRootTask, actualRootTask.getRootTask()); |
| |
| taskDisplayArea.setLaunchAdjacentFlagRootTask(null); |
| actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, |
| ACTIVITY_TYPE_STANDARD, null /* options */, null /* sourceTask */, |
| FLAG_ACTIVITY_LAUNCH_ADJACENT); |
| assertNull(actualRootTask); |
| } |
| |
| @Test |
| public void getLaunchRootTask_checksFocusedRootTask() { |
| final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); |
| final Task rootTask = createTaskWithActivity( |
| taskDisplayArea, |
| WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, ON_TOP, true); |
| rootTask.mCreatedByOrganizer = true; |
| |
| final Task adjacentRootTask = createTask( |
| mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |
| adjacentRootTask.mCreatedByOrganizer = true; |
| adjacentRootTask.setAdjacentTaskFragment(rootTask); |
| |
| taskDisplayArea.setLaunchRootTask(rootTask, |
| new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD}); |
| |
| Task actualRootTask = taskDisplayArea.getLaunchRootTask( |
| WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, null /* options */, |
| null /* sourceTask */, 0 /*launchFlags*/); |
| assertTrue(actualRootTask.isFocusedRootTaskOnDisplay()); |
| } |
| |
| @Test |
| public void getLaunchRootTask_fromLaunchAdjacentFlagRoot_checksAdjacentRoot() { |
| final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); |
| final Task rootTask = createTask( |
| mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |
| rootTask.mCreatedByOrganizer = true; |
| final Task adjacentRootTask = createTask( |
| mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |
| adjacentRootTask.mCreatedByOrganizer = true; |
| final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); |
| adjacentRootTask.setAdjacentTaskFragment(rootTask); |
| |
| taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); |
| final Task actualRootTask = taskDisplayArea.getLaunchRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, null /* options */, |
| adjacentRootTask /* sourceTask */, FLAG_ACTIVITY_LAUNCH_ADJACENT); |
| |
| assertSame(rootTask, actualRootTask.getRootTask()); |
| } |
| |
| @Test |
| public void getOrCreateLaunchRootRespectsResolvedWindowingMode() { |
| 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 = createTask( |
| mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); |
| final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); |
| final LaunchParams launchParams = new LaunchParams(); |
| launchParams.mWindowingMode = WINDOWING_MODE_FREEFORM; |
| |
| final Task actualRootTask = taskDisplayArea.getOrCreateRootTask( |
| activity, null /* options */, candidateRootTask, null /* sourceTask */, |
| launchParams, 0 /* launchFlags */, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| assertSame(rootTask, actualRootTask.getRootTask()); |
| } |
| |
| @Test |
| public void getOrCreateLaunchRootUsesActivityOptionsWindowingMode() { |
| 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 = createTask( |
| mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); |
| final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); |
| final ActivityOptions options = ActivityOptions.makeBasic(); |
| options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); |
| |
| final Task actualRootTask = taskDisplayArea.getOrCreateRootTask( |
| activity, options, candidateRootTask, null /* sourceTask */, |
| null /* launchParams */, 0 /* launchFlags */, ACTIVITY_TYPE_STANDARD, |
| true /* onTop */); |
| assertSame(rootTask, actualRootTask.getRootTask()); |
| } |
| |
| @Test |
| public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() { |
| 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(); |
| activity.mNeedsAnimationBoundsLayer = true; |
| activity.mNeedsZBoost = true; |
| spyOn(taskDisplayArea.mSurfaceAnimator); |
| |
| mDisplayContent.assignChildLayers(mTransaction); |
| |
| assertThat(activity.needsZBoost()).isTrue(); |
| assertThat(taskDisplayArea.needsZBoost()).isFalse(); |
| verify(taskDisplayArea.mSurfaceAnimator, never()).setLayer(eq(mTransaction), anyInt()); |
| } |
| |
| @Test |
| 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 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 taskContainer = rootTask1.getParent(); |
| |
| final int rootTaskPos = taskContainer.mChildren.indexOf(rootTask1); |
| final int pinnedTaskPos = taskContainer.mChildren.indexOf(pinnedTask); |
| assertThat(pinnedTaskPos).isGreaterThan(rootTaskPos); |
| |
| taskContainer.positionChildAt(WindowContainer.POSITION_TOP, rootTask1, false); |
| assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1); |
| assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask); |
| |
| taskContainer.positionChildAt(taskContainer.mChildren.size() - 1, rootTask1, false); |
| assertEquals(taskContainer.mChildren.get(rootTaskPos), rootTask1); |
| assertEquals(taskContainer.mChildren.get(pinnedTaskPos), pinnedTask); |
| } |
| |
| @Test |
| 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 root task to move to top. |
| mDisplayContent.mDontMoveToTop = false; |
| |
| // 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. |
| 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, indexOfDisplayWithPinnedRootTask); |
| } |
| |
| @Test |
| public void testMovingChildTaskOnTop() { |
| // 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 root task to move to top. |
| mDisplayContent.mDontMoveToTop = false; |
| |
| // 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(), |
| false /* includingParents */); |
| |
| // Ensure that original display ({@code mDisplayContent}) is not on top. |
| assertEquals("Testing DisplayContent should not be on the top", |
| mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); |
| |
| // Move the task of {@code mDisplayContent} to top. |
| 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", |
| mWm.mRoot.getChildCount() - 1, mWm.mRoot.mChildren.indexOf(mDisplayContent)); |
| } |
| |
| @Test |
| public void testDontMovingChildTaskOnTop() { |
| // 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 root task to move to top. |
| mDisplayContent.mDontMoveToTop = true; |
| |
| // 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(), |
| false /* includingParents */); |
| |
| // Ensure that original display ({@code mDisplayContent}) is not on top. |
| assertEquals("Testing DisplayContent should not be on the top", |
| mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); |
| |
| // Try moving the task of {@code mDisplayContent} to top. |
| rootTask.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); |
| |
| // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not |
| // on the top. |
| assertEquals("The testing DisplayContent should not be moved to top with task", |
| mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); |
| } |
| |
| @Test |
| public void testReuseTaskAsRootTask() { |
| final Task candidateTask = createTask(mDisplayContent); |
| final int type = ACTIVITY_TYPE_STANDARD; |
| assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask, |
| true /* reuseCandidate */); |
| assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask, |
| true /* reuseCandidate */); |
| assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask, |
| true /* reuseCandidate */); |
| assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask, |
| true /* reuseCandidate */); |
| assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask, |
| true /* reuseCandidate */); |
| |
| final int windowingMode = WINDOWING_MODE_FULLSCREEN; |
| assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_HOME, candidateTask, |
| false /* reuseCandidate */); |
| assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_RECENTS, candidateTask, |
| false /* reuseCandidate */); |
| assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_ASSISTANT, candidateTask, |
| false /* reuseCandidate */); |
| assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_DREAM, candidateTask, |
| false /* reuseCandidate */); |
| } |
| |
| @Test |
| public void testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange() { |
| final RootWindowContainer rootWindowContainer = mWm.mAtmService.mRootWindowContainer; |
| final TaskDisplayArea defaultTaskDisplayArea = |
| rootWindowContainer.getDefaultTaskDisplayArea(); |
| |
| final Task rootHomeTask = defaultTaskDisplayArea.getRootHomeTask(); |
| rootHomeTask.mResizeMode = RESIZE_MODE_UNRESIZEABLE; |
| |
| final Task primarySplitTask = new TaskBuilder(mSupervisor) |
| .setTaskDisplayArea(defaultTaskDisplayArea) |
| .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW) |
| .setActivityType(ACTIVITY_TYPE_STANDARD) |
| .setOnTop(true) |
| .setCreateActivity(true) |
| .build(); |
| ActivityRecord primarySplitActivity = primarySplitTask.getTopNonFinishingActivity(); |
| assertNotNull(primarySplitActivity); |
| primarySplitActivity.setState(RESUMED, |
| "testGetOrientation_nonResizableHomeTaskWithHomeActivityPendingVisibilityChange"); |
| |
| ActivityRecord homeActivity = rootHomeTask.getTopNonFinishingActivity(); |
| if (homeActivity == null) { |
| homeActivity = new ActivityBuilder(mWm.mAtmService) |
| .setParentTask(rootHomeTask).setCreateTask(true).build(); |
| } |
| homeActivity.setVisible(false); |
| homeActivity.mVisibleRequested = true; |
| assertFalse(rootHomeTask.isVisible()); |
| |
| assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation()); |
| } |
| |
| @Test |
| public void testIsLastFocused() { |
| 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(); |
| |
| // Activity on TDA1 is focused |
| mDisplayContent.setFocusedApp(firstActivity); |
| |
| assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); |
| assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); |
| |
| // No focused app, TDA1 is still recorded as last focused. |
| mDisplayContent.setFocusedApp(null); |
| |
| assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue(); |
| assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); |
| |
| // Activity on TDA2 is focused |
| mDisplayContent.setFocusedApp(secondActivity); |
| |
| assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); |
| assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); |
| } |
| |
| @Test |
| public void testIsLastFocused_onlyCountIfTaskDisplayAreaHandlesOrientationRequest() { |
| 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(); |
| firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); |
| secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); |
| |
| // Activity on TDA1 is focused, but TDA1 doesn't respect orientation request |
| mDisplayContent.setFocusedApp(firstActivity); |
| |
| assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); |
| assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse(); |
| |
| // Activity on TDA2 is focused, and TDA2 respects orientation request |
| mDisplayContent.setFocusedApp(secondActivity); |
| |
| assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse(); |
| assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue(); |
| } |
| |
| @Test |
| public void testIgnoreOrientationRequest() { |
| final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); |
| final Task task = taskDisplayArea.createRootTask( |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); |
| |
| mDisplayContent.setFocusedApp(activity); |
| activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); |
| |
| assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); |
| |
| taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); |
| |
| assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); |
| } |
| |
| @Test |
| @UseTestDisplay |
| public void testRemove_reparentToDefault() { |
| final Task task = createTask(mDisplayContent); |
| final TaskDisplayArea displayArea = task.getDisplayArea(); |
| displayArea.remove(); |
| assertTrue(displayArea.isRemoved()); |
| assertFalse(displayArea.hasChild()); |
| |
| final RootWindowContainer rootWindowContainer = mWm.mAtmService.mRootWindowContainer; |
| final TaskDisplayArea defaultTaskDisplayArea = |
| rootWindowContainer.getDefaultTaskDisplayArea(); |
| assertTrue(defaultTaskDisplayArea.mChildren.contains(task)); |
| } |
| |
| @Test |
| @UseTestDisplay |
| public void testRemove_rootTaskCreatedByOrganizer() { |
| final Task task = createTask(mDisplayContent); |
| task.mCreatedByOrganizer = true; |
| final TaskDisplayArea displayArea = task.getDisplayArea(); |
| displayArea.remove(); |
| assertTrue(displayArea.isRemoved()); |
| assertFalse(displayArea.hasChild()); |
| |
| final RootWindowContainer rootWindowContainer = mWm.mAtmService.mRootWindowContainer; |
| final TaskDisplayArea defaultTaskDisplayArea = |
| rootWindowContainer.getDefaultTaskDisplayArea(); |
| assertFalse(defaultTaskDisplayArea.mChildren.contains(task)); |
| } |
| |
| private void assertGetOrCreateRootTask(int windowingMode, int activityType, Task candidateTask, |
| boolean reuseCandidate) { |
| final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); |
| final Task rootTask = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType, |
| false /* onTop */, candidateTask /* candidateTask */, null /* sourceTask */, |
| null /* activityOptions */, 0 /* launchFlags */); |
| 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) |
| .setParentTaskFragment(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()); |
| 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)); |
| } |
| |
| /** |
| * This test verifies proper launch root based on source and candidate task for split screen. |
| * If a task is launching from a created-by-organizer task, it should be launched into the |
| * same created-by-organizer task as well. Unless, the candidate task is already positioned in |
| * the split. |
| */ |
| @Test |
| public void getLaunchRootTaskInSplit() { |
| final Task rootTask = createTask( |
| mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |
| rootTask.mCreatedByOrganizer = true; |
| final Task adjacentRootTask = createTask( |
| mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |
| adjacentRootTask.mCreatedByOrganizer = true; |
| final Task candidateTask = createTaskInRootTask(rootTask, 0 /* userId*/); |
| final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); |
| adjacentRootTask.setAdjacentTaskFragment(rootTask); |
| |
| // Verify the launch root with candidate task |
| Task actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, |
| ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */, |
| 0 /* launchFlags */, candidateTask); |
| assertSame(rootTask, actualRootTask); |
| |
| // Verify the launch root task without candidate task |
| actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, |
| ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */, |
| 0 /* launchFlags */); |
| assertSame(adjacentRootTask, actualRootTask); |
| |
| final Task pinnedTask = createTask( |
| mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); |
| // Verify not adjusting launch target for pinned candidate task |
| actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, |
| ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */, |
| 0 /* launchFlags */, pinnedTask /* candidateTask */); |
| assertNull(actualRootTask); |
| } |
| } |