blob: e14a64ed50c0ae146244d15fe6ec1f67e9b596b5 [file] [log] [blame]
/*
* Copyright (C) 2019 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 android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.server.wm.ComponentNameUtils.getWindowName;
import static android.server.wm.StateLogger.logE;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.WindowManagerState.STATE_STOPPED;
import static android.server.wm.WindowManagerState.TRANSIT_TASK_CLOSE;
import static android.server.wm.WindowManagerState.TRANSIT_TASK_OPEN;
import static android.server.wm.app.Components.BOTTOM_ACTIVITY;
import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
import static android.server.wm.app.Components.LAUNCH_TEST_ON_DESTROY_ACTIVITY;
import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY;
import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_ATTR_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.app.Components.TOAST_ACTIVITY;
import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
import static android.server.wm.app27.Components.SDK_27_SEPARATE_PROCESS_ACTIVITY;
import static android.server.wm.app27.Components.SDK_27_TEST_ACTIVITY;
import static android.server.wm.lifecycle.ActivityStarterTests.StandardActivity;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.Presubmit;
import android.server.wm.CommandSession.ActivityCallback;
import android.server.wm.CommandSession.ActivitySession;
import android.server.wm.CommandSession.SizeInfo;
import android.server.wm.WindowManagerState.Task;
import android.server.wm.WindowManagerState.DisplayContent;
import org.junit.Before;
import org.junit.Test;
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:MultiDisplayPolicyTests
*
* Tests each expected policy on multi-display environment.
*/
@Presubmit
@android.server.wm.annotation.Group3
public class MultiDisplayPolicyTests extends MultiDisplayTestBase {
@Before
@Override
public void setUp() throws Exception {
super.setUp();
assumeTrue(supportsMultiDisplay());
}
/**
* Tests that all activities that were on the private display are destroyed on display removal.
*/
@Test
public void testContentDestroyOnDisplayRemoved() {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
// Create new private virtual display.
final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activities on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Launched activity must be resumed");
launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Launched activity must be resumed");
separateTestJournal();
// Destroy the display and check if activities are removed from system.
}
mWmState.waitForActivityRemoved(TEST_ACTIVITY);
mWmState.waitForActivityRemoved(RESIZEABLE_ACTIVITY);
// Check AM state.
assertFalse("Activity from removed display must be destroyed",
mWmState.containsActivity(TEST_ACTIVITY));
assertFalse("Activity from removed display must be destroyed",
mWmState.containsActivity(RESIZEABLE_ACTIVITY));
// Check WM state.
assertFalse("Activity windows from removed display must be destroyed",
mWmState.containsWindow(getWindowName(TEST_ACTIVITY)));
assertFalse("Activity windows from removed display must be destroyed",
mWmState.containsWindow(getWindowName(RESIZEABLE_ACTIVITY)));
// Check activity logs.
assertActivityDestroyed(TEST_ACTIVITY);
assertActivityDestroyed(RESIZEABLE_ACTIVITY);
}
/**
* Tests that newly launched activity will be landing on default display on display removal.
*/
@Test
public void testActivityLaunchOnContentDestroyDisplayRemoved() {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
// Create new private virtual display.
final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activities on new secondary display.
launchActivityOnDisplay(LAUNCH_TEST_ON_DESTROY_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(LAUNCH_TEST_ON_DESTROY_ACTIVITY, STATE_RESUMED,
newDisplay.mId,"Launched activity must be resumed on secondary display");
// Destroy the display
}
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Newly launches activity should be landing on default display");
}
/**
* Tests that the update of display metrics updates all its content.
*/
@Test
public void testDisplayResize() {
final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
// Create new virtual display.
final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch a resizeable activity on new secondary display.
separateTestJournal();
launchActivityOnDisplay(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Launched activity must be resumed");
// Grab reported sizes and compute new with slight size change.
final SizeInfo initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
// Resize the display
separateTestJournal();
virtualDisplaySession.resizeDisplay();
mWmState.waitForWithAmState(amState -> {
try {
return amState.hasActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED)
&& new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY)
.getCount(ActivityCallback.ON_CONFIGURATION_CHANGED) == 1;
} catch (Exception e) {
logE("Error waiting for valid state: " + e.getMessage());
return false;
}
}, "the configuration change to happen and activity to be resumed");
mWmState.computeState(
new WaitForValidActivityState(RESIZEABLE_ACTIVITY),
new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true);
// Check if activity in virtual display was resized properly.
assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
1 /* numConfigChange */);
final SizeInfo updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
assertTrue(updatedSize.widthDp <= initialSize.widthDp);
assertTrue(updatedSize.heightDp <= initialSize.heightDp);
assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
}
/**
* Tests that when primary display is rotated secondary displays are not affected.
*/
@Test
public void testRotationNotAffectingSecondaryScreen() {
final VirtualDisplayLauncher virtualLauncher =
mObjectTracker.manage(new VirtualDisplayLauncher());
// Create new virtual display.
final DisplayContent newDisplay = virtualLauncher.setResizeDisplay(false).createDisplay();
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activity on new secondary display.
final ActivitySession resizeableActivitySession =
virtualLauncher.launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay);
waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Top activity must be on secondary display");
final SizeInfo initialSize = resizeableActivitySession.getConfigInfo().sizeInfo;
assertNotNull("Test activity must have reported initial size on launch", initialSize);
final RotationSession rotationSession = createManagedRotationSession();
// Rotate primary display and check that activity on secondary display is not affected.
rotateAndCheckSameSizes(rotationSession, resizeableActivitySession, initialSize);
// Launch activity to secondary display when primary one is rotated.
final int initialRotation = mWmState.getRotation();
rotationSession.set((initialRotation + 1) % 4);
final ActivitySession testActivitySession =
virtualLauncher.launchActivityOnDisplay(TEST_ACTIVITY, newDisplay);
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Top activity must be on secondary display");
final SizeInfo testActivitySize = testActivitySession.getConfigInfo().sizeInfo;
assertEquals("Sizes of secondary display must not change after rotation of primary"
+ " display", initialSize, testActivitySize);
}
private void rotateAndCheckSameSizes(RotationSession rotationSession,
ActivitySession activitySession, SizeInfo initialSize) {
for (int rotation = 3; rotation >= 0; --rotation) {
rotationSession.set(rotation);
final SizeInfo rotatedSize = activitySession.getConfigInfo().sizeInfo;
assertEquals("Sizes must not change after rotation", initialSize, rotatedSize);
}
}
/**
* Tests that turning the primary display off does not affect the activity running
* on an external secondary display.
*/
@Test
public void testExternalDisplayActivityTurnPrimaryOff() {
// Launch something on the primary display so we know there is a resumed activity there
launchActivity(RESIZEABLE_ACTIVITY);
waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
"Activity launched on primary display must be resumed");
final DisplayContent newDisplay = createManagedExternalDisplaySession()
.setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
// Check that the activity is launched onto the external display
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Activity launched on external display must be resumed");
mWmState.assertFocusedAppOnDisplay("App on default display must still be focused",
RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY);
separateTestJournal();
mObjectTracker.manage(new PrimaryDisplayStateSession()).turnScreenOff();
// Wait for the fullscreen stack to start sleeping, and then make sure the
// test activity is still resumed.
final ActivityLifecycleCounts counts = new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY);
if (!Condition.waitFor(counts.countWithRetry(RESIZEABLE_ACTIVITY + " to be stopped",
countSpec(ActivityCallback.ON_STOP, CountSpec.EQUALS, 1)))) {
fail(RESIZEABLE_ACTIVITY + " has received "
+ counts.getCount(ActivityCallback.ON_STOP)
+ " onStop() calls, expecting 1");
}
// For this test we create this virtual display with flag showContentWhenLocked, so it
// cannot be effected when default display screen off.
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Activity launched on external display must be resumed");
}
/**
* Tests that turning the secondary display off stops activities running and makes invisible
* on that display.
*/
@Test
public void testExternalDisplayToggleState() {
final ExternalDisplaySession externalDisplaySession = createManagedExternalDisplaySession();
final DisplayContent newDisplay = externalDisplaySession.createVirtualDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
// Check that the test activity is resumed on the external display
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Activity launched on external display must be resumed");
externalDisplaySession.turnDisplayOff();
// Check that turning off the external display stops the activity, and makes it
// invisible.
waitAndAssertActivityState(TEST_ACTIVITY, STATE_STOPPED,
"Activity launched on external display must be stopped after turning off");
mWmState.assertVisibility(TEST_ACTIVITY, false /* visible */);
externalDisplaySession.turnDisplayOn();
// Check that turning on the external display resumes the activity
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Activity launched on external display must be resumed");
}
/**
* Tests no leaking after external display removed.
*/
@Test
public void testNoLeakOnExternalDisplay() throws Exception {
// How this test works:
// When receiving the request to remove a display and some activities still exist on that
// display, it will finish those activities first, so the display won't be removed
// immediately. Then, when all activities were destroyed, the display removes itself.
// Get display count before testing, as some devices may have more than one built-in
// display.
mWmState.computeState();
final int displayCount = mWmState.getDisplayCount();
try (final VirtualDisplaySession externalDisplaySession = new VirtualDisplaySession()) {
final DisplayContent newDisplay = externalDisplaySession
.setSimulateDisplay(true).createDisplay();
launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
"Virtual activity should be Top Resumed Activity.");
mWmState.assertFocusedAppOnDisplay("Activity on second display must be focused.",
VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
}
mWmState.waitFor((amState) -> amState.getDisplayCount() == displayCount,
"external displays to be removed");
assertEquals(displayCount, mWmState.getDisplayCount());
assertEquals(displayCount, mWmState.getKeyguardControllerState().
mKeyguardOccludedStates.size());
}
/**
* Tests launching activities on secondary and then on primary display to see if the stack
* visibility is not affected.
*/
@Test
public void testLaunchActivitiesAffectsVisibility() {
// Start launching activity.
launchActivity(LAUNCHING_ACTIVITY);
// Create new virtual display.
final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
mWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activity on primary display and check if it doesn't affect activity on
// secondary display.
getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY).execute();
mWmState.waitForValidState(RESIZEABLE_ACTIVITY);
mWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY),
pair(newDisplay.mId, TEST_ACTIVITY));
}
/**
* Test that move-task works when moving between displays.
*/
@Test
public void testMoveTaskBetweenDisplays() {
// Create new virtual display.
final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mWmState.assertFocusedActivity("Virtual display activity must be on top",
VIRTUAL_DISPLAY_ACTIVITY);
final int defaultDisplayStackId = mWmState.getFocusedTaskId();
Task frontStack = mWmState.getRootTask(
defaultDisplayStackId);
assertEquals("Top stack must remain on primary display",
DEFAULT_DISPLAY, frontStack.mDisplayId);
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Top activity must be on secondary display");
assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY),
pair(newDisplay.mId, TEST_ACTIVITY));
// Move activity from secondary display to primary.
moveActivityToRootTaskOrOnTop(TEST_ACTIVITY, defaultDisplayStackId);
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Moved activity must be on top");
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
* This version launches virtual display creator to fullscreen stack in split-screen.
*/
@Test
public void testStackFocusSwitchOnDisplayRemoved() {
assumeTrue(supportsSplitScreenMultiWindow());
// Start launching activity into docked stack.
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
mWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
WINDOWING_MODE_MULTI_WINDOW);
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
* This version launches virtual display creator to docked stack in split-screen.
*/
@Test
public void testStackFocusSwitchOnDisplayRemoved2() {
assumeTrue(supportsSplitScreenMultiWindow());
// Setup split-screen.
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY));
mWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
WINDOWING_MODE_MULTI_WINDOW);
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
* This version works without split-screen.
*/
@Test
public void testStackFocusSwitchOnDisplayRemoved3() {
// Start an activity on default display to determine default stack.
launchActivity(BROADCAST_RECEIVER_ACTIVITY);
final int focusedStackWindowingMode = mWmState.getFrontRootTaskWindowingMode(
DEFAULT_DISPLAY);
// Finish probing activity.
mBroadcastActionTrigger.finishBroadcastReceiverActivity();
tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */,
focusedStackWindowingMode);
}
/**
* Create a virtual display, launch a test activity there, destroy the display and check if test
* activity is moved to a stack on the default display.
*/
private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode) {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
// Create new virtual display.
final DisplayContent newDisplay = virtualDisplaySession
.setPublicDisplay(true)
.setLaunchInSplitScreen(splitScreen)
.createDisplay();
if (splitScreen) {
// Set the secondary split root task as launch root to verify remaining tasks will
// be reparented to matching launch root after removed the virtual display.
mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
}
// Launch activity on new secondary display.
launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Test activity must be on secondary display");
separateTestJournal();
// Destroy virtual display.
}
mWmState.computeState();
assertActivityLifecycle(RESIZEABLE_ACTIVITY, false /* relaunched */);
mWmState.waitForValidState(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY)
.setWindowingMode(windowingMode)
.setActivityType(ACTIVITY_TYPE_STANDARD)
.build());
mWmState.assertValidity();
// Check if the top activity is now back on primary display.
mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
mWmState.assertFocusedRootTask(
"Default stack on primary display must be focused after display removed",
windowingMode, ACTIVITY_TYPE_STANDARD);
mWmState.assertFocusedActivity(
"Focus must be switched back to activity on primary display",
RESIZEABLE_ACTIVITY);
}
/**
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
*/
@Test
public void testStackFocusSwitchOnStackEmptiedInSleeping() {
assumeTrue(supportsLockScreen());
validateStackFocusSwitchOnStackEmptied(createManagedVirtualDisplaySession(),
createManagedLockScreenSession());
}
/**
* Tests launching activities on secondary display and then finishing it to see if stack focus
* is moved correctly.
*/
@Test
public void testStackFocusSwitchOnStackEmptied() {
validateStackFocusSwitchOnStackEmptied(createManagedVirtualDisplaySession(),
null /* lockScreenSession */);
}
private void validateStackFocusSwitchOnStackEmptied(VirtualDisplaySession virtualDisplaySession,
LockScreenSession lockScreenSession) {
if (lockScreenSession != null) {
lockScreenSession.setLockCredential();
}
// Create new virtual display.
final DisplayContent newDisplay = virtualDisplaySession.createDisplay();
mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
// Launch activity on new secondary display.
launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED,
newDisplay.mId,"Top activity must be on secondary display");
if (lockScreenSession != null) {
// Lock the device, so that activity containers will be detached.
lockScreenSession.sleepDevice();
}
// Finish activity on secondary display.
mBroadcastActionTrigger.finishBroadcastReceiverActivity();
if (lockScreenSession != null) {
// Unlock and check if the focus is switched back to primary display.
lockScreenSession.wakeUpDevice().enterAndConfirmLockCredential();
}
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
"Top activity must be switched back to primary display");
}
/**
* Tests that input events on the primary display take focus from the virtual display.
*/
@Test
public void testStackFocusSwitchOnTouchEvent() {
// If config_perDisplayFocusEnabled, the focus will not move even if touching on
// the Activity in the different display.
assumeFalse(perDisplayFocusEnabled());
// Create new virtual display.
final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay();
mWmState.computeState(VIRTUAL_DISPLAY_ACTIVITY);
mWmState.assertFocusedActivity("Top activity must be the latest launched one",
VIRTUAL_DISPLAY_ACTIVITY);
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Activity launched on secondary display must be resumed");
tapOnDisplayCenter(DEFAULT_DISPLAY);
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, DEFAULT_DISPLAY,
"Top activity must be on the primary display");
assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, VIRTUAL_DISPLAY_ACTIVITY),
pair(newDisplay.mId, TEST_ACTIVITY));
tapOnDisplayCenter(newDisplay.mId);
mWmState.waitForValidState(TEST_ACTIVITY);
mWmState.assertFocusedAppOnDisplay("App on secondary display must be focused",
TEST_ACTIVITY, newDisplay.mId);
}
/**
* Tests that tapping on the primary display after showing the keyguard resumes the
* activity on the primary display.
*/
@Test
public void testStackFocusSwitchOnTouchEventAfterKeyguard() {
assumeFalse(perDisplayFocusEnabled());
assumeTrue(supportsLockScreen());
// Launch something on the primary display so we know there is a resumed activity there
launchActivity(RESIZEABLE_ACTIVITY);
waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
"Activity launched on primary display must be resumed");
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
lockScreenSession.sleepDevice();
// Make sure there is no resumed activity when the primary display is off
waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED,
"Activity launched on primary display must be stopped after turning off");
assertEquals("Unexpected resumed activity",
0, mWmState.getResumedActivitiesCount());
final DisplayContent newDisplay = createManagedExternalDisplaySession()
.setCanShowWithInsecureKeyguard(true).createVirtualDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
// Unlock the device and tap on the middle of the primary display
lockScreenSession.wakeUpDevice();
executeShellCommand("wm dismiss-keyguard");
mWmState.waitForKeyguardGone();
mWmState.waitForValidState(RESIZEABLE_ACTIVITY, TEST_ACTIVITY);
// Check that the test activity is resumed on the external display and is on top
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Activity on external display must be resumed");
assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY),
pair(newDisplay.mId, TEST_ACTIVITY));
tapOnDisplayCenter(DEFAULT_DISPLAY);
// Check that the activity on the primary display is the topmost resumed
waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
"Activity on primary display must be resumed and on top");
assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, RESIZEABLE_ACTIVITY),
pair(newDisplay.mId, TEST_ACTIVITY));
}
/**
* Tests that showWhenLocked works on a secondary display.
*/
@Test
public void testSecondaryDisplayShowWhenLocked() {
assumeTrue(supportsSecureLock());
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
lockScreenSession.setLockCredential();
launchActivity(TEST_ACTIVITY);
final DisplayContent newDisplay = createManagedExternalDisplaySession()
.createVirtualDisplay();
launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, newDisplay.mId);
lockScreenSession.gotoKeyguard();
waitAndAssertActivityState(TEST_ACTIVITY, STATE_STOPPED,
"Expected stopped activity on default display");
waitAndAssertActivityStateOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY, STATE_RESUMED,
newDisplay.mId, "Expected resumed activity on secondary display");
}
/**
* Tests tap and set focus between displays.
*/
@Test
public void testSecondaryDisplayFocus() {
assumeFalse(perDisplayFocusEnabled());
launchActivity(TEST_ACTIVITY);
mWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
final DisplayContent newDisplay = createManagedVirtualDisplaySession()
.setSimulateDisplay(true).createDisplay();
launchActivityOnDisplay(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
"Virtual activity should be Top Resumed Activity.");
mWmState.assertFocusedAppOnDisplay("Activity on second display must be focused.",
VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
tapOnDisplayCenter(DEFAULT_DISPLAY);
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Activity should be top resumed when tapped.");
mWmState.assertFocusedActivity("Activity on default display must be top focused.",
TEST_ACTIVITY);
tapOnDisplayCenter(newDisplay.mId);
waitAndAssertTopResumedActivity(VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId,
"Virtual display activity should be top resumed when tapped.");
mWmState.assertFocusedActivity("Activity on second display must be top focused.",
VIRTUAL_DISPLAY_ACTIVITY);
mWmState.assertFocusedAppOnDisplay(
"Activity on default display must be still focused.",
TEST_ACTIVITY, DEFAULT_DISPLAY);
}
/**
* Tests that toast works on a secondary display.
*/
@Test
public void testSecondaryDisplayShowToast() {
final DisplayContent newDisplay = createManagedVirtualDisplaySession()
.setPublicDisplay(true)
.createDisplay();
final String TOAST_NAME = "Toast";
launchActivityOnDisplay(TOAST_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(TOAST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Activity launched on external display must be resumed");
assertTrue("Toast window must be shown", mWmState.waitForWithAmState(
state -> state.containsWindow(TOAST_NAME), "toast window to show"));
assertTrue("Toast window must be visible",
mWmState.isWindowSurfaceShown(TOAST_NAME));
}
/**
* Tests that the surface size of a fullscreen task is same as its display's surface size.
* Also check that the surface size has updated after reparenting to other display.
*/
@Test
public void testTaskSurfaceSizeAfterReparentDisplay() {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
// Create new simulated display and launch an activity on it.
final DisplayContent newDisplay = virtualDisplaySession.setSimulateDisplay(true)
.createDisplay();
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId,
"Top activity must be the newly launched one");
assertTopTaskSameSurfaceSizeWithDisplay(newDisplay.mId);
separateTestJournal();
// Destroy the display.
}
// Activity must be reparented to default display and relaunched.
assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */);
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Top activity must be reparented to default display");
// Check the surface size after task was reparented to default display.
assertTopTaskSameSurfaceSizeWithDisplay(DEFAULT_DISPLAY);
}
private void assertTopTaskSameSurfaceSizeWithDisplay(int displayId) {
final DisplayContent display = mWmState.getDisplay(displayId);
final int stackId = mWmState.getFrontRootTaskId(displayId);
final Task task = mWmState.getRootTask(stackId).getTopTask();
assertEquals("Task must have same surface width with its display",
display.getSurfaceSize(), task.getSurfaceWidth());
assertEquals("Task must have same surface height with its display",
display.getSurfaceSize(), task.getSurfaceHeight());
}
@Test
public void testAppTransitionForActivityOnDifferentDisplay() {
final TestActivitySession<StandardActivity> transitionActivitySession =
createManagedTestActivitySession();
// Create new simulated display.
final DisplayContent newDisplay = createManagedVirtualDisplaySession()
.setSimulateDisplay(true).createDisplay();
// Launch BottomActivity on top of launcher activity to prevent transition state
// affected by wallpaper theme.
launchActivityOnDisplay(BOTTOM_ACTIVITY, DEFAULT_DISPLAY);
waitAndAssertTopResumedActivity(BOTTOM_ACTIVITY, DEFAULT_DISPLAY,
"Activity must be resumed");
// Launch StandardActivity on default display, verify last transition if is correct.
transitionActivitySession.launchTestActivityOnDisplaySync(StandardActivity.class,
DEFAULT_DISPLAY);
mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
mWmState.assertValidity();
assertEquals(TRANSIT_TASK_OPEN,
mWmState.getDisplay(DEFAULT_DISPLAY).getLastTransition());
// Finish current activity & launch another TestActivity in virtual display in parallel.
transitionActivitySession.finishCurrentActivityNoWait();
launchActivityOnDisplayNoWait(TEST_ACTIVITY, newDisplay.mId);
mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
mWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId);
mWmState.assertValidity();
// Verify each display's last transition if is correct as expected.
assertEquals(TRANSIT_TASK_CLOSE,
mWmState.getDisplay(DEFAULT_DISPLAY).getLastTransition());
assertEquals(TRANSIT_TASK_OPEN,
mWmState.getDisplay(newDisplay.mId).getLastTransition());
}
@Test
public void testNoTransitionWhenMovingActivityToDisplay() throws Exception {
// Create new simulated display & capture new display's transition state.
final DisplayContent newDisplay = createManagedVirtualDisplaySession()
.setSimulateDisplay(true).createDisplay();
// Launch TestActivity in virtual display & capture its transition state.
launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId);
mWmState.waitForAppTransitionIdleOnDisplay(newDisplay.mId);
mWmState.assertValidity();
final String lastTranstionOnVirtualDisplay = mWmState
.getDisplay(newDisplay.mId).getLastTransition();
// Move TestActivity from virtual display to default display.
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
.allowMultipleInstances(false).setNewTask(true)
.setDisplayId(DEFAULT_DISPLAY).execute();
// Verify TestActivity moved to virtual display.
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Existing task must be brought to front");
// Make sure last transition will not change when task move to another display.
assertEquals(lastTranstionOnVirtualDisplay,
mWmState.getDisplay(newDisplay.mId).getLastTransition());
}
@Test
public void testPreQTopProcessResumedActivity() {
final DisplayContent newDisplay = createManagedVirtualDisplaySession()
.setSimulateDisplay(true).createDisplay();
getLaunchActivityBuilder().setUseInstrumentation()
.setTargetActivity(SDK_27_TEST_ACTIVITY).setNewTask(true)
.setDisplayId(newDisplay.mId).execute();
waitAndAssertTopResumedActivity(SDK_27_TEST_ACTIVITY, newDisplay.mId,
"Activity launched on secondary display must be resumed and focused");
getLaunchActivityBuilder().setUseInstrumentation()
.setTargetActivity(SDK_27_LAUNCHING_ACTIVITY).setNewTask(true)
.setDisplayId(DEFAULT_DISPLAY).setWindowingMode(WINDOWING_MODE_FULLSCREEN)
.execute();
waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
"Activity launched on default display must be resumed and focused");
assertEquals("There must be only one resumed activity in the package.", 1,
mWmState.getResumedActivitiesCountInPackage(
SDK_27_LAUNCHING_ACTIVITY.getPackageName()));
// Start SeparateProcessActivity in the same task as LaunchingActivity by setting
// allowMultipleInstances to false, and the TestActivity should be resumed.
getLaunchActivityBuilder().setUseInstrumentation()
.setTargetActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY).setNewTask(true)
.setDisplayId(DEFAULT_DISPLAY).setWindowingMode(WINDOWING_MODE_FULLSCREEN)
.allowMultipleInstances(false).execute();
waitAndAssertTopResumedActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY, DEFAULT_DISPLAY,
"Activity launched on default display must be resumed and focused");
assertTrue("Activity that was on secondary display must be resumed",
mWmState.hasActivityState(SDK_27_TEST_ACTIVITY, STATE_RESUMED));
assertEquals("There must be only two resumed activities in the package.", 2,
mWmState.getResumedActivitiesCountInPackage(
SDK_27_TEST_ACTIVITY.getPackageName()));
}
@Test
public void testPreQTopProcessResumedDisplayMoved() throws Exception {
final DisplayContent newDisplay = createManagedVirtualDisplaySession()
.setSimulateDisplay(true).createDisplay();
getLaunchActivityBuilder().setUseInstrumentation()
.setTargetActivity(SDK_27_LAUNCHING_ACTIVITY).setNewTask(true)
.setDisplayId(DEFAULT_DISPLAY).execute();
waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
"Activity launched on default display must be resumed and focused");
getLaunchActivityBuilder().setUseInstrumentation()
.setTargetActivity(SDK_27_TEST_ACTIVITY).setNewTask(true)
.setDisplayId(newDisplay.mId).execute();
waitAndAssertTopResumedActivity(SDK_27_TEST_ACTIVITY, newDisplay.mId,
"Activity launched on secondary display must be resumed and focused");
tapOnDisplayCenter(DEFAULT_DISPLAY);
waitAndAssertTopResumedActivity(SDK_27_LAUNCHING_ACTIVITY, DEFAULT_DISPLAY,
"Activity launched on default display must be resumed and focused");
assertEquals("There must be only one resumed activity in the package.", 1,
mWmState.getResumedActivitiesCountInPackage(
SDK_27_LAUNCHING_ACTIVITY.getPackageName()));
}
}