Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4719e35a authored by Toshiki Kikuchi's avatar Toshiki Kikuchi
Browse files

Move desktop-first policy from Shell to LPM

This CL moves desktop-first policy around windowing mode decisions from
handleRequest to LaunchParamsModifier so that the launch windowing mode
by ActivityOptions can be respected (and there will be no double
windowing mode change on an app launch).
Note that this change is exclusive for desktop-first and a few
touch-first policies remain in handleRequest.

Flag: com.android.window.flags.enable_desktop_first_policy_in_lpm
Bug: 427828063
Bug: 431866074
Test: DesktopTasksControllerTest
Test: DesktopModeLaunchParamsModifierTests
Change-Id: Ia7a0c0b5b39b4c8b5abe180615d205b96f433fcf
parent ed169d6c
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -3526,13 +3526,19 @@ class DesktopTasksController(
                // If there is an active desk on the target display, then it is already in desktop
                // windowing so the new task should also be placed in desktop windowing.
                anyDeskActive -> true
                DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_TOP_FULLSCREEN_BUGFIX.isTrue ->
                DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_TOP_FULLSCREEN_BUGFIX.isTrue ||
                    DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_POLICY_IN_LPM.isTrue ->
                    // Here we have no desk activated, but check if we really want to force a task
                    // into desktop.
                    if (rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(task.displayId)) {
                        if (DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_POLICY_IN_LPM.isTrue) {
                            // Fully trust the LPM's decision under desktop-first mode.
                            true
                        } else {
                            // In desktop-first mode, we force to activate desk only when the
                            // desktop-first policy can be applied.
                            shouldForceEnterDesktop
                        }
                    } else {
                        // In touch-first mode, new tasks should be forced into desktop, while known
                        // desktop tasks should be moved outside of desktop.
@@ -3828,6 +3834,12 @@ class DesktopTasksController(
        }

        val isDesktopFirst = rootTaskDisplayAreaOrganizer.isDisplayDesktopFirst(targetDisplayId)
        if (DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_POLICY_IN_LPM.isTrue && isDesktopFirst) {
            // The desktop-first policy has to be considered already in
            // `DesktopModeLaunchParamsModifier`.
            return false
        }

        if (DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_TOP_FULLSCREEN_BUGFIX.isTrue) {
            val anyDeskActive = isAnyDeskActive(targetDisplayId)
            val focusedTask = focusTransitionObserver.getFocusedTaskOnDisplay(targetDisplayId)
+27 −2
Original line number Diff line number Diff line
@@ -5637,7 +5637,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
    @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
    @DisableFlags(
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
        Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM,
    )
    fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
        desktopState.enterDesktopByDefaultOnFreeformDisplay = true
@@ -5664,6 +5667,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
    )
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM)
    fun handleRequest_fullscreenTask_noInDesk_enforceDesktop_freeformDisplay_movesToDesk() {
        val deskId = 0
        taskRepository.setDeskInactive(deskId)
@@ -5685,13 +5689,13 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
        Flags.FLAG_ENABLE_DESKTOP_FIRST_BASED_DEFAULT_TO_DESKTOP_BUGFIX,
    )
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM)
    fun handleRequest_fullscreenTask_noInDesk_enforceDesktop_freeformDisplay_movesToDesk_desktopFirst() {
        // Ensure the force enter desktop works when the deprecated flag is off.
        desktopState.enterDesktopByDefaultOnFreeformDisplay = false
        val deskId = 0
        taskRepository.setDeskInactive(deskId)
        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
        desktopState.enterDesktopByDefaultOnFreeformDisplay = true
        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM

@@ -5707,6 +5711,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
    )
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM)
    fun handleRequest_fullscreenTask_noInDesk_enforceDesktop_secondaryDisplay_movesToDesk() {
        val deskId = 5
        taskRepository.addDesk(displayId = SECONDARY_DISPLAY_ID, deskId = deskId)
@@ -5732,6 +5737,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        Flags.FLAG_ENABLE_DESKTOP_FIRST_BASED_DEFAULT_TO_DESKTOP_BUGFIX,
        Flags.FLAG_ENABLE_DESKTOP_FIRST_TOP_FULLSCREEN_BUGFIX,
    )
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM)
    fun handleRequest_fullscreenTask_fullscreenFocused_freeformDisplay_returnNull() {
        val deskId = 0
        taskRepository.setDeskInactive(deskId)
@@ -5748,6 +5754,25 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
        Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
        Flags.FLAG_ENABLE_DESKTOP_FIRST_BASED_DEFAULT_TO_DESKTOP_BUGFIX,
        Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM,
    )
    fun handleRequest_fullscreenTask_noInDesk_enforceDesktop_desktopFirst_returnNull() {
        val deskId = 0
        taskRepository.setDeskInactive(deskId)
        whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
        desktopState.enterDesktopByDefaultOnFreeformDisplay = true
        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM

        val fullscreenTask = createFullscreenTask()
        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
    }

    @Test
    fun handleRequest_fullscreenTask_notInDesk_enforceDesktop_fullscreenDisplay_returnNull() {
        taskRepository.setDeskInactive(deskId = 0)
+94 −8
Original line number Diff line number Diff line
@@ -134,13 +134,27 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
            }
        }

        boolean forcedFreeformByDesktopFirstPolicy = shouldApplyDesktopFirstWindowingModePolicy(
                task, source, options, suggestedDisplayArea, currentParams);
        if (forcedFreeformByDesktopFirstPolicy) {
            outParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
            if (task == null) {
                // Windowing mode is resolved by desktop-first policy but not ready to resolve
                // bounds since task is null, return RESULT_DONE to prevent other modifiers from
                // overwriting the params.
                return RESULT_DONE;
            }
            hasLaunchWindowingMode = true;
        }

        if (task == null || !task.isAttached()) {
            appendLog("task null, skipping");
            return RESULT_SKIP;
        }

        if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()
                && !isEnteringDesktopMode(task, options, currentParams)) {
                && !isEnteringDesktopMode(task, source, options, suggestedDisplayArea,
                currentParams)) {
            appendLog("not entering desktop mode, skipping");
            return RESULT_SKIP;
        }
