/*
 * Copyright (C) 2015 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.ActivityManager.LOCK_TASK_MODE_NONE;
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.TestTaskOrganizer.INVALID_TASK_ID;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.WindowManagerState.STATE_STOPPED;
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_RELAUNCH_ACTIVITY;
import static android.server.wm.app.Components.SINGLE_INSTANCE_ACTIVITY;
import static android.server.wm.app.Components.SINGLE_TASK_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY_WITH_SAME_AFFINITY;
import static android.server.wm.app.Components.TRANSLUCENT_TEST_ACTIVITY;
import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;

import android.content.ComponentName;
import android.content.res.Resources;
import android.platform.test.annotations.Presubmit;
import android.server.wm.CommandSession.ActivityCallback;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import org.junit.Before;
import org.junit.Test;

/**
 * Build/Install/Run:
 *     atest CtsWindowManagerDeviceTestCases:MultiWindowTests
 */
@Presubmit
@android.server.wm.annotation.Group2
public class MultiWindowTests extends ActivityManagerTestBase {

    private boolean mIsHomeRecentsComponent;

    @Before
    @Override
    public void setUp() throws Exception {
        super.setUp();

        mIsHomeRecentsComponent = mWmState.isHomeRecentsComponent();

        assumeTrue("Skipping test: no split multi-window support",
                supportsSplitScreenMultiWindow());
    }

    @Test
    public void testMinimumDeviceSize() {
        mWmState.assertDeviceDefaultDisplaySizeForMultiWindow(
                "Devices supporting multi-window must be larger than the default minimum"
                        + " task size");
        mWmState.assertDeviceDefaultDisplaySizeForSplitScreen(
                "Devices supporting split-screen multi-window must be larger than the"
                        + " default minimum display size.");
    }

    /** Resizeable activity should be able to enter multi-window mode.*/
    @Test
    public void testResizeableActivity() {
        assertActivitySupportedInSplitScreen(TEST_ACTIVITY);
    }

    /**
     * Depending on the value of
     * {@link com.android.internal.R.integer.config_supportsNonResizableMultiWindow},
     * non-resizeable activity may or may not be able to enter multi-window mode.
     *
     * Based on the flag value:
     * -1: not support non-resizable in multi window.
     *  0: check the screen smallest width, if it is a large screen, support non-resizable in multi
     *     window. Otherwise, not support.
     *  1: always support non-resizable in multi window.
     */
    @Test
    public void testNonResizeableActivity() {
        createManagedDevEnableNonResizableMultiWindowSession().set(0);
        final Resources resources = mContext.getResources();
        final int configSupportsNonResizableMultiWindow;
        try {
            configSupportsNonResizableMultiWindow = resources.getInteger(resources.getIdentifier(
                    "config_supportsNonResizableMultiWindow", "integer", "android"));
        } catch (Resources.NotFoundException e) {
            fail("Device must define config_supportsNonResizableMultiWindow");
            return;
        }
        switch (configSupportsNonResizableMultiWindow) {
            case -1:
                assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
                break;
            case 1:
                assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
                break;
            case 0:
                final int configLargeScreenSmallestScreenWidthDp;
                try {
                    configLargeScreenSmallestScreenWidthDp =
                            resources.getInteger(resources.getIdentifier(
                                    "config_largeScreenSmallestScreenWidthDp",
                                    "integer", "android"));
                } catch (Resources.NotFoundException e) {
                    fail("Device must define config_largeScreenSmallestScreenWidthDp");
                    return;
                }
                final int smallestScreenWidthDp = mWmState.getHomeTask()
                        .mFullConfiguration.smallestScreenWidthDp;
                if (smallestScreenWidthDp >= configLargeScreenSmallestScreenWidthDp) {
                    assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
                } else {
                    assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
                }
                break;
            default:
                fail("config_supportsNonResizableMultiWindow must be -1, 0, or 1.");
        }
    }

