blob: aa85e2c0f9ded56098227f30108c63af467f89fe [file] [log] [blame]
/*
* Copyright (C) 2017 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_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
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_RESUMED;
import static android.server.wm.app.Components.ANIMATION_TEST_ACTIVITY;
import static android.server.wm.app.Components.ASSISTANT_ACTIVITY;
import static android.server.wm.app.Components.ASSISTANT_VOICE_INTERACTION_SERVICE;
import static android.server.wm.app.Components.AssistantActivity.EXTRA_ASSISTANT_DISPLAY_ID;
import static android.server.wm.app.Components.AssistantActivity.EXTRA_ASSISTANT_ENTER_PIP;
import static android.server.wm.app.Components.AssistantActivity.EXTRA_ASSISTANT_FINISH_SELF;
import static android.server.wm.app.Components.AssistantActivity.EXTRA_ASSISTANT_LAUNCH_NEW_TASK;
import static android.server.wm.app.Components.DOCKED_ACTIVITY;
import static android.server.wm.app.Components.LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION;
import static android.server.wm.app.Components.LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK;
import static android.server.wm.app.Components.LaunchAssistantActivityIntoAssistantStack.EXTRA_ASSISTANT_IS_TRANSLUCENT;
import static android.server.wm.app.Components.PIP_ACTIVITY;
import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.app.Components.TRANSLUCENT_ASSISTANT_ACTIVITY;
import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.server.wm.settings.SettingsSession;
import org.junit.Ignore;
import org.junit.Test;
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:AssistantStackTests
*/
@Presubmit
public class AssistantStackTests extends ActivityManagerTestBase {
private int mAssistantDisplayId = DEFAULT_DISPLAY;
private int mDefaultWindowingMode;
public void setUp() throws Exception {
super.setUp();
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK);
waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
WindowManagerState.Task assistantStack =
mWmState.getRootTaskByActivityType(ACTIVITY_TYPE_ASSISTANT);
mAssistantDisplayId = assistantStack.mDisplayId;
mDefaultWindowingMode = getDefaultDisplayWindowingMode();
}
}
@Test
public void testLaunchingAssistantActivityIntoAssistantStack() throws Exception {
// Enable the assistant and launch an assistant activity
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
// Ensure that the activity launched in the fullscreen assistant stack
assertAssistantStackExists();
// In a multi-window environment the assistant might not be fullscreen
assumeTrue(mDefaultWindowingMode == WINDOWING_MODE_FULLSCREEN);
assertTrue("Expected assistant stack to be fullscreen",
mWmState.getRootTaskByActivityType(
ACTIVITY_TYPE_ASSISTANT).isFullscreen());
}
}
@Test
public void testAssistantStackZOrder() throws Exception {
assumeTrue(assistantRunsOnPrimaryDisplay());
assumeTrue(supportsPip());
assumeTrue(supportsSplitScreenMultiWindow());
// Launch a pinned stack task
launchActivity(PIP_ACTIVITY, extraString(EXTRA_ENTER_PIP, "true"));
waitForValidStateWithActivityTypeAndWindowingMode(
PIP_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_PINNED);
mWmState.assertContainsStack("Must contain pinned stack.",
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
// Dock a task
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
// Enable the assistant and launch an assistant activity, ensure it is on top
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.assertFrontStack("Pinned stack should be on top.",
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
mWmState.assertFocusedRootTask("Assistant stack should be focused.",
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
}
}
@Test
public void testAssistantStackLaunchNewTask() throws Exception {
assertAssistantStackCanLaunchAndReturnFromNewTask(mDefaultWindowingMode);
}
@Test
@Ignore("b/77272253#comment10")
public void testAssistantStackLaunchNewTaskWithDockedStack() throws Exception {
assumeTrue(assistantRunsOnPrimaryDisplay());
assumeTrue(supportsSplitScreenMultiWindow());
// Dock a task
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
//assertAssistantStackCanLaunchAndReturnFromNewTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
}
private void assertAssistantStackCanLaunchAndReturnFromNewTask(int expectedWindowingMode)
throws Exception {
// Enable the assistant and launch an assistant activity which will launch a new task
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
launchActivityOnDisplayNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK, mAssistantDisplayId,
extraString(EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY)),
extraString(EXTRA_ASSISTANT_DISPLAY_ID, Integer.toString(mAssistantDisplayId)));
// Ensure that the fullscreen stack is on top and the test activity is now visible
waitForValidStateWithActivityTypeAndWindowingMode(
TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, expectedWindowingMode);
}
if (isAssistantOnTopOfDream()) {
// If the assistant is configured to be always-on-top, then the new task should have
// been started behind it and the assistant stack should still be on top.
mWmState.assertFocusedActivity(
"AssistantActivity should be focused", ASSISTANT_ACTIVITY);
mWmState.assertFrontStackActivityType(
"Assistant stack should be on top.", ACTIVITY_TYPE_ASSISTANT);
mWmState.assertFocusedRootTask("Assistant stack should be focused.",
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
} else {
mWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
mWmState.assertFrontStack("TestActivity stack should be on top.",
expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
mWmState.assertFocusedRootTask("TestActivity stack should be focused.",
expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
}
// Now, tell it to finish itself and ensure that the assistant stack is brought back forward
mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
mWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
mWmState.assertFrontStackActivityType(
"Assistant stack should be on top.", ACTIVITY_TYPE_ASSISTANT);
mWmState.assertFocusedRootTask("Assistant stack should be focused.",
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
}
@Test
public void testAssistantStackFinishToPreviousApp() throws Exception {
// If the Assistant is configured to be always-on-top, then the assistant activity
// started in setUp() will not allow any other activities to start. Therefore we should
// remove it before launching a fullscreen activity.
if (isAssistantOnTopOfDream()) {
removeRootTasksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
}
// Launch an assistant activity on top of an existing fullscreen activity, and ensure that
// the fullscreen activity is still visible and on top after the assistant activity finishes
launchActivityOnDisplay(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN, mAssistantDisplayId);
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
extraString(EXTRA_ASSISTANT_FINISH_SELF, "true"));
mWmState.waitFor((amState) -> !amState.containsActivity(ASSISTANT_ACTIVITY),
getActivityName(ASSISTANT_ACTIVITY) + " finished");
}
waitForValidStateWithActivityTypeAndWindowingMode(
TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, mDefaultWindowingMode);
waitAndAssertTopResumedActivity(TEST_ACTIVITY, mAssistantDisplayId,
"TestActivity should be resumed");
mWmState.assertFocusedActivity("TestActivity should be focused", TEST_ACTIVITY);
mWmState.assertFrontStack("Fullscreen stack should be on top.",
mDefaultWindowingMode, ACTIVITY_TYPE_STANDARD);
mWmState.assertFocusedRootTask("Fullscreen stack should be focused.",
mDefaultWindowingMode, ACTIVITY_TYPE_STANDARD);
}
@Test
public void testDisallowEnterPiPFromAssistantStack() throws Exception {
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
extraString(EXTRA_ASSISTANT_ENTER_PIP, "true"));
}
waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
mWmState.assertDoesNotContainStack("Must not contain pinned stack.",
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
}
@Test
public void testTranslucentAssistantActivityStackVisibility() throws Exception {
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
// Go home, launch the assistant and check to see that home is visible
removeRootTasksInWindowingModes(WINDOWING_MODE_FULLSCREEN);
pressHomeButton();
resumeAppSwitches();
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
extraString(EXTRA_ASSISTANT_IS_TRANSLUCENT, "true"));
waitForValidStateWithActivityType(
TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.waitForHomeActivityVisible();
if (hasHomeScreen()) {
mWmState.assertHomeActivityVisible(true);
}
// Launch a fullscreen app and then launch the assistant and check to see that it is
// also visible
removeRootTasksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
launchActivityOnDisplay(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN, mAssistantDisplayId);
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
extraString(EXTRA_ASSISTANT_IS_TRANSLUCENT, "true"));
waitForValidStateWithActivityType(
TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.assertVisibility(TEST_ACTIVITY, true);
// Go home, launch assistant, launch app into fullscreen with activity present, and go
// back.Ensure home is visible.
removeRootTasksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
pressHomeButton();
resumeAppSwitches();
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
extraString(EXTRA_ASSISTANT_IS_TRANSLUCENT, "true"),
extraString(EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY)));
waitForValidStateWithActivityTypeAndWindowingMode(
TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, mDefaultWindowingMode);
final ComponentName homeActivity = mWmState.getHomeActivityName();
int windowingMode = mWmState.getFocusedRootTaskWindowingMode();
// In a multi-window environment the home activity might not be fully covered
assumeTrue(windowingMode == WINDOWING_MODE_FULLSCREEN);
mWmState.waitAndAssertVisibilityGone(homeActivity);
mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
mWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.waitForHomeActivityVisible();
if (hasHomeScreen()) {
mWmState.assertHomeActivityVisible(true);
}
// Launch a fullscreen and docked app and then launch the assistant and check to see
// that it
// is also visible
if (supportsSplitScreenMultiWindow() && assistantRunsOnPrimaryDisplay()) {
removeRootTasksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
extraString(EXTRA_ASSISTANT_IS_TRANSLUCENT, "true"));
waitForValidStateWithActivityType(
TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.assertVisibility(DOCKED_ACTIVITY, true);
mWmState.assertVisibility(TEST_ACTIVITY, true);
}
}
}
@Test
public void testLaunchIntoSameTask() throws Exception {
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
// Launch the assistant
launchActivityOnDisplayNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION,
mAssistantDisplayId);
waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
mWmState.assertFocusedRootTask("Expected assistant stack focused",
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
final WindowManagerState amState = mWmState;
assertThat(amState.getRootTaskByActivityType(ACTIVITY_TYPE_ASSISTANT).getTasks(),
hasSize(1));
final int taskId = mWmState.getTaskByActivity(ASSISTANT_ACTIVITY)
.mTaskId;
// Launch a new fullscreen activity
// Using Animation Test Activity because it is opaque on all devices.
launchActivityOnDisplay(ANIMATION_TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN, mAssistantDisplayId);
// Wait for animation finished.
mWmState.waitForActivityState(ANIMATION_TEST_ACTIVITY, STATE_RESUMED);
if (isAssistantOnTopOfDream()) {
mWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
} else {
mWmState.waitAndAssertVisibilityGone(ASSISTANT_ACTIVITY);
}
// Launch the assistant again and ensure that it goes into the same task
launchActivityOnDisplayNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION,
mAssistantDisplayId);
waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
mWmState.assertFocusedRootTask("Expected assistant stack focused",
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
assertThat(amState.getRootTaskByActivityType(ACTIVITY_TYPE_ASSISTANT).getTasks(),
hasSize(1));
assertEquals(taskId,
mWmState.getTaskByActivity(ASSISTANT_ACTIVITY).mTaskId);
}
}
@Test
public void testPinnedStackWithAssistant() throws Exception {
assumeTrue(supportsPip());
assumeTrue(supportsSplitScreenMultiWindow());
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
// Launch a fullscreen activity and a PIP activity, then launch the assistant, and
// ensure that the test activity is still visible
launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
launchActivity(PIP_ACTIVITY, extraString(EXTRA_ENTER_PIP, "true"));
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
extraString(EXTRA_ASSISTANT_IS_TRANSLUCENT, String.valueOf(true)));
waitForValidStateWithActivityType(
TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.assertVisibility(TRANSLUCENT_ASSISTANT_ACTIVITY, true);
mWmState.assertVisibility(PIP_ACTIVITY, true);
mWmState.assertVisibility(TEST_ACTIVITY, true);
}
}
private void waitForValidStateWithActivityType(ComponentName activityName, int activityType)
throws Exception {
mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
.setActivityType(activityType)
.build());
}
private void waitForValidStateWithActivityTypeAndWindowingMode(ComponentName activityName,
int activityType, int windowingMode) throws Exception {
mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
.setActivityType(activityType)
.setWindowingMode(windowingMode)
.build());
}
/**
* Asserts that the assistant stack exists.
*/
private void assertAssistantStackExists() throws Exception {
mWmState.assertContainsStack("Must contain assistant stack.",
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
}
// Any 2D Activity in VR mode is run on a special VR virtual display, so check if the Assistant
// is going to run on the same display as other tasks.
protected boolean assistantRunsOnPrimaryDisplay() {
return mAssistantDisplayId == DEFAULT_DISPLAY;
}
/**
* @return Windowing Mode from the default display
*/
private int getDefaultDisplayWindowingMode() {
mWmState.computeState();
return mWmState.getDisplay(DEFAULT_DISPLAY).getWindowingMode();
}
/** Helper class to save, set, and restore
* {@link Settings.Secure#VOICE_INTERACTION_SERVICE} system preference.
*/
private static class AssistantSession extends SettingsSession<String> {
AssistantSession() {
super(Settings.Secure.getUriFor(Settings.Secure.VOICE_INTERACTION_SERVICE),
Settings.Secure::getString, Settings.Secure::putString);
}
void setVoiceInteractionService(ComponentName assistantName) throws Exception {
super.set(getActivityName(assistantName));
}
}
}