@@ -199,6 +213,9 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
            // Copy over any values.
            outParams.set(currentParams);
            outParams.mPreferredTaskDisplayArea = suggestedDisplayArea;
            if (forcedFreeformByDesktopFirstPolicy) {
                outParams.mWindowingMode = WINDOWING_MODE_FREEFORM;
            }
        }

        boolean isFullscreenInDeskTask = inDesktopFirstContainer && requestFullscreen;
@@ -209,7 +226,8 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
            requestFullscreen |= task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
            isFullscreenInDeskTask = inDesktopFirstContainer && requestFullscreen;
            if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()
                    && isEnteringDesktopMode(sourceTask, options, currentParams)
                    && isEnteringDesktopMode(sourceTask, source, options, suggestedDisplayArea,
                    currentParams)
                    && !isFullscreenInDeskTask) {
                // If trampoline source is not freeform but we are entering or in desktop mode,
                // ignore the source windowing mode and set the windowing mode to freeform.
@@ -304,8 +322,32 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
    @VisibleForTesting
    boolean isEnteringDesktopMode(
            @NonNull Task task,
            @Nullable ActivityRecord source,
            @Nullable ActivityOptions options,
            @NonNull TaskDisplayArea taskDisplayArea,
            @NonNull LaunchParamsController.LaunchParams currentParams) {
        if (isRequestingFreeformWindowMode(task, options, currentParams)) {
            // It's launching in freeform without any modifications.
            return true;
        }

        if (!checkSourceWindowModesCompatible(task, options, currentParams)) {
            // It's launching in incompatible mode.
            return false;
        }

        if (shouldApplyDesktopFirstWindowingModePolicy(task, source, options, taskDisplayArea,
                currentParams)) {
            // It's a target of desktop-first policy.
            return true;
        }

        if (DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_POLICY_IN_LPM.isTrue()
                && taskDisplayArea.inFreeformWindowingMode()) {
            // The display is in desktop-first mode but non-freeform mode is requested.
            return false;
        }

        //  As freeform tasks cannot exist outside of desktop mode, it is safe to assume if
        //  freeform tasks are visible we are in desktop mode and as a result any launching
        //  activity will also enter desktop mode. On this same relationship, we can also assume
@@ -313,9 +355,7 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
        //  will force the device into desktop mode.
        final Task visibleFreeformTask = task.getDisplayContent().getTask(
                t -> t.inFreeformWindowingMode() && t.isVisibleRequested());
        return (visibleFreeformTask != null
                    && checkSourceWindowModesCompatible(task, options, currentParams))
                || isRequestingFreeformWindowMode(task, options, currentParams);
        return visibleFreeformTask != null;
    }

    private boolean isRequestingFreeformWindowMode(
@@ -328,16 +368,62 @@ class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
                && !DesktopExperienceFlags.IGNORE_CURRENT_PARAMS_IN_DESKTOP_LAUNCH_PARAMS.isTrue());
    }

    /**
     * Modify windowing mode of LaunchParams according to the desktop-first policy. Returns true if
     * the policy is applied.
     */
    private boolean shouldApplyDesktopFirstWindowingModePolicy(
            @Nullable Task task,
            @Nullable ActivityRecord source,
            @Nullable ActivityOptions options,
            @NonNull TaskDisplayArea taskDisplayArea,
            @NonNull LaunchParamsController.LaunchParams currentParams) {
        if (!DesktopExperienceFlags.ENABLE_DESKTOP_FIRST_POLICY_IN_LPM.isTrue()) {
            return false;
        }

        if (!taskDisplayArea.inFreeformWindowingMode()) {
            // The display is in touch-first mode.
            return false;
        }

        if (!checkSourceWindowModesCompatible(task, options, currentParams)) {
            // The task is launching in incompatible mode (e.g., PIP).
            appendLog("desktop-first-but-incompatible-mode");
            return false;
        }

        final boolean hasLaunchWindowingModeOption = options != null
                && options.getLaunchWindowingMode() != WINDOWING_MODE_UNDEFINED;
        if (hasLaunchWindowingModeOption) {
            // ActivityOptions comes first.
            appendLog("desktop-first-but-has-launch-windowing-mode-options");
            return false;
        }

        final boolean isFullscreenRelaunch = source != null && source.getTask() != null
                && source.getTask().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                && source.getTask() == task;
        if (isFullscreenRelaunch) {
            // Fullscreen relaunch is not a target of desktop-first policy.
            appendLog("desktop-first-but-fullscreen-relaunch");
            return false;
        }

        appendLog("forced-freeform-in-desktop-first");
        return true;
    }

    /**
     * Returns true is all possible source window modes are compatible with desktop mode.
     */
    private boolean checkSourceWindowModesCompatible(
            @NonNull Task task,
            @Nullable Task task,
            @Nullable ActivityOptions options,
            @NonNull LaunchParamsController.LaunchParams currentParams) {
        // 1. Check the task's own windowing mode.
        final boolean isTaskWindowModeCompatible =
                isCompatibleDesktopWindowingMode(task.getWindowingMode());
        final boolean isTaskWindowModeCompatible = task == null
                || isCompatibleDesktopWindowingMode(task.getWindowingMode());
        // 2. Check the windowing mode from ActivityOptions, if they exist.
        // If options are null, we consider it compatible.
        final boolean isOptionsWindowModeCompatible = options == null
+113 −8
Original line number Diff line number Diff line
@@ -131,7 +131,7 @@ public class DesktopModeLaunchParamsModifierTests extends
        Context spyContext = spy(mContext);
        mTarget = spy(new DesktopModeLaunchParamsModifier(spyContext, mSupervisor,
                new DesktopModeCompatPolicy(spyContext)));
        doReturn(true).when(mTarget).isEnteringDesktopMode(any(), any(), any());
        doReturn(true).when(mTarget).isEnteringDesktopMode(any(), any(), any(), any(), any());
        doReturn(HOME_ACTIVITIES).when(mPackageManager).getHomeActivities(any());
        doReturn(mPackageManager).when(spyContext).getPackageManager();
    }
