diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 9584994774a9d..535d656462f45 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3691,6 +3691,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", diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 9661e8d30b227..5919806ae7e17 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -34,6 +34,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; @@ -86,6 +87,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 diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 3d7ac6c1a3f88..8e21d9607072a 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -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 mSelectRootForWindowFunc; + @Nullable private Function 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 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)}. + *

+ * This function returns {@link TaskDisplayArea} specified by + * {@link ActivityOptions#getLaunchTaskDisplayArea()} if it is not {@code null}. Otherwise, + * returns {@link DisplayContent#getDefaultTaskDisplayArea()}. + *

+ */ + private static class DefaultSelectTaskDisplayAreaFunction implements + Function { + 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 mDisplayAreaGroupRoots; final BiFunction mSelectRootForWindowFunc; + private final Function mSelectTaskDisplayAreaFunc; private final TaskDisplayArea mDefaultTaskDisplayArea; Result(WindowManagerService wmService, RootDisplayArea root, List displayAreaGroupRoots, - BiFunction selectRootForWindowFunc) { + BiFunction selectRootForWindowFunc, + Function 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 { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8e0435f1908f0..217fc09ae21df 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -6464,9 +6464,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 diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index cb9eb52bfc00e..22e411ea1500a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -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; @@ -48,12 +49,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; @@ -80,6 +83,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(); @@ -103,6 +108,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); @@ -711,6 +717,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(