| /* |
| * 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_HOME; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; |
| 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_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_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.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; |
| import static com.android.server.wm.Task.ActivityState.DESTROYING; |
| import static com.android.server.wm.Task.ActivityState.FINISHING; |
| import static com.android.server.wm.Task.ActivityState.PAUSING; |
| import static com.android.server.wm.Task.ActivityState.RESUMED; |
| import static com.android.server.wm.Task.ActivityState.STOPPED; |
| import static com.android.server.wm.Task.ActivityState.STOPPING; |
| import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; |
| import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; |
| import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; |
| 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; |
| |
| 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 org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.function.Consumer; |
| |
| /** |
| * Tests for the root {@link Task} behavior. |
| * |
| * Build/Install/Run: |
| * atest WmTests:RootTaskTests |
| */ |
| @SmallTest |
| @Presubmit |
| @RunWith(WindowTestRunner.class) |
| public class RootTaskTests extends WindowTestsBase { |
| |
| private TaskDisplayArea mDefaultTaskDisplayArea; |
| |
| @Before |
| public void setUp() throws Exception { |
| mDefaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); |
| } |
| |
| @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(); |
| assertNull(task.getResumedActivity()); |
| r.setState(RESUMED, "testResumedActivity"); |
| assertEquals(r, task.getResumedActivity()); |
| r.setState(PAUSING, "testResumedActivity"); |
| assertNull(task.getResumedActivity()); |
| } |
| |
| @Test |
| public void testResumedActivityFromTaskReparenting() { |
| final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); |
| final ActivityRecord r = new ActivityBuilder(mAtm) |
| .setCreateTask(true).setParentTask(rootTask).build(); |
| final Task task = r.getTask(); |
| // Ensure moving task between two root tasks updates resumed activity |
| r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); |
| assertEquals(r, rootTask.getResumedActivity()); |
| |
| 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(rootTask.getResumedActivity()); |
| assertEquals(r, destRootTask.getResumedActivity()); |
| } |
| |
| @Test |
| public void testResumedActivityFromActivityReparenting() { |
| final Task rootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); |
| final ActivityRecord r = new ActivityBuilder(mAtm) |
| .setCreateTask(true).setParentTask(rootTask).build(); |
| final Task task = r.getTask(); |
| // Ensure moving task between two root tasks updates resumed activity |
| r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); |
| assertEquals(r, rootTask.getResumedActivity()); |
| |
| 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(rootTask.getResumedActivity()); |
| assertEquals(r, destRootTask.getResumedActivity()); |
| } |
| |
| @Test |
| public void testPrimarySplitScreenMoveToBack() { |
| TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); |
| // We're testing an edge case here where we have primary + fullscreen rather than secondary. |
| organizer.setMoveToSecondaryOnEnter(false); |
| |
| // Create primary splitscreen root task. |
| final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor) |
| .setParentTask(organizer.mPrimary) |
| .setOnTop(true) |
| .build(); |
| |
| // Assert windowing mode. |
| assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode()); |
| |
| // Move primary to back. |
| primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack", |
| null /* task */); |
| |
| // Assert that root task is at the bottom. |
| assertEquals(0, getTaskIndexOf(mDefaultTaskDisplayArea, primarySplitScreen)); |
| |
| // Ensure no longer in splitscreen. |
| assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); |
| |
| // Ensure that the override mode is restored to undefined |
| assertEquals(WINDOWING_MODE_UNDEFINED, |
| primarySplitScreen.getRequestedOverrideWindowingMode()); |
| } |
| |
| @Test |
| public void testMoveToPrimarySplitScreenThenMoveToBack() { |
| TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); |
| // This time, start with a fullscreen activity root task. |
| final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| primarySplitScreen.reparent(organizer.mPrimary, POSITION_TOP, |
| false /*moveParents*/, "test"); |
| |
| // Assert windowing mode. |
| assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode()); |
| |
| // Move primary to back. |
| primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack", |
| null /* task */); |
| |
| // 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) |
| assertEquals(WINDOWING_MODE_UNDEFINED, |
| primarySplitScreen.getRequestedOverrideWindowingMode()); |
| } |
| |
| @Test |
| 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 root task. |
| final Task primaryTask = mDefaultTaskDisplayArea.createRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| final Task homeRoot = mDefaultTaskDisplayArea.getRootTask( |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); |
| primaryTask.reparent(organizer.mPrimary, POSITION_TOP); |
| mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary, |
| false /* includingParents */); |
| |
| // Move primary to back. |
| primaryTask.moveToBack("test", null /* task */); |
| |
| // Assert that the primaryTask is now below home in its parent but primary is left alone. |
| assertEquals(0, organizer.mPrimary.getChildCount()); |
| assertEquals(primaryTask, organizer.mSecondary.getChildAt(0)); |
| assertEquals(1, organizer.mPrimary.compareTo(organizer.mSecondary)); |
| assertEquals(1, homeRoot.compareTo(primaryTask)); |
| assertEquals(homeRoot.getParent(), primaryTask.getParent()); |
| |
| // Make sure windowing modes are correct |
| assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, organizer.mPrimary.getWindowingMode()); |
| assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, primaryTask.getWindowingMode()); |
| |
| // Move secondary to back via parent (should be equivalent) |
| organizer.mSecondary.moveToBack("test", secondaryTask); |
| |
| // Assert that it is now in back but still in secondary split |
| assertEquals(1, homeRoot.compareTo(primaryTask)); |
| assertEquals(secondaryTask, organizer.mSecondary.getChildAt(0)); |
| assertEquals(1, primaryTask.compareTo(secondaryTask)); |
| assertEquals(homeRoot.getParent(), secondaryTask.getParent()); |
| } |
| |
| @Test |
| public void testRemoveOrganizedTask_UpdateRootTaskReference() { |
| final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask(); |
| final ActivityRecord homeActivity = new ActivityBuilder(mAtm) |
| .setTask(rootHomeTask) |
| .build(); |
| final Task secondaryRootTask = mAtm.mTaskOrganizerController.createRootTask( |
| rootHomeTask.getDisplayContent(), WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); |
| |
| rootHomeTask.reparent(secondaryRootTask, POSITION_TOP); |
| assertEquals(secondaryRootTask, rootHomeTask.getParent()); |
| |
| // This should call to {@link TaskDisplayArea#removeRootTaskReferenceIfNeeded}. |
| homeActivity.removeImmediately(); |
| assertNull(mDefaultTaskDisplayArea.getRootHomeTask()); |
| } |
| |
| @Test |
| public void testRootTaskInheritsDisplayWindowingMode() { |
| final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); |
| assertEquals(WINDOWING_MODE_UNDEFINED, |
| primarySplitScreen.getRequestedOverrideWindowingMode()); |
| |
| mDefaultTaskDisplayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); |
| assertEquals(WINDOWING_MODE_FREEFORM, primarySplitScreen.getWindowingMode()); |
| assertEquals(WINDOWING_MODE_UNDEFINED, |
| primarySplitScreen.getRequestedOverrideWindowingMode()); |
| } |
| |
| @Test |
| public void testRootTaskOverridesDisplayWindowingMode() { |
| final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); |
| assertEquals(WINDOWING_MODE_UNDEFINED, |
| primarySplitScreen.getRequestedOverrideWindowingMode()); |
| |
| primarySplitScreen.setWindowingMode(WINDOWING_MODE_FULLSCREEN); |
| // setting windowing mode should still work even though resolved mode is already fullscreen |
| assertEquals(WINDOWING_MODE_FULLSCREEN, |
| primarySplitScreen.getRequestedOverrideWindowingMode()); |
| |
| mDefaultTaskDisplayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); |
| assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); |
| } |
| |
| @Test |
| public void testStopActivityWhenActivityDestroyed() { |
| final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); |
| r.info.flags |= ActivityInfo.FLAG_NO_HISTORY; |
| r.getTask().moveToFront("testStopActivityWithDestroy"); |
| r.stopIfPossible(); |
| // Mostly testing to make sure there is a crash in the call part, so if we get here we are |
| // good-to-go! |
| } |
| |
| @Test |
| public void testFindTaskWithOverlay() { |
| final ActivityRecord r = new ActivityBuilder(mAtm) |
| .setCreateTask(true) |
| .setUid(0) |
| .build(); |
| final Task task = r.getTask(); |
| // Overlay must be for a different user to prevent recognizing a matching top activity |
| final ActivityRecord taskOverlay = new ActivityBuilder(mAtm).setTask(task) |
| .setUid(UserHandle.PER_USER_RANGE * 2).build(); |
| taskOverlay.setTaskOverlay(true); |
| |
| final RootWindowContainer.FindTaskResult result = |
| new RootWindowContainer.FindTaskResult(); |
| result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info); |
| result.process(task); |
| |
| assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */)); |
| assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */)); |
| assertNotNull(result.mIdealRecord); |
| } |
| |
| @Test |
| public void testFindTaskAlias() { |
| final String targetActivity = "target.activity"; |
| final String aliasActivity = "alias.activity"; |
| final ComponentName target = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, |
| targetActivity); |
| final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, |
| aliasActivity); |
| final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build(); |
| final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build(); |
| task.origActivity = alias; |
| task.realActivity = target; |
| new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity( |
| targetActivity).build(); |
| |
| // Using target activity to find task. |
| final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent( |
| target).setTargetActivity(targetActivity).build(); |
| RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); |
| result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info); |
| result.process(parentTask); |
| assertThat(result.mIdealRecord).isNotNull(); |
| |
| // Using alias activity to find task. |
| final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent( |
| alias).setTargetActivity(targetActivity).build(); |
| result = new RootWindowContainer.FindTaskResult(); |
| result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info); |
| result.process(parentTask); |
| assertThat(result.mIdealRecord).isNotNull(); |
| } |
| |
| @Test |
| public void testMoveRootTaskToBackIncludingParent() { |
| final TaskDisplayArea taskDisplayArea = addNewDisplayContentAt(DisplayContent.POSITION_TOP) |
| .getDefaultTaskDisplayArea(); |
| final Task rootTask1 = createTaskForShouldBeVisibleTest(taskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */, |
| true /* twoLevelTask */); |
| 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 root task. |
| rootTask2.moveToBack("testMoveRootTaskToBackIncludingParent", rootTask2.getTopMostTask()); |
| verify(rootTask2).positionChildAtBottom(any(), eq(false) /* 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 homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); |
| final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| // Add an activity to the pinned root task so it isn't considered empty for visibility |
| // check. |
| final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) |
| .setTask(pinnedRootTask) |
| .build(); |
| |
| assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); |
| assertTrue(pinnedRootTask.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() { |
| // task not supporting split should be fullscreen for this test. |
| final Task notSupportingSplitTask = createTaskForShouldBeVisibleTest( |
| mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, |
| true /* onTop */); |
| doReturn(false).when(notSupportingSplitTask).supportsSplitScreenWindowingMode(); |
| 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 */); |
| |
| // root task not supporting split 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(notSupportingSplitTask.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); |
| |
| // root task not supporting split shouldn't be visible if one of the halves of split-screen |
| // is translucent. |
| doReturn(true).when(splitScreenPrimary).isTranslucent(any()); |
| assertFalse(notSupportingSplitTask.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); |
| |
| 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. |
| doReturn(false).when(splitScreenSecondary2).isTranslucent(any()); |
| assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenSecondary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| splitScreenSecondary2.getVisibility(null /* starting */)); |
| |
| // First split-screen secondary should be visible behind another translucent split-screen |
| // secondary. |
| doReturn(true).when(splitScreenSecondary2).isTranslucent(any()); |
| assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| splitScreenSecondary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| splitScreenSecondary2.getVisibility(null /* starting */)); |
| |
| final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); |
| |
| // 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, |
| assistantRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenPrimary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenSecondary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenSecondary2.getVisibility(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, |
| assistantRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| splitScreenPrimary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| splitScreenSecondary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| splitScreenSecondary2.getVisibility(null /* starting */)); |
| |
| // 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(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(assistantRootTask.shouldBeVisible(null /* starting */)); |
| assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); |
| assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| assistantRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenPrimary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenSecondary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenSecondary2.getVisibility(null /* starting */)); |
| } else { |
| assertFalse(assistantRootTask.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| assistantRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| splitScreenPrimary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| splitScreenSecondary.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| splitScreenSecondary2.getVisibility(null /* starting */)); |
| } |
| } |
| |
| @Test |
| public void testGetVisibility_MultiLevel() { |
| 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. |
| 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, homeRootTask.getVisibility(null /* starting */)); |
| |
| |
| // Add fullscreen translucent task that partially occludes split tasks |
| final Task translucentRootTask = createStandardRootTaskForVisibilityTest( |
| WINDOWING_MODE_FULLSCREEN, true /* translucent */); |
| // Fullscreen translucent task should be visible |
| 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 */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| splitSecondary.getVisibility(null /* starting */)); |
| // Home task should be visible behind translucent since its parent is visible behind |
| // translucent. |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| 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, homeRootTask.getVisibility(null /* starting */)); |
| } |
| |
| @Test |
| public void testGetVisibility_FullscreenBehindTranslucent() { |
| final Task bottomRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| false /* translucent */); |
| final Task translucentRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| true /* translucent */); |
| |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| bottomRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| translucentRootTask.getVisibility(null /* starting */)); |
| } |
| |
| @Test |
| public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() { |
| final Task bottomRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| false /* translucent */); |
| final Task translucentRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| true /* translucent */); |
| final Task opaqueRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| false /* translucent */); |
| |
| assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| translucentRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */)); |
| } |
| |
| @Test |
| public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() { |
| final Task bottomRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| false /* translucent */); |
| final Task opaqueRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| false /* translucent */); |
| final Task translucentRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| true /* translucent */); |
| |
| assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| opaqueRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| translucentRootTask.getVisibility(null /* starting */)); |
| } |
| |
| @Test |
| public void testGetVisibility_FullscreenTranslucentBehindTranslucent() { |
| final Task bottomTranslucentRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| true /* translucent */); |
| final Task translucentRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| true /* translucent */); |
| |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| bottomTranslucentRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| translucentRootTask.getVisibility(null /* starting */)); |
| } |
| |
| @Test |
| public void testGetVisibility_FullscreenTranslucentBehindOpaque() { |
| final Task bottomTranslucentRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| true /* translucent */); |
| final Task opaqueRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| false /* translucent */); |
| |
| assertEquals(TASK_VISIBILITY_INVISIBLE, |
| bottomTranslucentRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */)); |
| } |
| |
| @Test |
| public void testGetVisibility_FullscreenBehindTranslucentAndPip() { |
| final Task bottomRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| false /* translucent */); |
| final Task translucentRootTask = |
| createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, |
| true /* translucent */); |
| final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, |
| bottomRootTask.getVisibility(null /* starting */)); |
| assertEquals(TASK_VISIBILITY_VISIBLE, |
| 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(pinnedRootTask) |
| .build(); |
| assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */)); |
| } |
| |
| @Test |
| public void testShouldBeVisible_Finishing() { |
| final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); |
| ActivityRecord topRunningHomeActivity = homeRootTask.topRunningActivity(); |
| if (topRunningHomeActivity == null) { |
| topRunningHomeActivity = new ActivityBuilder(mAtm) |
| .setTask(homeRootTask) |
| .build(); |
| } |
| |
| final Task translucentRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| doReturn(true).when(translucentRootTask).isTranslucent(any()); |
| |
| assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); |
| assertTrue(translucentRootTask.shouldBeVisible(null /* starting */)); |
| |
| topRunningHomeActivity.finishing = true; |
| final ActivityRecord topRunningTranslucentActivity = |
| translucentRootTask.topRunningActivity(); |
| topRunningTranslucentActivity.finishing = true; |
| |
| // 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(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_FullscreenBehindTranslucentInHomeRootTask() { |
| final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); |
| |
| final ActivityRecord firstActivity = new ActivityBuilder(mAtm) |
| .setParentTask(homeRootTask) |
| .setCreateTask(true) |
| .build(); |
| final Task task = firstActivity.getTask(); |
| final ActivityRecord secondActivity = new ActivityBuilder(mAtm) |
| .setTask(task) |
| .build(); |
| |
| doReturn(false).when(secondActivity).occludesParent(); |
| homeRootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, |
| false /* preserveWindows */); |
| |
| assertTrue(firstActivity.shouldBeVisible()); |
| } |
| |
| @Test |
| public void testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindFullscreen() { |
| final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); |
| final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| doReturn(false).when(homeRootTask).isTranslucent(any()); |
| doReturn(false).when(fullscreenRootTask).isTranslucent(any()); |
| |
| // 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 testMoveHomeRootTaskBehindBottomMostVisible_NoMoveHomeBehindTranslucent() { |
| final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); |
| final Task fullscreenRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| doReturn(false).when(homeRootTask).isTranslucent(any()); |
| doReturn(true).when(fullscreenRootTask).isTranslucent(any()); |
| |
| // 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 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(homeRootTask).isTranslucent(any()); |
| doReturn(false).when(fullscreenRootTask).isTranslucent(any()); |
| |
| // 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 testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreen() { |
| final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, 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 */); |
| final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| doReturn(false).when(homeRootTask).isTranslucent(any()); |
| doReturn(false).when(fullscreenRootTask1).isTranslucent(any()); |
| doReturn(false).when(fullscreenRootTask2).isTranslucent(any()); |
| |
| // 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 |
| testMoveHomeRootTaskBehindBottomMostVisible_MoveHomeBehindFullscreenAndTranslucent() { |
| final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, 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(homeRootTask).isTranslucent(any()); |
| doReturn(false).when(fullscreenRootTask1).isTranslucent(any()); |
| doReturn(true).when(fullscreenRootTask2).isTranslucent(any()); |
| |
| // 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 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(homeRootTask).isTranslucent(any()); |
| doReturn(false).when(fullscreenRootTask1).isTranslucent(any()); |
| doReturn(false).when(fullscreenRootTask2).isTranslucent(any()); |
| |
| // 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 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(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 homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); |
| final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| assertEquals(pinnedRootTask, getRootTaskAbove(homeRootTask)); |
| |
| 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 nonAlwaysOnTopRootTask = createTaskForShouldBeVisibleTest( |
| mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, |
| true /* onTop */); |
| // 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. |
| 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 = 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(assistantRootTask).isTranslucent(any()); |
| |
| assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); |
| assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); |
| assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); |
| |
| splitScreenSecondary.moveToFront("testSplitScreenMoveToFront"); |
| |
| if (isAssistantOnTop()) { |
| assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); |
| assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); |
| assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); |
| } else { |
| assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); |
| assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); |
| assertFalse(assistantRootTask.shouldBeVisible(null /* starting */)); |
| } |
| } |
| |
| private Task createStandardRootTaskForVisibilityTest(int windowingMode, |
| boolean translucent) { |
| final Task rootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| doReturn(translucent).when(rootTask).isTranslucent(any()); |
| return rootTask; |
| } |
| |
| private Task createTaskForShouldBeVisibleTest( |
| TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) { |
| return createTaskForShouldBeVisibleTest(taskDisplayArea, |
| windowingMode, activityType, onTop, false /* twoLevelTask */); |
| } |
| |
| @SuppressWarnings("TypeParameterUnusedInFormals") |
| private Task createTaskForShouldBeVisibleTest(TaskDisplayArea taskDisplayArea, |
| int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) { |
| final Task task; |
| if (activityType == ACTIVITY_TYPE_HOME) { |
| task = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, |
| ACTIVITY_TYPE_HOME); |
| mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task, |
| false /* includingParents */); |
| } else if (twoLevelTask) { |
| task = new TaskBuilder(mSupervisor) |
| .setTaskDisplayArea(taskDisplayArea) |
| .setWindowingMode(windowingMode) |
| .setActivityType(activityType) |
| .setOnTop(onTop) |
| .setCreateActivity(true) |
| .setCreateParentTask(true) |
| .build().getRootTask(); |
| } else { |
| task = new TaskBuilder(mSupervisor) |
| .setTaskDisplayArea(taskDisplayArea) |
| .setWindowingMode(windowingMode) |
| .setActivityType(activityType) |
| .setOnTop(onTop) |
| .setCreateActivity(true) |
| .build(); |
| } |
| return task; |
| } |
| |
| @Test |
| public void testFinishDisabledPackageActivities_FinishAliveActivities() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| firstActivity.setState(STOPPED, "testFinishDisabledPackageActivities"); |
| secondActivity.setState(RESUMED, "testFinishDisabledPackageActivities"); |
| task.setResumedActivity(secondActivity, "test"); |
| |
| // Note the activities have non-null ActivityRecord.app, so it won't remove directly. |
| mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process( |
| firstActivity.packageName, null /* filterByClasses */, true /* doit */, |
| true /* evenPersistent */, UserHandle.USER_ALL, false /* onlyRemoveNoProcess */); |
| |
| // If the activity is disabled with {@link android.content.pm.PackageManager#DONT_KILL_APP} |
| // the activity should still follow the normal flow to finish and destroy. |
| assertThat(firstActivity.getState()).isEqualTo(DESTROYING); |
| assertThat(secondActivity.getState()).isEqualTo(PAUSING); |
| assertTrue(secondActivity.finishing); |
| } |
| |
| @Test |
| public void testFinishDisabledPackageActivities_RemoveNonAliveActivities() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); |
| |
| // The overlay activity is not in the disabled package but it is in the same task. |
| final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task) |
| .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build(); |
| // If the task only remains overlay activity, the task should also be removed. |
| // See {@link ActivityStack#removeFromHistory}. |
| overlayActivity.setTaskOverlay(true); |
| |
| // The activity without an app means it will be removed immediately. |
| // See {@link ActivityStack#destroyActivityLocked}. |
| activity.app = null; |
| overlayActivity.app = null; |
| |
| assertEquals(2, task.getChildCount()); |
| |
| mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process( |
| activity.packageName, null /* filterByClasses */, true /* doit */, |
| true /* evenPersistent */, UserHandle.USER_ALL, false /* onlyRemoveNoProcess */); |
| |
| // Although the overlay activity is in another package, the non-overlay activities are |
| // removed from the task. Since the overlay activity should be removed as well, the task |
| // should be empty. |
| assertFalse(task.hasChild()); |
| } |
| |
| @Test |
| public void testHandleAppDied() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| |
| // Making the first activity a task overlay means it will be removed from the task's |
| // activities as well once second activity is removed as handleAppDied processes the |
| // activity list in reverse. |
| firstActivity.setTaskOverlay(true); |
| firstActivity.app = null; |
| |
| // second activity will be immediately removed as it has no state. |
| secondActivity.setSavedState(null /* savedState */); |
| |
| assertEquals(2, task.getChildCount()); |
| |
| secondActivity.app.handleAppDied(); |
| |
| assertFalse(task.hasChild()); |
| } |
| |
| @Test |
| public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); |
| |
| activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; |
| activity.launchCount = 1; |
| activity.setSavedState(null /* savedState */); |
| |
| activity.app.handleAppDied(); |
| |
| assertEquals(1, task.getChildCount()); |
| } |
| |
| @Test |
| public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); |
| |
| activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; |
| activity.launchCount = 3; |
| activity.setSavedState(null /* savedState */); |
| |
| activity.app.handleAppDied(); |
| |
| assertFalse(task.hasChild()); |
| } |
| |
| @Test |
| public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); |
| |
| activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; |
| activity.launchCount = 1; |
| activity.setSavedState(null /* savedState */); |
| |
| activity.app.handleAppDied(); |
| |
| assertEquals(1, task.getChildCount()); |
| } |
| |
| @Test |
| public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); |
| |
| activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; |
| activity.launchCount = 3; |
| activity.setSavedState(null /* savedState */); |
| |
| activity.app.handleAppDied(); |
| |
| assertFalse(task.hasChild()); |
| } |
| |
| @Test |
| public void testCompletePauseOnResumeWhilePausingActivity() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| doReturn(true).when(bottomActivity).attachedToProcess(); |
| task.setPausingActivity(null); |
| task.setResumedActivity(bottomActivity, "test"); |
| final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING; |
| |
| task.startPausingLocked(false /* uiSleeping */, topActivity, |
| "test"); |
| verify(task).completePauseLocked(anyBoolean(), eq(topActivity)); |
| } |
| |
| @Test |
| public void testWontFinishHomeRootTaskImmediately() { |
| final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); |
| |
| ActivityRecord activity = homeRootTask.topRunningActivity(); |
| if (activity == null) { |
| activity = new ActivityBuilder(mAtm) |
| .setParentTask(homeRootTask) |
| .setCreateTask(true) |
| .build(); |
| } |
| |
| // Home root task should not be destroyed immediately. |
| final ActivityRecord activity1 = finishTopActivity(homeRootTask); |
| assertEquals(FINISHING, activity1.getState()); |
| } |
| |
| @Test |
| public void testFinishCurrentActivity() { |
| // Create 2 activities on a new display. |
| final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP); |
| final Task rootTask1 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(), |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| final Task rootTask2 = createTaskForShouldBeVisibleTest(display.getDefaultTaskDisplayArea(), |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| |
| // 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(rootTask1); |
| assertEquals(DESTROYING, activity1.getState()); |
| verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */, |
| eq(display.mDisplayId), anyBoolean(), anyBoolean()); |
| } |
| |
| private ActivityRecord finishTopActivity(Task task) { |
| final ActivityRecord activity = task.topRunningActivity(); |
| assertNotNull(activity); |
| activity.setState(STOPPED, "finishTopActivity"); |
| activity.makeFinishingLocked(); |
| activity.completeFinishing("finishTopActivity"); |
| return activity; |
| } |
| |
| @Test |
| public void testShouldSleepActivities() { |
| // 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 /* focusedRootTask */, true /*keyguardGoingAway*/, |
| true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); |
| verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/, |
| true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */); |
| |
| // 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 /* focusedRootTask */, false /*keyguardGoingAway*/, |
| true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); |
| verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/, |
| false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); |
| } |
| |
| @Test |
| public void testRootTaskOrderChangedOnRemoveRootTask() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); |
| mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); |
| try { |
| mDefaultTaskDisplayArea.removeRootTask(task); |
| } finally { |
| mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener); |
| } |
| assertTrue(listener.mChanged); |
| } |
| |
| @Test |
| public void testRootTaskOrderChangedOnAddPositionRootTask() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| mDefaultTaskDisplayArea.removeRootTask(task); |
| |
| RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); |
| mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); |
| try { |
| task.mReparenting = true; |
| mDefaultTaskDisplayArea.addChild(task, 0); |
| } finally { |
| mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener); |
| } |
| assertTrue(listener.mChanged); |
| } |
| |
| @Test |
| public void testRootTaskOrderChangedOnPositionRootTask() { |
| RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); |
| try { |
| final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest( |
| mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, |
| true /* onTop */); |
| mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); |
| mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenRootTask1, |
| false /*includingParents*/); |
| } finally { |
| mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener); |
| } |
| assertTrue(listener.mChanged); |
| } |
| |
| @Test |
| public void testNavigateUpTo() { |
| final ActivityStartController controller = mock(ActivityStartController.class); |
| final ActivityStarter starter = new ActivityStarter(controller, |
| mAtm, mAtm.mTaskSupervisor, mock(ActivityStartInterceptor.class)); |
| doReturn(controller).when(mAtm).getActivityStartController(); |
| spyOn(starter); |
| doReturn(ActivityManager.START_SUCCESS).when(starter).execute(); |
| |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task) |
| .setUid(firstActivity.getUid() + 1).build(); |
| doReturn(starter).when(controller).obtainStarter(eq(firstActivity.intent), anyString()); |
| |
| final IApplicationThread thread = secondActivity.app.getThread(); |
| secondActivity.app.setThread(null); |
| // This should do nothing from a non-attached caller. |
| assertFalse(task.navigateUpTo(secondActivity /* source record */, |
| firstActivity.intent /* destIntent */, null /* destGrants */, |
| 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); |
| |
| secondActivity.app.setThread(thread); |
| assertTrue(task.navigateUpTo(secondActivity /* source record */, |
| firstActivity.intent /* destIntent */, null /* destGrants */, |
| 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); |
| // The firstActivity uses default launch mode, so the activities between it and itself will |
| // be finished. |
| assertTrue(secondActivity.finishing); |
| assertTrue(firstActivity.finishing); |
| // The caller uid of the new activity should be the current real caller. |
| assertEquals(starter.mRequest.callingUid, secondActivity.getUid()); |
| } |
| |
| @Test |
| public void testShouldUpRecreateTaskLockedWithCorrectAffinityFormat() { |
| final String affinity = "affinity"; |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity) |
| .setUid(Binder.getCallingUid()).setCreateTask(true).build(); |
| final Task task = activity.getTask(); |
| task.affinity = activity.taskAffinity; |
| |
| assertFalse(task.shouldUpRecreateTaskLocked(activity, affinity)); |
| } |
| |
| @Test |
| public void testShouldUpRecreateTaskLockedWithWrongAffinityFormat() { |
| final String affinity = "affinity"; |
| final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity) |
| .setUid(Binder.getCallingUid()).setCreateTask(true).build(); |
| final Task task = activity.getTask(); |
| task.affinity = activity.taskAffinity; |
| final String fakeAffinity = activity.getUid() + activity.taskAffinity; |
| |
| assertTrue(task.shouldUpRecreateTaskLocked(activity, fakeAffinity)); |
| } |
| |
| @Test |
| public void testResetTaskWithFinishingActivities() { |
| final ActivityRecord taskTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); |
| final Task task = taskTop.getTask(); |
| // Make all activities in the task are finishing to simulate Task#getTopActivity |
| // returns null. |
| taskTop.finishing = true; |
| |
| final ActivityRecord newR = new ActivityBuilder(mAtm).build(); |
| final ActivityRecord result = task.resetTaskIfNeeded(taskTop, newR); |
| assertThat(result).isEqualTo(taskTop); |
| } |
| |
| @Test |
| public void testIterateOccludedActivity() { |
| final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>(); |
| final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add; |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| // Top activity occludes bottom activity. |
| doReturn(true).when(task).shouldBeVisible(any()); |
| assertTrue(topActivity.shouldBeVisible()); |
| assertFalse(bottomActivity.shouldBeVisible()); |
| |
| task.forAllOccludedActivities(handleOccludedActivity); |
| assertThat(occludedActivities).containsExactly(bottomActivity); |
| |
| // Top activity doesn't occlude parent, so the bottom activity is not occluded. |
| doReturn(false).when(topActivity).occludesParent(); |
| assertTrue(bottomActivity.shouldBeVisible()); |
| |
| occludedActivities.clear(); |
| task.forAllOccludedActivities(handleOccludedActivity); |
| assertThat(occludedActivities).isEmpty(); |
| |
| // A finishing activity should not occlude other activities behind. |
| final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build(); |
| finishingActivity.finishing = true; |
| doCallRealMethod().when(finishingActivity).occludesParent(); |
| assertTrue(topActivity.shouldBeVisible()); |
| assertTrue(bottomActivity.shouldBeVisible()); |
| |
| occludedActivities.clear(); |
| task.forAllOccludedActivities(handleOccludedActivity); |
| assertThat(occludedActivities).isEmpty(); |
| } |
| |
| @Test |
| public void testClearUnknownAppVisibilityBehindFullscreenActivity() { |
| final UnknownAppVisibilityController unknownAppVisibilityController = |
| mDefaultTaskDisplayArea.mDisplayContent.mUnknownAppVisibilityController; |
| final KeyguardController keyguardController = mSupervisor.getKeyguardController(); |
| doReturn(true).when(keyguardController).isKeyguardLocked(); |
| |
| // Start 2 activities that their processes have not yet started. |
| final ActivityRecord[] activities = new ActivityRecord[2]; |
| mSupervisor.beginDeferResume(); |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| for (int i = 0; i < activities.length; i++) { |
| final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build(); |
| activities[i] = r; |
| doReturn(null).when(mAtm).getProcessController( |
| eq(r.processName), eq(r.info.applicationInfo.uid)); |
| r.setState(Task.ActivityState.INITIALIZING, "test"); |
| // Ensure precondition that the activity is opaque. |
| assertTrue(r.occludesParent()); |
| mSupervisor.startSpecificActivity(r, false /* andResume */, |
| false /* checkConfig */); |
| } |
| mSupervisor.endDeferResume(); |
| |
| setBooted(mAtm); |
| // 2 activities are started while keyguard is locked, so they are waiting to be resolved. |
| assertFalse(unknownAppVisibilityController.allResolved()); |
| |
| // Assume the top activity is going to resume and |
| // {@link RootWindowContainer#cancelInitializingActivities} should clear the unknown |
| // visibility records that are occluded. |
| task.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); |
| // Assume the top activity relayouted, just remove it directly. |
| unknownAppVisibilityController.appRemovedOrHidden(activities[1]); |
| // All unresolved records should be removed. |
| assertTrue(unknownAppVisibilityController.allResolved()); |
| } |
| |
| @Test |
| public void testNonTopVisibleActivityNotResume() { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final ActivityRecord nonTopVisibleActivity = |
| new ActivityBuilder(mAtm).setTask(task).build(); |
| new ActivityBuilder(mAtm).setTask(task).build(); |
| // The scenario we are testing is when the app isn't visible yet. |
| nonTopVisibleActivity.setVisible(false); |
| nonTopVisibleActivity.mVisibleRequested = false; |
| doReturn(false).when(nonTopVisibleActivity).attachedToProcess(); |
| doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked(); |
| doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(), |
| anyBoolean()); |
| |
| task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, |
| false /* preserveWindows */); |
| verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */, |
| anyBoolean()); |
| } |
| |
| private boolean isAssistantOnTop() { |
| return mContext.getResources().getBoolean( |
| com.android.internal.R.bool.config_assistantOnTopOfDream); |
| } |
| |
| private void verifyShouldSleepActivities(boolean focusedRootTask, |
| boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, |
| boolean expected) { |
| final Task task = new TaskBuilder(mSupervisor).build(); |
| final DisplayContent display = mock(DisplayContent.class); |
| final KeyguardController keyguardController = mSupervisor.getKeyguardController(); |
| display.isDefaultDisplay = isDefaultDisplay; |
| |
| task.mDisplayContent = display; |
| doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); |
| doReturn(displaySleeping).when(display).isSleeping(); |
| doReturn(focusedRootTask).when(task).isFocusedRootTaskOnDisplay(); |
| |
| assertEquals(expected, task.shouldSleepActivities()); |
| } |
| |
| private static class RootTaskOrderChangedListener |
| implements TaskDisplayArea.OnRootTaskOrderChangedListener { |
| public boolean mChanged = false; |
| |
| @Override |
| public void onRootTaskOrderChanged(Task rootTask) { |
| mChanged = true; |
| } |
| } |
| } |