blob: e3bb1b6ca9f36128b1129e277ec935b802676d9d [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for the {@link DisplayContent} class.
*
* Build/Install/Run:
* atest WmTests:ActivityDisplayTests
*/
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
// TODO(b/144248496): Merge to DisplayContentTests
public class ActivityDisplayTests extends ActivityTestsBase {
@Test
public void testLastFocusedStackIsUpdatedWhenMovingStack() {
// Create a stack at bottom.
final TaskDisplayArea taskDisplayAreas =
mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
final ActivityStack stack =
new StackBuilder(mRootWindowContainer).setOnTop(!ON_TOP).build();
final ActivityStack prevFocusedStack = taskDisplayAreas.getFocusedStack();
stack.moveToFront("moveStackToFront");
// After moving the stack to front, the previous focused should be the last focused.
assertTrue(stack.isFocusedStackOnDisplay());
assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedStack());
stack.moveToBack("moveStackToBack", null /* task */);
// After moving the stack to back, the stack should be the last focused.
assertEquals(stack, taskDisplayAreas.getLastFocusedStack());
}
/**
* This test simulates the picture-in-picture menu activity launches an activity to fullscreen
* stack. The fullscreen stack should be the top focused for resuming correctly.
*/
@Test
public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
// Create a pinned stack and move to front.
final ActivityStack pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea()
.createStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
final Task pinnedTask = new TaskBuilder(mService.mStackSupervisor)
.setStack(pinnedStack).build();
new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
.setTask(pinnedTask).build();
pinnedStack.moveToFront("movePinnedStackToFront");
// The focused stack should be the pinned stack.
assertTrue(pinnedStack.isFocusedStackOnDisplay());
// Create a fullscreen stack and move to front.
final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
mRootWindowContainer.getDefaultDisplay());
fullscreenStack.moveToFront("moveFullscreenStackToFront");
// The focused stack should be the fullscreen stack.
assertTrue(fullscreenStack.isFocusedStackOnDisplay());
}
/**
* Test {@link TaskDisplayArea#mPreferredTopFocusableStack} will be cleared when
* the stack is removed or moved to back, and the focused stack will be according to z-order.
*/
@Test
public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
// Create a display which only contains 2 stacks.
final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
// Put stack1 and stack2 on top.
stack1.moveToFront("moveStack1ToFront");
stack2.moveToFront("moveStack2ToFront");
assertTrue(stack2.isFocusedStackOnDisplay());
// Stack1 should be focused after moving stack2 to back.
stack2.moveToBack("moveStack2ToBack", null /* task */);
assertTrue(stack1.isFocusedStackOnDisplay());
// Stack2 should be focused after removing stack1.
stack1.getDisplayArea().removeStack(stack1);
assertTrue(stack2.isFocusedStackOnDisplay());
}
/**
* Verifies {@link DisplayContent#remove} should not resume home stack on the removing display.
*/
@Test
public void testNotResumeHomeStackOnRemovingDisplay() {
// Create a display which supports system decoration and allows reparenting stacks to
// another display when the display is removed.
final DisplayContent display = new TestDisplayContent.Builder(
mService, 1000, 1500).setSystemDecorations(true).build();
doReturn(false).when(display).shouldDestroyContentOnRemove();
// Put home stack on the display.
final ActivityStack homeStack = new StackBuilder(mRootWindowContainer)
.setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
// Put a finishing standard activity which will be reparented.
final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display);
stack.topRunningActivity().makeFinishingLocked();
clearInvocations(homeStack);
display.remove();
// The removed display should have no focused stack and its home stack should never resume.
assertNull(display.getFocusedStack());
verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
}
private ActivityStack createFullscreenStackWithSimpleActivityAt(DisplayContent display) {
final ActivityStack fullscreenStack = display.getDefaultTaskDisplayArea().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
final Task fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
.setStack(fullscreenStack).build();
new ActivityBuilder(mService).setTask(fullscreenTask).build();
return fullscreenStack;
}
/**
* Verifies the correct activity is returned when querying the top running activity.
*/
@Test
public void testTopRunningActivity() {
final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
final KeyguardController keyguard = mSupervisor.getKeyguardController();
final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
final ActivityRecord activity = stack.getTopNonFinishingActivity();
// Create empty stack on top.
final ActivityStack emptyStack =
new StackBuilder(mRootWindowContainer).setCreateActivity(false).build();
// Make sure the top running activity is not affected when keyguard is not locked.
assertTopRunningActivity(activity, display);
// Check to make sure activity not reported when it cannot show on lock and lock is on.
doReturn(true).when(keyguard).isKeyguardLocked();
assertEquals(activity, display.topRunningActivity());
assertNull(display.topRunningActivity(true /* considerKeyguardState */));
// Move stack with activity to top.
stack.moveToFront("testStackToFront");
assertEquals(stack, display.getFocusedStack());
assertEquals(activity, display.topRunningActivity());
assertNull(display.topRunningActivity(true /* considerKeyguardState */));
// Add activity that should be shown on the keyguard.
final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
.setCreateTask(true)
.setStack(stack)
.setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
.build();
// Ensure the show when locked activity is returned.
assertTopRunningActivity(showWhenLockedActivity, display);
// Move empty stack to front. The running activity in focusable stack which below the
// empty stack should be returned.
emptyStack.moveToFront("emptyStackToFront");
assertEquals(stack, display.getFocusedStack());
assertTopRunningActivity(showWhenLockedActivity, display);
}
private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) {
assertEquals(top, display.topRunningActivity());
assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
}
/**
* This test enforces that alwaysOnTop stack is placed at proper position.
*/
@Test
public void testAlwaysOnTopStackLocation() {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final ActivityStack alwaysOnTopStack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(alwaysOnTopStack).build();
alwaysOnTopStack.setAlwaysOnTop(true);
taskDisplayArea.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
assertTrue(alwaysOnTopStack.isAlwaysOnTop());
// Ensure always on top state is synced to the children of the stack.
assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
assertEquals(alwaysOnTopStack, taskDisplayArea.getTopStack());
final ActivityStack pinnedStack = taskDisplayArea.createStack(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask());
assertEquals(pinnedStack, taskDisplayArea.getTopStack());
final ActivityStack anotherAlwaysOnTopStack = taskDisplayArea.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
int topPosition = taskDisplayArea.getStackCount() - 1;
// Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
// existing alwaysOnTop stack.
assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1));
final ActivityStack nonAlwaysOnTopStack = taskDisplayArea.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea());
topPosition = taskDisplayArea.getStackCount() - 1;
// Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
// existing other non-alwaysOnTop stacks.
assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3));
anotherAlwaysOnTopStack.setAlwaysOnTop(false);
taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
// Ensure, when always on top is turned off for a stack, the stack is put just below all
// other always on top stacks.
assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2));
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
// Ensure always on top state changes properly when windowing mode changes.
anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2));
anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1));
final ActivityStack dreamStack = taskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
assertEquals(taskDisplayArea, dreamStack.getDisplayArea());
assertTrue(dreamStack.isAlwaysOnTop());
topPosition = taskDisplayArea.getStackCount() - 1;
// Ensure dream shows above all activities, including PiP
assertEquals(dreamStack, taskDisplayArea.getTopStack());
assertEquals(pinnedStack, taskDisplayArea.getStackAt(topPosition - 1));
final ActivityStack assistStack = taskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
assertEquals(taskDisplayArea, assistStack.getDisplayArea());
assertFalse(assistStack.isAlwaysOnTop());
topPosition = taskDisplayArea.getStackCount() - 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(assistStack, taskDisplayArea.getStackAt(
isAssistantOnTop ? topPosition : topPosition - 4));
}
@Test
public void testRemoveStackInWindowingModes() {
removeStackTests(() -> mRootWindowContainer.removeStacksInWindowingModes(
WINDOWING_MODE_FULLSCREEN));
}
@Test
public void testRemoveStackWithActivityTypes() {
removeStackTests(
() -> mRootWindowContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD));
}
private void removeStackTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final ActivityStack stack1 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
final ActivityStack stack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
final ActivityStack stack3 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
final ActivityStack stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
final Task task1 = new TaskBuilder(mService.mStackSupervisor).setStack(stack1).build();
final Task task2 = new TaskBuilder(mService.mStackSupervisor).setStack(stack2).build();
final Task task3 = new TaskBuilder(mService.mStackSupervisor).setStack(stack3).build();
final Task task4 = new TaskBuilder(mService.mStackSupervisor).setStack(stack4).build();
// Reordering stacks while removing stacks.
doAnswer(invocation -> {
taskDisplayArea.positionStackAtTop(stack3, false);
return true;
}).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
// Removing stacks from the display while removing stacks.
doAnswer(invocation -> {
taskDisplayArea.removeStack(stack2);
return true;
}).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
runnable.run();
verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any());
verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any());
}
}