From e83e2a20c638d0045d86ccd65fa246cb66eafa48 Mon Sep 17 00:00:00 2001 From: Nicu Corj Date: Mon, 30 May 2022 17:06:56 +0300 Subject: [PATCH] Add API to set launch TDA featureId in options - Add new api in ActivityOptions for a caller to be able to specify launch TaskDisplayArea in ActivityOptions. - Add option to specify launch TaskDisplayArea featureId in ActivityManagerShellCommand. Bug: 234349363 Test: atest TaskLaunchParamsModifierTests Change-Id: I092787ede5245d15774e01054c408533fe632b57 --- core/api/test-current.txt | 1 + core/java/android/app/ActivityOptions.java | 32 ++++++++ .../am/ActivityManagerShellCommand.java | 11 +++ .../server/wm/SafeActivityOptions.java | 21 ++++- .../server/wm/TaskLaunchParamsModifier.java | 21 ++++- .../wm/TaskLaunchParamsModifierTests.java | 77 ++++++++++++++++++- 6 files changed, 159 insertions(+), 4 deletions(-) diff --git a/core/api/test-current.txt b/core/api/test-current.txt index bfbb9105dbb89..51ecd4cc61c6b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -149,6 +149,7 @@ package android.app { method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener); method public static void setExitTransitionTimeout(long); method public void setLaunchActivityType(int); + method public void setLaunchTaskDisplayAreaFeatureId(int); method public void setLaunchWindowingMode(int); method public void setLaunchedFromBubble(boolean); method public void setTaskAlwaysOnTop(boolean); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 0ff9f6655b8a8..a92cbf3b96e54 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import android.annotation.IntDef; import android.annotation.NonNull; @@ -207,6 +208,14 @@ public class ActivityOptions { private static final String KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN = "android.activity.launchTaskDisplayAreaToken"; + /** + * The task display area feature id the activity should be launched into. + * @see #setLaunchTaskDisplayAreaFeatureId(int) + * @hide + */ + private static final String KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID = + "android.activity.launchTaskDisplayAreaFeatureId"; + /** * The root task token the activity should be launched into. * @see #setLaunchRootTask(WindowContainerToken) @@ -404,6 +413,7 @@ public class ActivityOptions { private int mLaunchDisplayId = INVALID_DISPLAY; private int mCallerDisplayId = INVALID_DISPLAY; private WindowContainerToken mLaunchTaskDisplayArea; + private int mLaunchTaskDisplayAreaFeatureId = FEATURE_UNDEFINED; private WindowContainerToken mLaunchRootTask; private IBinder mLaunchTaskFragmentToken; @WindowConfiguration.WindowingMode @@ -1147,6 +1157,8 @@ public class ActivityOptions { mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY); mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY); mLaunchTaskDisplayArea = opts.getParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN); + mLaunchTaskDisplayAreaFeatureId = opts.getInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, + FEATURE_UNDEFINED); mLaunchRootTask = opts.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN); mLaunchTaskFragmentToken = opts.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN); mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED); @@ -1471,6 +1483,23 @@ public class ActivityOptions { return this; } + /** @hide */ + public int getLaunchTaskDisplayAreaFeatureId() { + return mLaunchTaskDisplayAreaFeatureId; + } + + /** + * Sets the TaskDisplayArea feature Id the activity should launch into. + * Note: It is possible to have TaskDisplayAreas with the same featureId on multiple displays. + * If launch display id is not specified, the TaskDisplayArea on the default display will be + * used. + * @hide + */ + @TestApi + public void setLaunchTaskDisplayAreaFeatureId(int launchTaskDisplayAreaFeatureId) { + mLaunchTaskDisplayAreaFeatureId = launchTaskDisplayAreaFeatureId; + } + /** @hide */ public WindowContainerToken getLaunchRootTask() { return mLaunchRootTask; @@ -1902,6 +1931,9 @@ public class ActivityOptions { if (mLaunchTaskDisplayArea != null) { b.putParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN, mLaunchTaskDisplayArea); } + if (mLaunchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) { + b.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mLaunchTaskDisplayAreaFeatureId); + } if (mLaunchRootTask != null) { b.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, mLaunchRootTask); } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index ea28117a6a3df..c222c5715e65f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -24,6 +24,7 @@ import static android.app.WaitResult.launchStateToString; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL; import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW; @@ -170,6 +171,7 @@ final class ActivityManagerShellCommand extends ShellCommand { private String mAgent; // Agent to attach on startup. private boolean mAttachAgentDuringBind; // Whether agent should be attached late. private int mDisplayId; + private int mTaskDisplayAreaFeatureId; private int mWindowingMode; private int mActivityType; private int mTaskId; @@ -353,6 +355,7 @@ final class ActivityManagerShellCommand extends ShellCommand { mStreaming = false; mUserId = defUser; mDisplayId = INVALID_DISPLAY; + mTaskDisplayAreaFeatureId = FEATURE_UNDEFINED; mWindowingMode = WINDOWING_MODE_UNDEFINED; mActivityType = ACTIVITY_TYPE_UNDEFINED; mTaskId = INVALID_TASK_ID; @@ -408,6 +411,8 @@ final class ActivityManagerShellCommand extends ShellCommand { mReceiverPermission = getNextArgRequired(); } else if (opt.equals("--display")) { mDisplayId = Integer.parseInt(getNextArgRequired()); + } else if (opt.equals("--task-display-area-feature-id")) { + mTaskDisplayAreaFeatureId = Integer.parseInt(getNextArgRequired()); } else if (opt.equals("--windowingMode")) { mWindowingMode = Integer.parseInt(getNextArgRequired()); } else if (opt.equals("--activityType")) { @@ -535,6 +540,12 @@ final class ActivityManagerShellCommand extends ShellCommand { options = ActivityOptions.makeBasic(); options.setLaunchDisplayId(mDisplayId); } + if (mTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) { + if (options == null) { + options = ActivityOptions.makeBasic(); + } + options.setLaunchTaskDisplayAreaFeatureId(mTaskDisplayAreaFeatureId); + } if (mWindowingMode != WINDOWING_MODE_UNDEFINED) { if (options == null) { options = ActivityOptions.makeBasic(); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index 2d4aef682d623..66d02308bf0b6 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -25,7 +25,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -248,8 +250,25 @@ public class SafeActivityOptions { } // Check if the caller is allowed to launch on the specified display area. final WindowContainerToken daToken = options.getLaunchTaskDisplayArea(); - final TaskDisplayArea taskDisplayArea = daToken != null + TaskDisplayArea taskDisplayArea = daToken != null ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; + + // If we do not have a task display area token, check if the launch task display area + // feature id is specified. + if (taskDisplayArea == null) { + final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId(); + if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) { + final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY + ? DEFAULT_DISPLAY : options.getLaunchDisplayId(); + final DisplayContent dc = supervisor.mRootWindowContainer + .getDisplayContent(launchDisplayId); + if (dc != null) { + taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda -> + tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null); + } + } + } + if (aInfo != null && taskDisplayArea != null && !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid, taskDisplayArea, aInfo)) { diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index b8ceb4a4f421b..24a745b3896ed 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -35,6 +35,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT; import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; @@ -293,7 +294,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { TaskDisplayArea taskDisplayArea = suggestedDisplayArea; // If launch task display area is set in options we should just use it. We assume the // suggestedDisplayArea has the right one in this case. - if (options == null || options.getLaunchTaskDisplayArea() == null) { + if (options == null || (options.getLaunchTaskDisplayArea() == null + && options.getLaunchTaskDisplayAreaFeatureId() == FEATURE_UNDEFINED)) { final int activityType = mSupervisor.mRootWindowContainer.resolveActivityType(root, options, task); display.forAllTaskDisplayAreas(displayArea -> { @@ -377,7 +379,22 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { if (optionLaunchTaskDisplayAreaToken != null) { taskDisplayArea = (TaskDisplayArea) WindowContainer.fromBinder( optionLaunchTaskDisplayAreaToken.asBinder()); - if (DEBUG) appendLog("display-area-from-option=" + taskDisplayArea); + if (DEBUG) appendLog("display-area-token-from-option=" + taskDisplayArea); + } + + if (taskDisplayArea == null && options != null) { + final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId(); + if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) { + final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY + ? DEFAULT_DISPLAY : options.getLaunchDisplayId(); + final DisplayContent dc = mSupervisor.mRootWindowContainer + .getDisplayContent(launchDisplayId); + if (dc != null) { + taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda -> + tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null); + if (DEBUG) appendLog("display-area-feature-from-option=" + taskDisplayArea); + } + } } // If task display area is not specified in options - try display id diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 168c250a8c930..159d482830854 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -195,6 +195,28 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { mResult.mPreferredTaskDisplayArea); } + @Test + public void testUsesOptionsDisplayAreaFeatureIdIfSet() { + final TestDisplayContent freeformDisplay = createNewDisplayContent( + WINDOWING_MODE_FREEFORM); + final TestDisplayContent fullscreenDisplay = createNewDisplayContent( + WINDOWING_MODE_FULLSCREEN); + + mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea(); + ActivityRecord source = createSourceActivity(freeformDisplay); + + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(fullscreenDisplay.mDisplayId); + options.setLaunchTaskDisplayAreaFeatureId( + fullscreenDisplay.getDefaultTaskDisplayArea().mFeatureId); + + assertEquals(RESULT_CONTINUE, + new CalculateRequestBuilder().setSource(source).setOptions(options).calculate()); + + assertEquals(fullscreenDisplay.getDefaultTaskDisplayArea(), + mResult.mPreferredTaskDisplayArea); + } + @Test public void testUsesSourcesDisplayAreaIdPriorToTaskIfSet() { final TestDisplayContent freeformDisplay = createNewDisplayContent( @@ -453,7 +475,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { } @Test - public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayArea() { + public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayAreaToken() { final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay, mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, @@ -474,6 +496,52 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { mDefaultDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea); } + @Test + public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayAreaFeatureId() { + final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay, + mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); + final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); + launchRoot.mCreatedByOrganizer = true; + + secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN }, + new int[] { ACTIVITY_TYPE_STANDARD }); + + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchTaskDisplayAreaFeatureId( + mDefaultDisplay.getDefaultTaskDisplayArea().mFeatureId); + + assertEquals(RESULT_CONTINUE, + new CalculateRequestBuilder().setOptions(options).calculate()); + + assertEquals( + mDefaultDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testUsesOptionsDisplayAreaFeatureIdDisplayIdNotSet() { + final TestDisplayContent secondaryDisplay = createNewDisplayContent( + WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea tdaOnSecondaryDisplay = createTaskDisplayArea(secondaryDisplay, + mWm, "TestTaskDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); + + final TaskDisplayArea tdaOnDefaultDisplay = createTaskDisplayArea(mDefaultDisplay, + mWm, "TestTaskDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST); + + mCurrent.mPreferredTaskDisplayArea = tdaOnSecondaryDisplay; + ActivityRecord source = createSourceActivity(tdaOnSecondaryDisplay, + WINDOWING_MODE_FULLSCREEN); + + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchTaskDisplayAreaFeatureId(tdaOnSecondaryDisplay.mFeatureId); + + assertEquals(RESULT_CONTINUE, + new CalculateRequestBuilder().setSource(source).setOptions(options).calculate()); + // Display id wasn't specified in ActivityOptions - the activity should be placed on the + // default display, into the TaskDisplayArea with the same feature id. + assertEquals(tdaOnDefaultDisplay, mResult.mPreferredTaskDisplayArea); + } + @Test public void testRecalculateFreeformInitialBoundsWithOverrideDisplayArea() { final TestDisplayContent freeformDisplay = createNewDisplayContent( @@ -1822,6 +1890,13 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { return new ActivityBuilder(mAtm).setTask(rootTask).build(); } + private ActivityRecord createSourceActivity(TaskDisplayArea taskDisplayArea, + int windowingMode) { + final Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD, + true); + return new ActivityBuilder(mAtm).setTask(rootTask).build(); + } + private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) { final Task rootTask = display.getDefaultTaskDisplayArea() .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);