@@ -176,7 +176,7 @@ public class DesktopModeLaunchParamsModifierTests extends
            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
    public void testReturnsSkipIfIsEnteringDesktopModeFalse() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenReturn(false);
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenReturn(false);

        final Task task = new TaskBuilder(mSupervisor).build();

@@ -188,7 +188,7 @@ public class DesktopModeLaunchParamsModifierTests extends
            Flags.FLAG_RESPECT_FULLSCREEN_ACTIVITY_OPTION_IN_DESKTOP_LAUNCH_PARAMS})
    public void testAppliesFullscreenAndReturnDoneIfRequestViaActivityOptions() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenReturn(true);
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenReturn(true);

        final Task task = new TaskBuilder(mSupervisor).build();
        final ActivityOptions options = ActivityOptions.makeBasic();
@@ -365,7 +365,7 @@ public class DesktopModeLaunchParamsModifierTests extends
            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
    public void testReturnsContinueIfFreeformTaskExists() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final DisplayContent dc = spy(createNewDisplay());
        final Task existingFreeformTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
@@ -383,7 +383,7 @@ public class DesktopModeLaunchParamsModifierTests extends
            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
    public void testReturnsContinueIfTaskInFreeform() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final Task task = new TaskBuilder(mSupervisor).setWindowingMode(WINDOWING_MODE_FREEFORM)
                .build();
