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

Commit 082dec98 authored by Charles Chen's avatar Charles Chen
Browse files

Choose TDA by DisplayAreaPolicy for WindowContext

Previously, WMS always associate TYPE_APPLICATION WindowContext with
the default TDA. This CL provides the capibility to select a TDA by
DisplayAreaPolicy.

Test: atest DisplayAreaPolicyBuilderTest
fixes: 185769955

Change-Id: Ie7cb686cd5d1da81ef618a3f1316dc3932a8272f
parent 410d7f7c
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -3649,6 +3649,18 @@
      "group": "WM_ERROR",
      "at": "com\/android\/server\/wm\/WindowManagerService.java"
    },
    "1874559932": {
      "message": "The TaskDisplayArea with %s does not exist.",
      "level": "WARN",
      "group": "WM_DEBUG_WINDOW_ORGANIZER",
      "at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.java"
    },
    "1884961873": {
      "message": "Sleep still need to stop %d activities",
      "level": "VERBOSE",
      "group": "WM_DEBUG_STATES",
      "at": "com\/android\/server\/wm\/Task.java"
    },
    "1891501279": {
      "message": "cancelAnimation(): reason=%s",
      "level": "DEBUG",
+4 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
import static com.android.server.wm.DisplayAreaPolicyBuilder.HierarchyBuilder;

import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.TextUtils;
@@ -88,6 +89,9 @@ public abstract class DisplayAreaPolicy {
     */
    public abstract TaskDisplayArea getDefaultTaskDisplayArea();

    /** Returns the {@link TaskDisplayArea} specified by launch options. */
    public abstract TaskDisplayArea getTaskDisplayArea(@Nullable Bundle options);

    /** Provider for platform-default display area policy. */
    static final class DefaultProvider implements DisplayAreaPolicy.Provider {
        @Override
+82 −5
Original line number Diff line number Diff line
@@ -27,12 +27,19 @@ import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;

import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.window.DisplayAreaOrganizer;
import android.window.WindowContainerToken;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;

import java.util.ArrayList;
@@ -43,6 +50,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * A builder for instantiating a complex {@link DisplayAreaPolicy}
@@ -149,6 +157,8 @@ class DisplayAreaPolicyBuilder {
     **/
    @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;

    @Nullable private Function<Bundle, TaskDisplayArea> mSelectTaskDisplayAreaFunc;

    /** Defines the root hierarchy for the whole logical display. */
    DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) {
        mRootHierarchyBuilder = rootHierarchyBuilder;
@@ -175,6 +185,16 @@ class DisplayAreaPolicyBuilder {
        return this;
    }

    /**
     * The policy will use this function to find the {@link TaskDisplayArea}.
     * @see DefaultSelectTaskDisplayAreaFunction as an example.
     */
    DisplayAreaPolicyBuilder setSelectTaskDisplayAreaFunc(
            Function<Bundle, TaskDisplayArea> selectTaskDisplayAreaFunc) {
        mSelectTaskDisplayAreaFunc = selectTaskDisplayAreaFunc;
        return this;
    }

    /**
     * Makes sure the setting meets the requirement:
     * 1. {@link #mRootHierarchyBuilder} must be set.
@@ -182,8 +202,9 @@ class DisplayAreaPolicyBuilder {
     * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids.
     * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container.
     * 5. There must be exactly one {@link HierarchyBuilder} that contains the default
     *    {@link TaskDisplayArea} with id {@link FEATURE_DEFAULT_TASK_CONTAINER}.
     * 6. None of the ids is greater than {@link FEATURE_VENDOR_LAST}.
     *    {@link TaskDisplayArea} with id
     *    {@link DisplayAreaOrganizer#FEATURE_DEFAULT_TASK_CONTAINER}.
     * 6. None of the ids is greater than {@link DisplayAreaOrganizer#FEATURE_VENDOR_LAST}.
     */
    private void validate() {
        if (mRootHierarchyBuilder == null) {
@@ -250,7 +271,7 @@ class DisplayAreaPolicyBuilder {
     * {@link Feature} below the same {@link RootDisplayArea} must have unique ids, but
     * {@link Feature} below different {@link RootDisplayArea} can have the same id so that we can
     * organize them together.
     * None of the ids is greater than {@link FEATURE_VENDOR_LAST}
     * None of the ids is greater than {@link DisplayAreaOrganizer#FEATURE_VENDOR_LAST}
     *
     * @param uniqueIdSet ids of {@link RootDisplayArea} and {@link TaskDisplayArea} that must be
     *                    unique,
@@ -323,7 +344,7 @@ class DisplayAreaPolicyBuilder {
                    mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
        }
        return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
                mSelectRootForWindowFunc);
                mSelectRootForWindowFunc, mSelectTaskDisplayAreaFunc);
    }

    /**
@@ -367,6 +388,51 @@ class DisplayAreaPolicyBuilder {
        }
    }

    /**
     * The default function to find {@link TaskDisplayArea} if there's no other function set
     * through {@link #setSelectTaskDisplayAreaFunc(Function)}.
     * <p>
     * This function returns {@link TaskDisplayArea} specified by
     * {@link ActivityOptions#getLaunchTaskDisplayArea()} if it is not {@code null}. Otherwise,
     * returns {@link DisplayContent#getDefaultTaskDisplayArea()}.
     * </p>
     */
    private static class DefaultSelectTaskDisplayAreaFunction implements
            Function<Bundle, TaskDisplayArea> {
        private final TaskDisplayArea mDefaultTaskDisplayArea;
        private final int mDisplayId;

        DefaultSelectTaskDisplayAreaFunction(TaskDisplayArea defaultTaskDisplayArea) {
            mDefaultTaskDisplayArea = defaultTaskDisplayArea;
            mDisplayId = defaultTaskDisplayArea.getDisplayId();
        }

        @Override
        public TaskDisplayArea apply(@Nullable Bundle options) {
            if (options == null) {
                return mDefaultTaskDisplayArea;
            }
            final ActivityOptions activityOptions = new ActivityOptions(options);
            final WindowContainerToken tdaToken = activityOptions.getLaunchTaskDisplayArea();
            if (tdaToken == null) {
                return mDefaultTaskDisplayArea;
            }
            final TaskDisplayArea tda = WindowContainer.fromBinder(tdaToken.asBinder())
                    .asTaskDisplayArea();
            if (tda == null) {
                ProtoLog.w(WM_DEBUG_WINDOW_ORGANIZER, "The TaskDisplayArea with %s does not "
                        + "exist.", tdaToken);
                return mDefaultTaskDisplayArea;
            }
            if (tda.getDisplayId() != mDisplayId) {
                throw new IllegalArgumentException("The specified TaskDisplayArea must attach "
                        + "to Display#" + mDisplayId + ", but it is in Display#"
                        + tda.getDisplayId());
            }
            return tda;
        }
    }

    /**
     *  Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a
     * {@link RootDisplayArea}
@@ -722,11 +788,13 @@ class DisplayAreaPolicyBuilder {
    static class Result extends DisplayAreaPolicy {
        final List<RootDisplayArea> mDisplayAreaGroupRoots;
        final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
        private final Function<Bundle, TaskDisplayArea> mSelectTaskDisplayAreaFunc;
        private final TaskDisplayArea mDefaultTaskDisplayArea;

        Result(WindowManagerService wmService, RootDisplayArea root,
                List<RootDisplayArea> displayAreaGroupRoots,
                BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) {
                BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc,
                Function<Bundle, TaskDisplayArea> selectTaskDisplayAreaFunc) {
            super(wmService, root);
            mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
            mSelectRootForWindowFunc = selectRootForWindowFunc;
@@ -740,6 +808,9 @@ class DisplayAreaPolicyBuilder {
                throw new IllegalStateException(
                        "No display area with FEATURE_DEFAULT_TASK_CONTAINER");
            }
            mSelectTaskDisplayAreaFunc = selectTaskDisplayAreaFunc != null
                    ? selectTaskDisplayAreaFunc
                    : new DefaultSelectTaskDisplayAreaFunction(mDefaultTaskDisplayArea);
        }

        @Override
@@ -796,6 +867,12 @@ class DisplayAreaPolicyBuilder {
        public TaskDisplayArea getDefaultTaskDisplayArea() {
            return mDefaultTaskDisplayArea;
        }

        @NonNull
        @Override
        public TaskDisplayArea getTaskDisplayArea(@Nullable Bundle options) {
            return mSelectTaskDisplayAreaFunc.apply(options);
        }
    }

    static class PendingArea {
+1 −2
Original line number Diff line number Diff line
@@ -6313,9 +6313,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp

    DisplayArea findAreaForWindowType(int windowType, Bundle options,
            boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
        // TODO(b/159767464): figure out how to find an appropriate TDA.
        if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
            return getDefaultTaskDisplayArea();
            return mDisplayAreaPolicy.getTaskDisplayArea(options);
        }
        // Return IME container here because it could be in one of sub RootDisplayAreas depending on
        // the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by
+208 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.wm;

import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -49,12 +50,14 @@ import static org.testng.Assert.assertThrows;

import static java.util.stream.Collectors.toList;

import android.app.ActivityOptions;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;

import com.google.android.collect.Lists;

@@ -81,6 +84,8 @@ import java.util.stream.Collectors;
 */
@Presubmit
public class DisplayAreaPolicyBuilderTest {
    private static final String KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID =
            "android.test.launchTaskDisplayAreaFeatureId";

    @Rule
    public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
@@ -104,6 +109,7 @@ public class DisplayAreaPolicyBuilderTest {
        mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer");
        mDisplayContent = mock(DisplayContent.class);
        doReturn(true).when(mDisplayContent).isTrusted();
        doReturn(DEFAULT_DISPLAY).when(mDisplayContent).getDisplayId();
        mDisplayContent.isDefaultDisplay = true;
        mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks",
                FEATURE_DEFAULT_TASK_CONTAINER);
@@ -731,6 +737,208 @@ public class DisplayAreaPolicyBuilderTest {
                .build();
    }

    @Test
    public void testGetTaskDisplayArea_DefaultFunction_NullOptions_ReturnsDefaultTda() {
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
                        .setTaskDisplayAreas(mTaskDisplayAreaList);
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
                        .setImeContainer(mImeContainer)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
                .setRootHierarchy(hierarchy0)
                .addDisplayAreaGroupHierarchy(hierarchy1)
                .addDisplayAreaGroupHierarchy(hierarchy2)
                .build(mWms);

        final TaskDisplayArea tda = policy.getTaskDisplayArea(null /* options */);

        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
    }

    @Test
    public void testGetTaskDisplayArea_DefaultFunction_NotContainsLunchedTda_ReturnsDefaultTda() {
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
                        .setTaskDisplayAreas(mTaskDisplayAreaList);
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
                        .setImeContainer(mImeContainer)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
                .setRootHierarchy(hierarchy0)
                .addDisplayAreaGroupHierarchy(hierarchy1)
                .addDisplayAreaGroupHierarchy(hierarchy2)
                .build(mWms);

        final TaskDisplayArea tda = policy.getTaskDisplayArea(new Bundle());

        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
    }

    @Test
    public void testGetTaskDisplayArea_DefaultFunction_InvalidTdaToken_ReturnsDefaultTda() {
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
                        .setTaskDisplayAreas(mTaskDisplayAreaList);
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
                        .setImeContainer(mImeContainer)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
                .setRootHierarchy(hierarchy0)
                .addDisplayAreaGroupHierarchy(hierarchy1)
                .addDisplayAreaGroupHierarchy(hierarchy2)
                .build(mWms);
        final ActivityOptions options = ActivityOptions.makeBasic();
        final WindowContainerToken fakeToken = mRoot.mRemoteToken.toWindowContainerToken();
        options.setLaunchTaskDisplayArea(fakeToken);

        final TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());

        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
    }

    @Test(expected = IllegalArgumentException.class)
    public void testGetTaskDisplayArea_DefaultFunction_TdaOnDifferentDisplay_ThrowException() {
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
                        .setTaskDisplayAreas(mTaskDisplayAreaList);
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
                        .setImeContainer(mImeContainer)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
                .setRootHierarchy(hierarchy0)
                .addDisplayAreaGroupHierarchy(hierarchy1)
                .addDisplayAreaGroupHierarchy(hierarchy2)
                .build(mWms);
        final TaskDisplayArea tdaOnSecondaryDisplay = mock(TaskDisplayArea.class);
        doReturn(DEFAULT_DISPLAY + 1).when(tdaOnSecondaryDisplay).getDisplayId();
        doReturn(tdaOnSecondaryDisplay).when(tdaOnSecondaryDisplay).asTaskDisplayArea();
        tdaOnSecondaryDisplay.mRemoteToken = new WindowContainer.RemoteToken(tdaOnSecondaryDisplay);

        final WindowContainerToken tdaToken = tdaOnSecondaryDisplay.mRemoteToken
                .toWindowContainerToken();
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchTaskDisplayArea(tdaToken);
        final TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());

        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
    }

    @Test
    public void testGetTaskDisplayArea_DefaultFunction_ContainsTdaToken_ReturnsTda() {
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
                        .setTaskDisplayAreas(mTaskDisplayAreaList);
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
                        .setImeContainer(mImeContainer)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
                .setRootHierarchy(hierarchy0)
                .addDisplayAreaGroupHierarchy(hierarchy1)
                .addDisplayAreaGroupHierarchy(hierarchy2)
                .build(mWms);
        final ActivityOptions options = ActivityOptions.makeBasic();

        final WindowContainerToken defaultTdaToken = mDefaultTaskDisplayArea.mRemoteToken
                .toWindowContainerToken();
        options.setLaunchTaskDisplayArea(defaultTdaToken);
        TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());

        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());

        final WindowContainerToken tda1Token = mTda1.mRemoteToken.toWindowContainerToken();
        options.setLaunchTaskDisplayArea(tda1Token);
        tda = policy.getTaskDisplayArea(options.toBundle());

        assertThat(tda).isEqualTo(mTda1);

        final WindowContainerToken tda2Token = mTda2.mRemoteToken.toWindowContainerToken();
        options.setLaunchTaskDisplayArea(tda2Token);
        tda = policy.getTaskDisplayArea(options.toBundle());

        assertThat(tda).isEqualTo(mTda2);
    }

    @Test
    public void testBuilder_getTaskDisplayArea_setSelectTaskDisplayAreaFunc() {
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
                        .setTaskDisplayAreas(mTaskDisplayAreaList);
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
                        .setImeContainer(mImeContainer)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda1));
        final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
                new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
                        .setTaskDisplayAreas(Lists.newArrayList(mTda2));
        final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
                .setRootHierarchy(hierarchy0)
                .setSelectTaskDisplayAreaFunc((options) -> {
                    if (options == null) {
                        return mDefaultTaskDisplayArea;
                    }
                    final int tdaFeatureId =
                            options.getInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID);
                    if (tdaFeatureId == mTda1.mFeatureId) {
                        return mTda1;
                    }
                    if (tdaFeatureId == mTda2.mFeatureId) {
                        return mTda2;
                    }
                    return mDefaultTaskDisplayArea;
                })
                .addDisplayAreaGroupHierarchy(hierarchy1)
                .addDisplayAreaGroupHierarchy(hierarchy2)
                .build(mWms);

        TaskDisplayArea tda = policy.getTaskDisplayArea(null /* options */);

        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
        assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());

        final Bundle options = new Bundle();
        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, -1);
        tda = policy.getTaskDisplayArea(options);
        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);

        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mDefaultTaskDisplayArea.mFeatureId);
        tda = policy.getTaskDisplayArea(options);
        assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);

        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mTda1.mFeatureId);
        tda = policy.getTaskDisplayArea(options);
        assertThat(tda).isEqualTo(mTda1);

        options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mTda2.mFeatureId);
        tda = policy.getTaskDisplayArea(options);
        assertThat(tda).isEqualTo(mTda2);
    }

    private static Resources resourcesWithProvider(String provider) {
        Resources mock = mock(Resources.class);
        when(mock.getString(