    /**
     * Non-resizeable activity can enter split-screen if
     * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is
     * set.
     */
    @Test
    public void testDevEnableNonResizeableMultiWindow_splitScreenPrimary() {
        createManagedDevEnableNonResizableMultiWindowSession().set(1);

        assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
    }

    /**
     * Non-resizeable activity can enter split-screen if
     * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is
     * set.
     */
    @Test
    public void testDevEnableNonResizeableMultiWindow_splitScreenSecondary() {
        createManagedDevEnableNonResizableMultiWindowSession().set(1);

        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY),
                getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY));

        mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED);
        mWmState.assertVisibility(NON_RESIZEABLE_ACTIVITY, true);
        assertTrue(mWmState.containsActivityInWindowingMode(
                NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW));
    }

    /** Asserts that the give activity can be shown in split screen. */
    private void assertActivitySupportedInSplitScreen(ComponentName activity) {
        launchActivityInPrimarySplit(activity);
        mWmState.waitForActivityState(activity, STATE_RESUMED);
        mWmState.assertVisibility(activity, true);
        assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_MULTI_WINDOW));
    }

    /** Asserts that the give activity can NOT be shown in split screen. */
    private void assertActivityNotSupportedInSplitScreen(ComponentName activity) {
        boolean gotAssertionError = false;
        try {
            launchActivityInPrimarySplit(activity);
        } catch (AssertionError e) {
            gotAssertionError = true;
        }
        assertTrue("Trying to put non-resizeable activity in split should throw error.",
                gotAssertionError);
        mWmState.waitForActivityState(activity, STATE_RESUMED);
        mWmState.assertVisibility(activity, true);
        assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_FULLSCREEN));
    }

    @Test
    public void testLaunchToSideMultiWindowCallbacks() {
        // Launch two activities in split-screen mode.
        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder().setTargetActivity(NO_RELAUNCH_ACTIVITY),
                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));

        int displayWindowingMode = mWmState.getDisplay(
                mWmState.getDisplayByActivity(TEST_ACTIVITY)).getWindowingMode();
        separateTestJournal();
        mTaskOrganizer.dismissSplitScreen();
        if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) {
            // Exit split-screen mode and ensure we only get 1 multi-window mode changed callback.
            final ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged(
                    NO_RELAUNCH_ACTIVITY);
            assertEquals(1,
                    lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED));
        } else {
            // Display is not a fullscreen display, so there won't be a multi-window callback.
            // Instead just verify that windows are not in split-screen anymore.
            waitForIdle();
            mWmState.computeState();
            mWmState.assertDoesNotContainStack("Must have exited split-screen",
                    WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
        }
    }

    @Test
    public void testNoUserLeaveHintOnMultiWindowModeChanged() {
        launchActivity(NO_RELAUNCH_ACTIVITY, WINDOWING_MODE_FULLSCREEN);

        // Move to primary split.
        separateTestJournal();
        putActivityInPrimarySplit(NO_RELAUNCH_ACTIVITY);

        ActivityLifecycleCounts lifecycleCounts =
                waitForOnMultiWindowModeChanged(NO_RELAUNCH_ACTIVITY);
        assertEquals("mMultiWindowModeChangedCount",
                1, lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED));
        assertEquals("mUserLeaveHintCount",
                0, lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT));

        // Make sure primary split is focused. This way when we dismiss it later fullscreen stack
        // will come up.
        launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
        putActivityInSecondarySplit(LAUNCHING_ACTIVITY);

        launchActivity(NO_RELAUNCH_ACTIVITY);

        separateTestJournal();

        // Move activities back to fullscreen screen.
        // TestTaskOrganizer sets windowing modes of tasks to unspecific when putting them to split
        // screens so we need to explicitly set their windowing modes back to fullscreen to avoid
        // inheriting freeform windowing mode from the display on freeform first devices.
        int noRelaunchTaskId = mWmState.getTaskByActivity(NO_RELAUNCH_ACTIVITY).mTaskId;
        WindowContainerToken noRelaunchTaskToken =
                mTaskOrganizer.getTaskInfo(noRelaunchTaskId).getToken();
        WindowContainerTransaction t = new WindowContainerTransaction()
                .setWindowingMode(noRelaunchTaskToken, WINDOWING_MODE_FULLSCREEN);
        mTaskOrganizer.dismissSplitScreen(t, false /* primaryOnTop */);

        lifecycleCounts = waitForOnMultiWindowModeChanged(NO_RELAUNCH_ACTIVITY);
        assertEquals("mMultiWindowModeChangedCount",
                1, lifecycleCounts.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED));
        assertEquals("mUserLeaveHintCount",
                0, lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT));
    }

    @Test
    public void testLaunchToSideAndBringToFront() {
        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));

        mWmState.assertFocusedActivity("Launched to side activity must be in front.",
                TEST_ACTIVITY);

        // Launch another activity to side to cover first one.
        launchActivityInSecondarySplit(NO_RELAUNCH_ACTIVITY);
        mWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
                NO_RELAUNCH_ACTIVITY);

        // Launch activity that was first launched to side. It should be brought to front.
        launchActivity(TEST_ACTIVITY);
        mWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
                TEST_ACTIVITY);
    }

    @Test
    public void testLaunchToSideMultiple() {
        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));

        final int taskNumberInitial = mTaskOrganizer.getSecondarySplitTaskCount();

        // Try to launch to side same activity again.
        launchActivity(TEST_ACTIVITY);
        mWmState.computeState(TEST_ACTIVITY, LAUNCHING_ACTIVITY);
        final int taskNumberFinal = mTaskOrganizer.getSecondarySplitTaskCount();
        assertEquals("Task number mustn't change.", taskNumberInitial, taskNumberFinal);
        mWmState.assertFocusedActivity("Launched to side activity must remain in front.",
                TEST_ACTIVITY);
    }

    @Test
    public void testLaunchToSideSingleInstance() {
        launchTargetToSide(SINGLE_INSTANCE_ACTIVITY, false);
    }

    @Test
    public void testLaunchToSideSingleTask() {
        launchTargetToSide(SINGLE_TASK_ACTIVITY, false);
    }

    @Test
    public void testLaunchToSideMultipleWithDifferentIntent() {
        launchTargetToSide(TEST_ACTIVITY, true);
    }

    private void launchTargetToSide(ComponentName targetActivityName,
            boolean taskCountMustIncrement) {
        launchActivityInPrimarySplit(LAUNCHING_ACTIVITY);

        // Launch target to side
        final LaunchActivityBuilder targetActivityLauncher = getLaunchActivityBuilder()
                .setTargetActivity(targetActivityName)
                .setToSide(true)
                .setRandomData(true)
                .setMultipleTask(false);
        targetActivityLauncher.execute();
        final int secondaryTaskId = mWmState.getTaskByActivity(targetActivityName).mTaskId;
        mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId);

        mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);
        final int taskNumberInitial = mTaskOrganizer.getSecondarySplitTaskCount();

        // Try to launch to side same activity again with different data.
        targetActivityLauncher.execute();
        mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);

        final int[] excludeTaskIds = new int[] { secondaryTaskId, INVALID_TASK_ID };
        if (taskCountMustIncrement) {
            mWmState.waitFor("Waiting for new activity to come up.",
                    state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null);
        }
        WindowManagerState.ActivityTask task = mWmState.getTaskByActivity(targetActivityName,
                excludeTaskIds);
        final int secondaryTaskId2;
        if (task != null) {
            secondaryTaskId2 = task.mTaskId;
            mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId2);
        } else {
            secondaryTaskId2 = INVALID_TASK_ID;
        }
        final int taskNumberSecondLaunch = mTaskOrganizer.getSecondarySplitTaskCount();

        if (taskCountMustIncrement) {
            assertEquals("Task number must be incremented.", taskNumberInitial + 1,
                    taskNumberSecondLaunch);
        } else {
            assertEquals("Task number must not change.", taskNumberInitial,
                    taskNumberSecondLaunch);
        }
        mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.",
                targetActivityName);
        mWmState.assertFocusedActivity("Launched to side activity must be in front.",
                targetActivityName);

        // Try to launch to side same activity again with different random data. Note that null
        // cannot be used here, since the first instance of TestActivity is launched with no data
        // in order to launch into split screen.
        targetActivityLauncher.execute();
        mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);

        excludeTaskIds[1] = secondaryTaskId2;
        if (taskCountMustIncrement) {
            mWmState.waitFor("Waiting for the second new activity to come up.",
                    state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null);
        }
        WindowManagerState.ActivityTask taskFinal =
                mWmState.getTaskByActivity(targetActivityName, excludeTaskIds);
        if (taskFinal != null) {
            int secondaryTaskId3 = taskFinal.mTaskId;
            mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId3);
        }
        final int taskNumberFinal = mTaskOrganizer.getSecondarySplitTaskCount();

        if (taskCountMustIncrement) {
            assertEquals("Task number must be incremented.", taskNumberSecondLaunch + 1,
                    taskNumberFinal);
        } else {
            assertEquals("Task number must not change.", taskNumberSecondLaunch,
                    taskNumberFinal);
        }
        mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.",
                targetActivityName);
        mWmState.assertFocusedActivity("Launched to side activity must be in front.",
                targetActivityName);
    }

    @Test
    public void testLaunchToSideMultipleWithFlag() {
        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder()
                        .setTargetActivity(TEST_ACTIVITY),
                getLaunchActivityBuilder()
                        // Try to launch to side same activity again,
                        // but with Intent#FLAG_ACTIVITY_MULTIPLE_TASK.
                        .setMultipleTask(true)
                        .setTargetActivity(TEST_ACTIVITY));
        assertTrue("Primary split must contain TEST_ACTIVITY",
                mWmState.getRootTask(mTaskOrganizer.getPrimarySplitTaskId())
                        .containsActivity(TEST_ACTIVITY)
        );

        assertTrue("Secondary split must contain TEST_ACTIVITY",
                mWmState.getRootTask(mTaskOrganizer.getSecondarySplitTaskId())
                        .containsActivity(TEST_ACTIVITY)
                );
        mWmState.assertFocusedActivity("Launched to side activity must be in front.",
                TEST_ACTIVITY);
    }

    @Test
    public void testSameProcessActivityResumedPreQ() {
        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
                getLaunchActivityBuilder().setTargetActivity(SDK_27_LAUNCHING_ACTIVITY));

        assertEquals("There must be only one resumed activity in the package.", 1,
                mWmState.getResumedActivitiesCountInPackage(
                        SDK_27_TEST_ACTIVITY.getPackageName()));
    }

    @Test
    public void testDifferentProcessActivityResumedPreQ() {
        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
                getLaunchActivityBuilder().setTargetActivity(SDK_27_SEPARATE_PROCESS_ACTIVITY));

        assertEquals("There must be only two resumed activities in the package.", 2,
                mWmState.getResumedActivitiesCountInPackage(
                        SDK_27_TEST_ACTIVITY.getPackageName()));
    }

    @Test
    public void testDisallowUpdateWindowingModeWhenInLockedTask() {
        launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
        final WindowManagerState.ActivityTask task =
                mWmState.getStandardRootTaskByWindowingMode(
                        WINDOWING_MODE_FULLSCREEN).getTopTask();

        try {
            // Lock the task
            runWithShellPermission(() -> mAtm.startSystemLockTaskMode(task.mTaskId));
            waitForOrFail("Fail to enter locked task mode", () ->
                    mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE);

            // Verify specifying non-fullscreen windowing mode will fail.
            boolean exceptionThrown = false;
            try {
                runWithShellPermission(() -> {
                    final WindowContainerTransaction wct = new WindowContainerTransaction()
                            .setWindowingMode(
                                    mTaskOrganizer.getTaskInfo(task.mTaskId).getToken(),
                                    WINDOWING_MODE_MULTI_WINDOW);
                    mTaskOrganizer.applyTransaction(wct);
                });
            } catch (UnsupportedOperationException e) {
                exceptionThrown = true;
            }
            assertTrue("Not allowed to specify windowing mode while in locked task mode.",
                    exceptionThrown);
        } finally {
            runWithShellPermission(() -> {
                mAtm.stopSystemLockTaskMode();
            });
        }
    }

    @Test
    public void testDisallowHierarchyOperationWhenInLockedTask() {
        launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
        launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW);
        final WindowManagerState.ActivityTask task = mWmState
                .getStandardRootTaskByWindowingMode(WINDOWING_MODE_FULLSCREEN).getTopTask();
        final WindowManagerState.ActivityTask root = mWmState
                .getStandardRootTaskByWindowingMode(WINDOWING_MODE_MULTI_WINDOW).getTopTask();

        try {
            // Lock the task
            runWithShellPermission(() -> {
                mAtm.startSystemLockTaskMode(task.mTaskId);
            });
            waitForOrFail("Fail to enter locked task mode", () ->
                    mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE);

            boolean gotAssertionError = false;
            try {
                runWithShellPermission(() -> {
                    // Fetch tokens of testing task and multi-window root.
                    final WindowContainerToken multiWindowRoot =
                            mTaskOrganizer.getTaskInfo(root.mTaskId).getToken();
                    final WindowContainerToken testChild =
                            mTaskOrganizer.getTaskInfo(task.mTaskId).getToken();

                    // Verify performing reparent operation is no operation.
                    final WindowContainerTransaction wct = new WindowContainerTransaction()
                            .reparent(testChild, multiWindowRoot, true /* onTop */);
                    mTaskOrganizer.applyTransaction(wct);
                    waitForOrFail("Fail to reparent", () ->
                            mTaskOrganizer.getTaskInfo(task.mTaskId).getParentTaskId()
                                    == root.mTaskId);
                });
            } catch (AssertionError e) {
                gotAssertionError = true;
            }
            assertTrue("Not allowed to perform hierarchy operation while in locked task mode.",
                    gotAssertionError);
        } finally {
            runWithShellPermission(() -> {
                mAtm.stopSystemLockTaskMode();
            });
        }
    }

    /**
     * Asserts that the activity is visible when the top opaque activity finishes and with another
     * translucent activity on top while in split-screen-secondary task.
     */
    @Test
    public void testVisibilityWithTranslucentAndTopFinishingActivity() {
        // Launch two activities in split-screen mode.
        launchActivitiesInSplitScreen(
                getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
                getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY));

        mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());

        // Launch two more activities on a different task on top of split-screen-secondary and
        // only the top opaque activity should be visible.
        // Explicitly launch them into fullscreen mode because the control windowing mode of the
        // launch root doesn't include freeform mode. Freeform first devices launch apps in freeform
        // mode by default, which won't trigger the launch root.
        getLaunchActivityBuilder().setTargetActivity(TRANSLUCENT_TEST_ACTIVITY)
                .setUseInstrumentation()
                .setWaitForLaunched(true)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .execute();
        getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
                .setUseInstrumentation()
                .setWaitForLaunched(true)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .execute();
        mWmState.assertVisibility(TEST_ACTIVITY, true);
        mWmState.waitForActivityState(TRANSLUCENT_TEST_ACTIVITY, STATE_STOPPED);
        mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, false);
        mWmState.assertVisibility(TEST_ACTIVITY_WITH_SAME_AFFINITY, false);

        // Finish the top opaque activity and both the two activities should be visible.
        mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
        mWmState.computeState(new WaitForValidActivityState(TRANSLUCENT_TEST_ACTIVITY));
        mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, true);
        mWmState.assertVisibility(TEST_ACTIVITY_WITH_SAME_AFFINITY, true);
    }
}