@@ -397,7 +397,7 @@ public class DesktopModeLaunchParamsModifierTests extends
            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
    public void testReturnsContinueIfFreeformRequestViaActivityOptions() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final Task task = new TaskBuilder(mSupervisor).build();
        final ActivityOptions options = ActivityOptions.makeBasic();
@@ -412,7 +412,7 @@ public class DesktopModeLaunchParamsModifierTests extends
            Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
    public void testReturnsContinueIfFreeformRequestViaPreviousModifier() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final Task task = new TaskBuilder(mSupervisor).build();
        final ActivityOptions options = ActivityOptions.makeBasic();
@@ -1920,7 +1920,7 @@ public class DesktopModeLaunchParamsModifierTests extends
            Flags.FLAG_IGNORE_CURRENT_PARAMS_IN_DESKTOP_LAUNCH_PARAMS})
    public void testDoesntInheritWindowingModeFromCurrentParams() {
        setupDesktopModeLaunchParamsModifier();
        doCallRealMethod().when(mTarget).isEnteringDesktopMode(any(), any(), any());
        doCallRealMethod().when(mTarget).isEnteringDesktopMode(any(), any(), any(), any(), any());

        final Task task = new TaskBuilder(mSupervisor).setActivityType(
                ACTIVITY_TYPE_STANDARD).build();
@@ -1982,6 +1982,111 @@ public class DesktopModeLaunchParamsModifierTests extends
        assertEquals(emptyRect, mResult.mAppBounds);
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM,
            Flags.FLAG_ENABLE_FREEFORM_DISPLAY_LAUNCH_PARAMS})
    public void testCalculate_desktopFirstPolicy_forcesFreeform() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final DisplayContent dc = createDisplayContent(ORIENTATION_LANDSCAPE,
                LANDSCAPE_DISPLAY_BOUNDS, WINDOWING_MODE_FREEFORM);
        final Task launchingTask = new TaskBuilder(mSupervisor)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .setDisplay(dc)
                .build();

        assertEquals(RESULT_DONE,
                new CalculateRequestBuilder().setTask(launchingTask).calculate());
        assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM,
            Flags.FLAG_ENABLE_FREEFORM_DISPLAY_LAUNCH_PARAMS})
    public void testCalculate_desktopFirstPolicy_taskNull_forcesFreeform() {
        setupDesktopModeLaunchParamsModifier();

        final DisplayContent dc = createDisplayContent(ORIENTATION_LANDSCAPE,
                LANDSCAPE_DISPLAY_BOUNDS, WINDOWING_MODE_FREEFORM);
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchDisplayId(dc.getDisplayId());

        // When task is null, getPreferredLaunchTaskDisplayArea will use the display from options.
        // Then forceFreeformByDesktopFirstPolicy will be true.
        // Because task is null, it should return RESULT_DONE.
        assertEquals(RESULT_DONE,
                new CalculateRequestBuilder().setTask(null).setOptions(options).calculate());
        assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM,
            Flags.FLAG_ENABLE_FREEFORM_DISPLAY_LAUNCH_PARAMS})
    public void testCalculate_desktopFirstPolicy_fullscreenRelaunch_bypassesPolicy() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final DisplayContent dc = createDisplayContent(ORIENTATION_LANDSCAPE,
                LANDSCAPE_DISPLAY_BOUNDS, WINDOWING_MODE_FREEFORM);
        final Task launchingTask = new TaskBuilder(mSupervisor)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .setDisplay(dc)
                .build();
        final ActivityRecord source = new ActivityBuilder(mAtm).setTask(launchingTask).build();

        // The task is launched by a different task on a desktop-first display.
        assertEquals(RESULT_SKIP,
                new CalculateRequestBuilder().setTask(launchingTask).setSource(source).calculate());
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM,
            Flags.FLAG_ENABLE_FREEFORM_DISPLAY_LAUNCH_PARAMS})
    public void testCalculate_desktopFirstPolicy_fullscreenSourceTask_forcesFreeform() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final DisplayContent dc = createDisplayContent(ORIENTATION_LANDSCAPE,
                LANDSCAPE_DISPLAY_BOUNDS, WINDOWING_MODE_FREEFORM);
        final Task launchingTask = new TaskBuilder(mSupervisor)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .setDisplay(dc)
                .build();
        final Task sourceTask = new TaskBuilder(mSupervisor)
                .setWindowingMode(WINDOWING_MODE_FREEFORM)
                .setDisplay(dc)
                .build();
        final ActivityRecord source = new ActivityBuilder(mAtm).setTask(sourceTask).build();

        // The task is launched by a different task on a desktop-first display.
        assertEquals(RESULT_DONE,
                new CalculateRequestBuilder().setTask(launchingTask).setSource(source).calculate());
        assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_FIRST_POLICY_IN_LPM,
            Flags.FLAG_ENABLE_FREEFORM_DISPLAY_LAUNCH_PARAMS})
    public void testCalculate_desktopFirstPolicy_requestFullscreen_bypassesPolicy() {
        setupDesktopModeLaunchParamsModifier();
        when(mTarget.isEnteringDesktopMode(any(), any(), any(), any(), any())).thenCallRealMethod();

        final DisplayContent dc = createDisplayContent(ORIENTATION_LANDSCAPE,
                LANDSCAPE_DISPLAY_BOUNDS, WINDOWING_MODE_FREEFORM);
        final Task launchingTask = new TaskBuilder(mSupervisor)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .setDisplay(dc)
                .build();
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);

        // The policy should be bypassed if fullscreen is requested.
        assertEquals(RESULT_SKIP,
                new CalculateRequestBuilder().setTask(launchingTask).setOptions(
                        options).calculate());
    }

    private Task createTask(DisplayContent display, boolean isResizeable) {
        final int resizeMode = isResizeable ? RESIZE_MODE_RESIZEABLE
                : RESIZE_MODE_UNRESIZEABLE;