| /* |
| * 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_ASSISTANT; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; |
| import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; |
| import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; |
| import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; |
| import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; |
| import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; |
| import static android.server.wm.ActivityLauncher.KEY_ACTION; |
| import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY; |
| import static android.server.wm.ActivityLauncher.KEY_LAUNCH_IMPLICIT; |
| import static android.server.wm.ActivityLauncher.KEY_LAUNCH_PENDING; |
| import static android.server.wm.ActivityLauncher.KEY_NEW_TASK; |
| import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT; |
| import static android.server.wm.CliIntentExtra.extraBool; |
| import static android.server.wm.CliIntentExtra.extraString; |
| import static android.server.wm.ComponentNameUtils.getActivityName; |
| import static android.server.wm.UiDeviceUtils.pressHomeButton; |
| import static android.server.wm.WindowManagerState.STATE_DESTROYED; |
| import static android.server.wm.WindowManagerState.STATE_RESUMED; |
| import static android.server.wm.WindowManagerState.STATE_STOPPED; |
| import static android.server.wm.app.Components.ALT_LAUNCHING_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.NON_RESIZEABLE_ACTIVITY; |
| import static android.server.wm.app.Components.NO_HISTORY_ACTIVITY; |
| import static android.server.wm.app.Components.NO_HISTORY_ACTIVITY2; |
| import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY; |
| import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_ACTIVITY; |
| import static android.server.wm.app.Components.SINGLE_TOP_ACTIVITY; |
| import static android.server.wm.app.Components.TEST_ACTIVITY; |
| import static android.server.wm.app.Components.TOP_ACTIVITY; |
| import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY; |
| import static android.server.wm.second.Components.IMPLICIT_TARGET_SECOND_ACTIVITY; |
| import static android.server.wm.second.Components.IMPLICIT_TARGET_SECOND_TEST_ACTION; |
| import static android.server.wm.second.Components.SECOND_ACTIVITY; |
| import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_ACTION; |
| import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_RECEIVER; |
| import static android.server.wm.third.Components.THIRD_ACTIVITY; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assume.assumeTrue; |
| |
| import android.app.Activity; |
| import android.app.ActivityOptions; |
| import android.app.PendingIntent; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Configuration; |
| import android.hardware.display.DisplayManager; |
| import android.hardware.display.VirtualDisplay; |
| import android.os.Bundle; |
| import android.platform.test.annotations.Presubmit; |
| 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 android.view.SurfaceView; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Build/Install/Run: |
| * atest CtsWindowManagerDeviceTestCases:MultiDisplayActivityLaunchTests |
| * |
| * Tests activity launching behavior on multi-display environment. |
| */ |
| @Presubmit |
| @android.server.wm.annotation.Group3 |
| public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase { |
| |
| @Before |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| assumeTrue(supportsMultiDisplay()); |
| } |
| |
| /** |
| * Tests launching an activity on virtual display. |
| */ |
| @Test |
| public void testLaunchActivityOnSecondaryDisplay() throws Exception { |
| validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_STANDARD); |
| } |
| |
| /** |
| * Tests launching a recent activity on virtual display. |
| */ |
| @Test |
| public void testLaunchRecentActivityOnSecondaryDisplay() throws Exception { |
| validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_RECENTS); |
| } |
| |
| /** |
| * Tests launching an assistant activity on virtual display. |
| */ |
| @Test |
| public void testLaunchAssistantActivityOnSecondaryDisplay() { |
| validateActivityLaunchOnNewDisplay(ACTIVITY_TYPE_ASSISTANT); |
| } |
| |
| private void validateActivityLaunchOnNewDisplay(int activityType) { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| // Launch activity on new secondary display. |
| separateTestJournal(); |
| getLaunchActivityBuilder().setUseInstrumentation().setWithShellPermission(true) |
| .setTargetActivity(TEST_ACTIVITY).setNewTask(true) |
| .setMultipleTask(true).setActivityType(activityType) |
| .setDisplayId(newDisplay.mId).execute(); |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be focused and on top"); |
| |
| // Check that activity config corresponds to display config. |
| final SizeInfo reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY); |
| assertEquals("Activity launched on secondary display must have proper configuration", |
| CUSTOM_DENSITY_DPI, reportedSizes.densityDpi); |
| |
| assertEquals("Top activity must have correct activity type", activityType, |
| mWmState.getFrontRootTaskActivityType(newDisplay.mId)); |
| } |
| |
| /** |
| * Tests launching an activity on primary display explicitly. |
| */ |
| @Test |
| public void testLaunchActivityOnPrimaryDisplay() throws Exception { |
| // Launch activity on primary display explicitly. |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY); |
| |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity launched on primary display must be focused and on top"); |
| |
| // Launch another activity on primary display using the first one |
| getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).setNewTask(true) |
| .setMultipleTask(true).setDisplayId(DEFAULT_DISPLAY).execute(); |
| mWmState.computeState(TEST_ACTIVITY); |
| |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity launched on primary display must be focused"); |
| } |
| |
| /** |
| * Tests launching an existing activity from an activity that resides on secondary display. An |
| * existing activity on a different display should be moved to the display of the launching |
| * activity. |
| */ |
| @Test |
| public void testLaunchActivityFromSecondaryDisplay() { |
| getLaunchActivityBuilder().setUseInstrumentation() |
| .setTargetActivity(TEST_ACTIVITY).setNewTask(true) |
| .setDisplayId(DEFAULT_DISPLAY).execute(); |
| |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| final int newDisplayId = newDisplay.mId; |
| |
| getLaunchActivityBuilder().setUseInstrumentation() |
| .setTargetActivity(BROADCAST_RECEIVER_ACTIVITY).setNewTask(true) |
| .setDisplayId(newDisplayId).execute(); |
| waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId, |
| "Activity should be resumed on secondary display"); |
| |
| mBroadcastActionTrigger.launchActivityNewTask(getActivityName(TEST_ACTIVITY)); |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplayId, |
| "Activity should be resumed on secondary display"); |
| |
| getLaunchActivityBuilder().setUseInstrumentation() |
| .setTargetActivity(TEST_ACTIVITY).setNewTask(true) |
| .setDisplayId(DEFAULT_DISPLAY).execute(); |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity should be the top resumed on default display"); |
| } |
| |
| /** |
| * Tests that an activity can be launched on a secondary display while the primary |
| * display is off. |
| */ |
| @Test |
| public void testLaunchExternalDisplayActivityWhilePrimaryOff() { |
| if (isOperatorTierDevice()) { |
| // This test is not applicable for the device who uses launch_after_boot configuration |
| return; |
| } |
| |
| // 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 PrimaryDisplayStateSession displayStateSession = |
| mObjectTracker.manage(new PrimaryDisplayStateSession()); |
| final ExternalDisplaySession externalDisplaySession = createManagedExternalDisplaySession(); |
| displayStateSession.turnScreenOff(); |
| |
| // 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 = externalDisplaySession |
| .setCanShowWithInsecureKeyguard(true).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"); |
| mWmState.assertFocusedAppOnDisplay("App on default display must still be focused", |
| RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY); |
| } |
| |
| /** |
| * Tests launching a non-resizeable activity on virtual display. It should land on the |
| * virtual display with correct configuration. |
| */ |
| @Test |
| public void testLaunchNonResizeableActivityOnSecondaryDisplay() { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY, newDisplay.mId); |
| |
| waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId, |
| "Activity requested to launch on secondary display must be focused"); |
| |
| final Configuration taskConfig = mWmState |
| .getTaskByActivity(NON_RESIZEABLE_ACTIVITY).mFullConfiguration; |
| final Configuration displayConfig = mWmState |
| .getDisplay(newDisplay.mId).mFullConfiguration; |
| |
| // Check that activity config corresponds to display config. |
| assertEquals("Activity launched on secondary display must have proper configuration", |
| taskConfig.densityDpi, displayConfig.densityDpi); |
| |
| assertEquals("Activity launched on secondary display must have proper configuration", |
| taskConfig.windowConfiguration.getBounds(), |
| displayConfig.windowConfiguration.getBounds()); |
| } |
| |
| /** |
| * Tests successfully moving a non-resizeable activity to a virtual display. |
| */ |
| @Test |
| public void testMoveNonResizeableActivityToSecondaryDisplay() { |
| final VirtualDisplayLauncher virtualLauncher = |
| mObjectTracker.manage(new VirtualDisplayLauncher()); |
| // Create new virtual display. |
| final DisplayContent newDisplay = virtualLauncher |
| .setSimulateDisplay(true).createDisplay(); |
| // Launch a non-resizeable activity on a primary display. |
| final ActivitySession nonResizeableSession = virtualLauncher.launchActivity( |
| builder -> builder.setTargetActivity(NON_RESIZEABLE_ACTIVITY).setNewTask(true)); |
| |
| // Launch a resizeable activity on new secondary display to create a new task there. |
| virtualLauncher.launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay); |
| final int externalFrontRootTaskId = mWmState |
| .getFrontRootTaskId(newDisplay.mId); |
| |
| // Clear lifecycle callback history before moving the activity so the later verification |
| // can get the callbacks which are related to the reparenting. |
| nonResizeableSession.takeCallbackHistory(); |
| |
| // Try to move the non-resizeable activity to the top of the root task on secondary display. |
| moveActivityToRootTaskOrOnTop(NON_RESIZEABLE_ACTIVITY, externalFrontRootTaskId); |
| // Wait for a while to check that it will move. |
| assertTrue("Non-resizeable activity should be moved", |
| mWmState.waitForWithAmState( |
| state -> newDisplay.mId == state |
| .getDisplayByActivity(NON_RESIZEABLE_ACTIVITY), |
| "seeing if activity won't be moved")); |
| |
| waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId, |
| "The moved non-resizeable activity must be focused"); |
| assertActivityLifecycle(nonResizeableSession, true /* relaunched */); |
| } |
| |
| /** |
| * Tests launching a non-resizeable activity on virtual display from activity there. It should |
| * land on the secondary display based on the resizeability of the root activity of the task. |
| */ |
| @Test |
| public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() { |
| // Create new simulated display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId); |
| waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be focused"); |
| |
| // Launch non-resizeable activity from secondary display. |
| mBroadcastActionTrigger.launchActivityNewTask(getActivityName(NON_RESIZEABLE_ACTIVITY)); |
| waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId, |
| "Launched activity must be on the secondary display and resumed"); |
| } |
| |
| /** |
| * Tests launching a non-resizeable activity on virtual display in a new task from activity |
| * there. It must land on the display as its caller. |
| */ |
| @Test |
| public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be focused"); |
| |
| // Launch non-resizeable activity from secondary display in a new task. |
| getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY) |
| .setNewTask(true).setMultipleTask(true).execute(); |
| |
| mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED); |
| |
| // Check that non-resizeable activity is on the same display. |
| final int newFrontRootTaskId = mWmState.getFocusedTaskId(); |
| final Task newFrontRootTask = mWmState.getRootTask(newFrontRootTaskId); |
| assertEquals("Launched activity must be on the same display", newDisplay.mId, |
| newFrontRootTask.mDisplayId); |
| assertEquals("Launched activity must be resumed", |
| getActivityName(NON_RESIZEABLE_ACTIVITY), |
| newFrontRootTask.mResumedActivity); |
| mWmState.assertFocusedRootTask( |
| "Top task must be the one with just launched activity", |
| newFrontRootTaskId); |
| mWmState.assertResumedActivity("NON_RESIZEABLE_ACTIVITY not resumed", |
| NON_RESIZEABLE_ACTIVITY); |
| } |
| |
| /** |
| * Tests launching an activity on virtual display and then launching another activity |
| * via shell command and without specifying the display id - the second activity |
| * must appear on the same display due to process affinity. |
| */ |
| @Test |
| public void testConsequentLaunchActivity() { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); |
| |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be on top"); |
| |
| // Launch second activity without specifying display. |
| launchActivity(LAUNCHING_ACTIVITY); |
| |
| // Check that activity is launched in focused task on the new display. |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Launched activity must be focused"); |
| mWmState.assertResumedActivity("LAUNCHING_ACTIVITY must be resumed", LAUNCHING_ACTIVITY); |
| } |
| |
| /** |
| * Tests launching an activity on a virtual display and then launching another activity in |
| * a new process via shell command and without specifying the display id - the second activity |
| * must appear on the primary display. |
| */ |
| @Test |
| public void testConsequentLaunchActivityInNewProcess() { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(TEST_ACTIVITY, newDisplay.mId); |
| |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be on top"); |
| |
| // Launch second activity without specifying display. |
| launchActivity(SECOND_ACTIVITY); |
| |
| // Check that activity is launched in focused task on primary display. |
| waitAndAssertTopResumedActivity(SECOND_ACTIVITY, DEFAULT_DISPLAY, |
| "Launched activity must be focused"); |
| assertBothDisplaysHaveResumedActivities(pair(newDisplay.mId, TEST_ACTIVITY), |
| pair(DEFAULT_DISPLAY, SECOND_ACTIVITY)); |
| } |
| |
| /** |
| * Tests launching an activity on simulated display and then launching another activity from the |
| * first one - it must appear on the secondary display, because it was launched from there. |
| */ |
| @Test |
| public void testConsequentLaunchActivityFromSecondaryDisplay() { |
| // Create new simulated display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); |
| |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be on top"); |
| |
| // Launch second activity from app on secondary display without specifying display id. |
| getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute(); |
| |
| // Check that activity is launched in focused task on external display. |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, |
| "Launched activity must be on top"); |
| } |
| |
| /** |
| * Tests launching an activity on virtual display and then launching another activity from the |
| * first one - it must appear on the secondary display, because it was launched from there. |
| */ |
| @Test |
| public void testConsequentLaunchActivityFromVirtualDisplay() { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); |
| |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be on top"); |
| |
| // Launch second activity from app on secondary display without specifying display id. |
| getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute(); |
| mWmState.computeState(TEST_ACTIVITY); |
| |
| // Check that activity is launched in focused task on external display. |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, |
| "Launched activity must be on top"); |
| } |
| |
| /** |
| * Tests launching an activity on virtual display and then launching another activity from the |
| * first one with specifying the target display - it must appear on the secondary display. |
| */ |
| @Test |
| public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| // Launch activity on new secondary display. |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); |
| |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be on top"); |
| |
| // Launch second activity from app on secondary display specifying same display id. |
| getLaunchActivityBuilder() |
| .setTargetActivity(SECOND_ACTIVITY) |
| .setDisplayId(newDisplay.mId) |
| .execute(); |
| |
| // Check that activity is launched in focused task on external display. |
| waitAndAssertTopResumedActivity(SECOND_ACTIVITY, newDisplay.mId, |
| "Launched activity must be on top"); |
| |
| // Launch other activity with different uid and check if it has launched successfully. |
| getLaunchActivityBuilder() |
| .setUseBroadcastReceiver(SECOND_LAUNCH_BROADCAST_RECEIVER, |
| SECOND_LAUNCH_BROADCAST_ACTION) |
| .setDisplayId(newDisplay.mId) |
| .setTargetActivity(THIRD_ACTIVITY) |
| .execute(); |
| |
| // Check that activity is launched in focused task on external display. |
| waitAndAssertTopResumedActivity(THIRD_ACTIVITY, newDisplay.mId, |
| "Launched activity must be on top"); |
| } |
| |
| /** |
| * Tests that when an {@link Activity} is running on one display but is started from a second |
| * display then the {@link Activity} is moved to the second display. |
| */ |
| @Test |
| public void testLaunchExistingActivityReparentDisplay() { |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| launchActivityOnDisplay(SECOND_ACTIVITY, DEFAULT_DISPLAY); |
| |
| waitAndAssertTopResumedActivity(SECOND_ACTIVITY, DEFAULT_DISPLAY, |
| "Must launch activity on same display."); |
| |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId, |
| extraBool(KEY_USE_APPLICATION_CONTEXT, true), extraBool(KEY_NEW_TASK, true), |
| extraBool(KEY_LAUNCH_ACTIVITY, true), extraBool(KEY_LAUNCH_IMPLICIT, true), |
| extraString(KEY_ACTION, IMPLICIT_TARGET_SECOND_TEST_ACTION)); |
| |
| waitAndAssertTopResumedActivity(IMPLICIT_TARGET_SECOND_ACTIVITY, newDisplay.mId, |
| "Must launch activity on same display."); |
| } |
| |
| /** |
| * Tests launching an activity to secondary display from activity on primary display. |
| */ |
| @Test |
| public void testLaunchActivityFromAppToSecondaryDisplay() { |
| // Start launching activity. |
| launchActivity(LAUNCHING_ACTIVITY); |
| |
| // Create new simulated display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| // Launch activity on secondary display from the app on primary display. |
| getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY) |
| .setDisplayId(newDisplay.mId).execute(); |
| |
| // Check that activity is launched on external display. |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be focused"); |
| assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, LAUNCHING_ACTIVITY), |
| pair(newDisplay.mId, TEST_ACTIVITY)); |
| } |
| |
| /** Tests that launching app from pending activity queue on external display is allowed. */ |
| @Test |
| public void testLaunchPendingActivityOnSecondaryDisplay() { |
| pressHomeButton(); |
| // Create new simulated display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| final Bundle bundle = ActivityOptions.makeBasic(). |
| setLaunchDisplayId(newDisplay.mId).toBundle(); |
| final Intent intent = new Intent(Intent.ACTION_VIEW) |
| .setComponent(SECOND_ACTIVITY) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) |
| .putExtra(KEY_LAUNCH_ACTIVITY, true) |
| .putExtra(KEY_NEW_TASK, true); |
| mContext.startActivity(intent, bundle); |
| |
| // If home key was pressed, stopAppSwitches will be called. |
| // Since this test case is not start activity from shell, it won't grant |
| // STOP_APP_SWITCHES and this activity should be put into pending activity queue |
| // and this activity should been launched after |
| // ActivityTaskManagerService.APP_SWITCH_DELAY_TIME |
| mWmState.waitForPendingActivityContain(SECOND_ACTIVITY); |
| // If the activity is not pending, skip this test. |
| mWmState.assumePendingActivityContain(SECOND_ACTIVITY); |
| // In order to speed up test case without waiting for APP_SWITCH_DELAY_TIME, we launch |
| // another activity with LaunchActivityBuilder, in this way the activity can be start |
| // directly and also trigger pending activity to be launched. |
| getLaunchActivityBuilder() |
| .setTargetActivity(THIRD_ACTIVITY) |
| .execute(); |
| mWmState.waitForValidState(SECOND_ACTIVITY); |
| waitAndAssertTopResumedActivity(THIRD_ACTIVITY, DEFAULT_DISPLAY, |
| "Top activity must be the newly launched one"); |
| mWmState.assertVisibility(SECOND_ACTIVITY, true); |
| assertEquals("Activity launched by app on secondary display must be on that display", |
| newDisplay.mId, mWmState.getDisplayByActivity(SECOND_ACTIVITY)); |
| } |
| |
| /** |
| * Tests that when an activity is launched with displayId specified and there is an existing |
| * matching task on some other display - that task will moved to the target display. |
| */ |
| @Test |
| public void testMoveToDisplayOnLaunch() { |
| // Launch activity with unique affinity, so it will the only one in its task. |
| launchActivity(LAUNCHING_ACTIVITY); |
| |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay(); |
| mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); |
| // Launch something to that display so that a new task is created. We need this to be |
| // able to compare task numbers in tasks later. |
| launchActivityOnDisplay(RESIZEABLE_ACTIVITY, newDisplay.mId); |
| mWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */); |
| |
| final int rootTaskNum = mWmState.getDisplay(DEFAULT_DISPLAY).mRootTasks.size(); |
| final int rootTaskNumOnSecondary = mWmState |
| .getDisplay(newDisplay.mId).mRootTasks.size(); |
| |
| // Launch activity on new secondary display. |
| // Using custom command here, because normally we add flags |
| // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} |
| // when launching on some specific display. We don't do it here as we want an existing |
| // task to be used. |
| final String launchCommand = "am start -n " + getActivityName(LAUNCHING_ACTIVITY) |
| + " --display " + newDisplay.mId; |
| executeShellCommand(launchCommand); |
| |
| // Check that activity is brought to front. |
| waitAndAssertActivityStateOnDisplay(LAUNCHING_ACTIVITY, STATE_RESUMED, newDisplay.mId, |
| "Existing task must be brought to front"); |
| |
| // Check that task has moved from primary display to secondary. |
| // Since it is 1-to-1 relationship between task and root task for standard type & |
| // fullscreen activity, we check the number of root tasks here |
| final int rootTaskNumFinal = mWmState.getDisplay(DEFAULT_DISPLAY) |
| .mRootTasks.size(); |
| assertEquals("Root task number in default root task must be decremented.", rootTaskNum - 1, |
| rootTaskNumFinal); |
| final int rootTaskNumFinalOnSecondary = mWmState |
| .getDisplay(newDisplay.mId).mRootTasks.size(); |
| assertEquals("Root task number on external display must be incremented.", |
| rootTaskNumOnSecondary + 1, rootTaskNumFinalOnSecondary); |
| } |
| |
| /** |
| * Tests that when an activity is launched with displayId specified and there is an existing |
| * matching task on some other display - that task will moved to the target display. |
| */ |
| @Test |
| public void testMoveToEmptyDisplayOnLaunch() { |
| // Launch activity with unique affinity, so it will the only one in its task. And choose |
| // resizeable activity to prevent the test activity be relaunched when launch it to another |
| // display, which may affect on this test case. |
| launchActivity(RESIZEABLE_ACTIVITY); |
| |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay(); |
| mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); |
| |
| final int rootTaskNum = mWmState.getDisplay(DEFAULT_DISPLAY).mRootTasks.size(); |
| |
| // Launch activity on new secondary display. |
| // Using custom command here, because normally we add flags |
| // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK} |
| // when launching on some specific display. We don't do it here as we want an existing |
| // task to be used. |
| final String launchCommand = "am start -n " + getActivityName(RESIZEABLE_ACTIVITY) |
| + " --display " + newDisplay.mId; |
| executeShellCommand(launchCommand); |
| |
| // Check that activity is brought to front. |
| waitAndAssertActivityStateOnDisplay(RESIZEABLE_ACTIVITY, STATE_RESUMED, newDisplay.mId, |
| "Existing task must be brought to front"); |
| |
| // Check that task has moved from primary display to secondary. |
| final int rootTaskNumFinal = mWmState.getDisplay(DEFAULT_DISPLAY) |
| .mRootTasks.size(); |
| assertEquals("Root task number in default root task must be decremented.", rootTaskNum - 1, |
| rootTaskNumFinal); |
| } |
| |
| /** |
| * Tests that if a second task has the same affinity as a running task but in a separate |
| * process the second task launches in the same display. |
| */ |
| @Test |
| public void testLaunchSameAffinityLaunchesSameDisplay() { |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); |
| mWmState.computeState(LAUNCHING_ACTIVITY); |
| |
| // Check that activity is on the secondary display. |
| final int frontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId); |
| final Task firstFrontRootTask = mWmState.getRootTask(frontRootTaskId); |
| assertEquals("Activity launched on secondary display must be resumed", |
| getActivityName(LAUNCHING_ACTIVITY), |
| firstFrontRootTask.mResumedActivity); |
| mWmState.assertFocusedRootTask("Top root task must be on secondary display", |
| frontRootTaskId); |
| |
| executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY)); |
| mWmState.waitForValidState(ALT_LAUNCHING_ACTIVITY); |
| |
| // Check that second activity gets launched on the default display despite |
| // the affinity match on the secondary display. |
| final int displayFrontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId); |
| final Task displayFrontRootTask = mWmState.getRootTask(displayFrontRootTaskId); |
| waitAndAssertTopResumedActivity(ALT_LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Activity launched on same display must be resumed"); |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Existing task must be brought to front"); |
| |
| // Check that the third intent is redirected to the first task due to the root |
| // component match on the secondary display. |
| final Task secondFrontRootTask = mWmState.getRootTask(frontRootTaskId); |
| final int secondFrontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId); |
| assertEquals("Activity launched on secondary display must be resumed", |
| getActivityName(ALT_LAUNCHING_ACTIVITY), |
| displayFrontRootTask.mResumedActivity); |
| mWmState.assertFocusedRootTask("Top root task must be on primary display", |
| secondFrontRootTaskId); |
| assertEquals("Second display must contain 2 root tasks", 2, |
| mWmState.getDisplay(newDisplay.mId).getRootTasks().size()); |
| assertEquals("Top task must contain 2 activities", 2, |
| secondFrontRootTask.getActivities().size()); |
| } |
| |
| /** |
| * Tests that an activity is launched on the preferred display where the caller resided when |
| * both displays have matching tasks. |
| */ |
| @Test |
| public void testTaskMatchOrderAcrossDisplays() { |
| getLaunchActivityBuilder().setUseInstrumentation() |
| .setTargetActivity(TEST_ACTIVITY).setNewTask(true) |
| .setDisplayId(DEFAULT_DISPLAY).execute(); |
| final int rootTaskId = mWmState.getFrontRootTaskId(DEFAULT_DISPLAY); |
| |
| getLaunchActivityBuilder().setUseInstrumentation() |
| .setTargetActivity(BROADCAST_RECEIVER_ACTIVITY).setNewTask(true) |
| .setDisplayId(DEFAULT_DISPLAY).execute(); |
| |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession().createDisplay(); |
| getLaunchActivityBuilder().setUseInstrumentation().setWithShellPermission(true) |
| .setTargetActivity(TEST_ACTIVITY).setNewTask(true) |
| .setDisplayId(newDisplay.mId).execute(); |
| assertNotEquals("Top focus root task should not be on default display", |
| rootTaskId, mWmState.getFocusedTaskId()); |
| |
| mBroadcastActionTrigger.launchActivityNewTask(getActivityName(TEST_ACTIVITY)); |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity must be launched on default display"); |
| mWmState.assertFocusedRootTask("Top focus root task must be on the default display", |
| rootTaskId); |
| } |
| |
| /** |
| * Tests that the task affinity search respects the launch display id. |
| */ |
| @Test |
| public void testLaunchDisplayAffinityMatch() { |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true).createDisplay(); |
| |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId); |
| |
| // Check that activity is on the secondary display. |
| final int frontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId); |
| final Task firstFrontRootTask = mWmState.getRootTask(frontRootTaskId); |
| assertEquals("Activity launched on secondary display must be resumed", |
| getActivityName(LAUNCHING_ACTIVITY), firstFrontRootTask.mResumedActivity); |
| mWmState.assertFocusedRootTask("Focus must be on secondary display", frontRootTaskId); |
| |
| // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay |
| executeShellCommand("am start -n " + getActivityName(ALT_LAUNCHING_ACTIVITY) |
| + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK |
| + " --display " + newDisplay.mId); |
| mWmState.computeState(ALT_LAUNCHING_ACTIVITY); |
| |
| // Check that second activity gets launched into the affinity matching |
| // task on the secondary display |
| final int secondFrontRootTaskId = mWmState.getFrontRootTaskId(newDisplay.mId); |
| final Task secondFrontRootTask = |
| mWmState.getRootTask(secondFrontRootTaskId); |
| assertEquals("Activity launched on secondary display must be resumed", |
| getActivityName(ALT_LAUNCHING_ACTIVITY), |
| secondFrontRootTask.mResumedActivity); |
| mWmState.assertFocusedRootTask("Top root task must be on secondary display", |
| secondFrontRootTaskId); |
| assertEquals("Second display must only contain 1 task", |
| 1, mWmState.getDisplay(newDisplay.mId).getRootTasks().size()); |
| assertEquals("Top root task must contain 2 activities", |
| 2, secondFrontRootTask.getActivities().size()); |
| } |
| |
| /** |
| * Tests that a new activity launched by an activity will end up on the same display |
| * even if the root task is not on the top for the display. |
| */ |
| @Test |
| public void testNewTaskSameDisplay() { |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId); |
| |
| // Check that the first activity is launched onto the secondary display |
| waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be resumed"); |
| |
| executeShellCommand("am start -n " + getActivityName(TEST_ACTIVITY)); |
| |
| // Check that the second activity is launched on the same display |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, newDisplay.mId, |
| "Activity launched on default display must be resumed"); |
| |
| mBroadcastActionTrigger.launchActivityNewTask(getActivityName(LAUNCHING_ACTIVITY)); |
| |
| // Check that the third activity ends up in a new task in the same display where the |
| // first activity lands |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Activity must be launched on secondary display"); |
| assertEquals("Secondary display must contain 2 root tasks", 2, |
| mWmState.getDisplay(newDisplay.mId).mRootTasks.size()); |
| } |
| |
| /** |
| * Tests that a new task launched by an activity will end up on the same display |
| * even if the focused task is not on that activity's display. |
| */ |
| @Test |
| public void testNewTaskDefaultDisplay() { |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId); |
| |
| // Check that the first activity is launched onto the secondary display |
| waitAndAssertTopResumedActivity(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be resumed"); |
| |
| launchActivityOnDisplay(SECOND_ACTIVITY, DEFAULT_DISPLAY); |
| |
| // Check that the second activity is launched on the default display because the affinity |
| // is different |
| waitAndAssertTopResumedActivity(SECOND_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity launched on default display must be resumed"); |
| assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, SECOND_ACTIVITY), |
| pair(newDisplay.mId, BROADCAST_RECEIVER_ACTIVITY)); |
| |
| mBroadcastActionTrigger.launchActivityNewTask(getActivityName(LAUNCHING_ACTIVITY)); |
| |
| // Check that the third activity ends up in a new task in the same display where the |
| // first activity lands |
| waitAndAssertTopResumedActivity(LAUNCHING_ACTIVITY, newDisplay.mId, |
| "Activity must be launched on secondary display"); |
| assertEquals("Secondary display must contain 2 root tasks", 2, |
| mWmState.getDisplay(newDisplay.mId).mRootTasks.size()); |
| assertBothDisplaysHaveResumedActivities(pair(DEFAULT_DISPLAY, SECOND_ACTIVITY), |
| pair(newDisplay.mId, LAUNCHING_ACTIVITY)); |
| } |
| |
| /** |
| * Test that launching an activity implicitly will end up on the same display |
| */ |
| @Test |
| public void testLaunchingFromApplicationContext() { |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId, |
| extraBool(KEY_LAUNCH_ACTIVITY, true), extraBool(KEY_LAUNCH_IMPLICIT, true), |
| extraBool(KEY_NEW_TASK, true), extraBool(KEY_USE_APPLICATION_CONTEXT, true), |
| extraString(KEY_ACTION, IMPLICIT_TARGET_SECOND_TEST_ACTION)); |
| waitAndAssertTopResumedActivity(IMPLICIT_TARGET_SECOND_ACTIVITY, newDisplay.mId, |
| "Implicitly launched activity must launch on the same display"); |
| } |
| |
| /** |
| * Test that launching an activity from pending intent will end up on the same display |
| */ |
| @Test |
| public void testLaunchingFromPendingIntent() { |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId, |
| extraBool(KEY_LAUNCH_ACTIVITY, true), |
| extraBool(KEY_LAUNCH_IMPLICIT, true), |
| extraBool(KEY_NEW_TASK, true), |
| extraBool(KEY_USE_APPLICATION_CONTEXT, true), |
| extraBool(KEY_LAUNCH_PENDING, true), |
| extraString(KEY_ACTION, IMPLICIT_TARGET_SECOND_TEST_ACTION)); |
| |
| waitAndAssertTopResumedActivity(IMPLICIT_TARGET_SECOND_ACTIVITY, newDisplay.mId, |
| "Activity launched from pending intent must launch on the same display"); |
| } |
| |
| /** |
| * Tests than an immediate launch after new display creation is handled correctly. |
| */ |
| @Test |
| public void testImmediateLaunchOnNewDisplay() { |
| // Create new virtual display and immediately launch an activity on it. |
| SurfaceView surfaceView = new SurfaceView(mContext); |
| final VirtualDisplay virtualDisplay = mDm.createVirtualDisplay( |
| "testImmediateLaunchOnNewDisplay", /*width=*/ 400, /*height=*/ 400, |
| /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(), |
| DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); |
| try { |
| int displayId = virtualDisplay.getDisplay().getDisplayId(); |
| ComponentName componentName = new ComponentName(mContext, |
| ImmediateLaunchTestActivity.class); |
| getLaunchActivityBuilder().setTargetActivity(componentName).setDisplayId( |
| displayId).setUseInstrumentation().execute(); |
| |
| // Check that activity is launched and placed correctly. |
| waitAndAssertActivityStateOnDisplay(componentName, STATE_RESUMED, displayId, |
| "Test activity must be on top"); |
| final int frontRootTaskId = mWmState.getFrontRootTaskId(displayId); |
| final Task firstFrontRootTask = mWmState.getRootTask(frontRootTaskId); |
| assertEquals("Activity launched on secondary display must be resumed", |
| getActivityName(componentName), firstFrontRootTask.mResumedActivity); |
| } finally { |
| virtualDisplay.release(); |
| } |
| |
| } |
| |
| @Test |
| public void testLaunchPendingIntentActivity() throws Exception { |
| final DisplayContent displayContent = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| // Activity should be launched on primary display by default. |
| getPendingIntentActivity(TEST_ACTIVITY).send(); |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity launched on primary display and on top"); |
| |
| final int resultCode = 1; |
| // Activity should be launched on target display according to the caller context. |
| final Context displayContext = |
| mContext.createDisplayContext(mDm.getDisplay(displayContent.mId)); |
| getPendingIntentActivity(TOP_ACTIVITY).send(displayContext, resultCode, null /* intent */); |
| waitAndAssertTopResumedActivity(TOP_ACTIVITY, displayContent.mId, |
| "Activity launched on secondary display and on top"); |
| |
| // Activity should be brought to front on the same display if it already existed. |
| getPendingIntentActivity(TEST_ACTIVITY).send(displayContext, resultCode, null /* intent */); |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity launched on primary display and on top"); |
| |
| // Activity should be moved to target display. |
| final ActivityOptions options = ActivityOptions.makeBasic(); |
| options.setLaunchDisplayId(displayContent.mId); |
| getPendingIntentActivity(TEST_ACTIVITY).send(mContext, resultCode, null /* intent */, |
| null /* onFinished */, null /* handler */, null /* requiredPermission */, |
| options.toBundle()); |
| waitAndAssertTopResumedActivity(TEST_ACTIVITY, displayContent.mId, |
| "Activity launched on secondary display and on top"); |
| } |
| |
| @Test |
| public void testLaunchActivityClearTask() { |
| assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TASK, LAUNCHING_ACTIVITY); |
| } |
| |
| @Test |
| public void testLaunchActivityClearTop() { |
| assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TOP, LAUNCHING_ACTIVITY); |
| } |
| |
| @Test |
| public void testLaunchActivitySingleTop() { |
| assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_SINGLE_TOP, TEST_ACTIVITY); |
| } |
| |
| @Test |
| public void testLaunchActivitySingleTopOnNewDisplay() { |
| launchActivity(SINGLE_TOP_ACTIVITY); |
| waitAndAssertTopResumedActivity(SINGLE_TOP_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity launched on primary display and on top"); |
| final int taskId = mWmState.getTaskByActivity(SINGLE_TOP_ACTIVITY).getTaskId(); |
| |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| // Launch activity on new secondary display. |
| getLaunchActivityBuilder() |
| .setUseInstrumentation() |
| .setTargetActivity(SINGLE_TOP_ACTIVITY) |
| .allowMultipleInstances(false) |
| .setDisplayId(newDisplay.mId).execute(); |
| |
| waitAndAssertTopResumedActivity(SINGLE_TOP_ACTIVITY, newDisplay.mId, |
| "Activity launched on secondary display must be on top"); |
| |
| final int taskId2 = mWmState.getTaskByActivity(SINGLE_TOP_ACTIVITY).getTaskId(); |
| assertEquals("Activity must be in the same task.", taskId, taskId2); |
| assertEquals("Activity is the only member of its task", 1, |
| mWmState.getActivityCountInTask(taskId2, null)); |
| } |
| |
| /** |
| * This test case tests the behavior that a fullscreen activity was started on top of the |
| * no-history activity from default display while sleeping. The no-history activity from |
| * the external display should not be finished. |
| */ |
| @Test |
| public void testLaunchNoHistoryActivityOnNewDisplay() { |
| launchActivity(NO_HISTORY_ACTIVITY); |
| waitAndAssertTopResumedActivity(NO_HISTORY_ACTIVITY, DEFAULT_DISPLAY, |
| "Activity launched on primary display and on top"); |
| |
| final int taskId = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY).getTaskId(); |
| |
| final PrimaryDisplayStateSession displayStateSession = |
| mObjectTracker.manage(new PrimaryDisplayStateSession()); |
| |
| // Create new virtual display. |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| launchActivityOnDisplay(NO_HISTORY_ACTIVITY2, newDisplay.mId); |
| |
| // Check that the activity is resumed on the external display |
| waitAndAssertActivityStateOnDisplay(NO_HISTORY_ACTIVITY2, STATE_RESUMED, newDisplay.mId, |
| "Activity launched on external display must be resumed"); |
| final int taskId2 = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY2).getTaskId(); |
| |
| displayStateSession.turnScreenOff(); |
| launchActivityOnDisplay(SHOW_WHEN_LOCKED_ACTIVITY, DEFAULT_DISPLAY); |
| |
| assertNotEquals("Activity must not be in the same task.", taskId, taskId2); |
| assertEquals("No-history activity is the member of its task", 1, |
| mWmState.getActivityCountInTask(taskId2, NO_HISTORY_ACTIVITY2)); |
| assertFalse("No-history activity should not be finished.", |
| mWmState.hasActivityState(NO_HISTORY_ACTIVITY2, STATE_DESTROYED)); |
| } |
| |
| private void assertBroughtExistingTaskToAnotherDisplay(int flags, ComponentName topActivity) { |
| // Start TEST_ACTIVITY on top of LAUNCHING_ACTIVITY within the same task |
| getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute(); |
| |
| final DisplayContent newDisplay = createManagedVirtualDisplaySession() |
| .setSimulateDisplay(true) |
| .createDisplay(); |
| |
| // Start LAUNCHING_ACTIVITY on secondary display with target flags, verify the task |
| // be reparented to secondary display |
| getLaunchActivityBuilder() |
| .setUseInstrumentation() |
| .setTargetActivity(LAUNCHING_ACTIVITY) |
| .setIntentFlags(flags) |
| .allowMultipleInstances(false) |
| .setDisplayId(newDisplay.mId).execute(); |
| waitAndAssertTopResumedActivity(topActivity, newDisplay.mId, |
| "Activity launched on secondary display and on top"); |
| } |
| |
| private PendingIntent getPendingIntentActivity(ComponentName activity) { |
| final Intent intent = new Intent(); |
| intent.setClassName(activity.getPackageName(), activity.getClassName()); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| return PendingIntent.getActivity(mContext, 1 /* requestCode */, intent, |
| PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); |
| } |
| |
| public static class ImmediateLaunchTestActivity extends Activity {} |
| } |