diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java index d3cc918c2a29b..a5aefd5157ce6 100644 --- a/core/java/android/window/DisplayWindowPolicyController.java +++ b/core/java/android/window/DisplayWindowPolicyController.java @@ -125,6 +125,11 @@ public abstract class DisplayWindowPolicyController { public abstract boolean keepActivityOnWindowFlagsChanged( ActivityInfo activityInfo, int windowFlags, int systemWindowFlags); + /** + * Returns {@code true} if the tasks which is on this virtual display can be showed on Recents. + */ + public abstract boolean canShowTasksInRecents(); + /** * This is called when the top activity of the display is changed. */ diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 0b437446826a0..4473a095063ab 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -16,6 +16,8 @@ package com.android.server.companion.virtual; +import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; +import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; @@ -24,6 +26,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.app.compat.CompatChanges; +import android.companion.AssociationRequest; import android.companion.virtual.VirtualDeviceManager.ActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.VirtualDeviceParams.ActivityPolicy; @@ -95,6 +98,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController private final Handler mHandler = new Handler(Looper.getMainLooper()); private final ArraySet mRunningAppsChangedListener = new ArraySet<>(); + private final @AssociationRequest.DeviceProfile String mDeviceProfile; /** * Creates a window policy controller that is generic to the different use cases of virtual @@ -119,6 +123,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController * is not populated in this callback and is always {@link Display#INVALID_DISPLAY}. * @param activityBlockedCallback Callback that is called when an activity is blocked from * launching. + * @param deviceProfile The {@link AssociationRequest.DeviceProfile} of this virtual device. */ public GenericWindowPolicyController(int windowFlags, int systemWindowFlags, @NonNull ArraySet allowedUsers, @@ -128,7 +133,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController @NonNull Set blockedActivities, @ActivityPolicy int defaultActivityPolicy, @NonNull ActivityListener activityListener, - @NonNull Consumer activityBlockedCallback) { + @NonNull Consumer activityBlockedCallback, + @AssociationRequest.DeviceProfile String deviceProfile) { super(); mAllowedUsers = allowedUsers; mAllowedCrossTaskNavigations = new ArraySet<>(allowedCrossTaskNavigations); @@ -139,6 +145,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mActivityBlockedCallback = activityBlockedCallback; setInterestedWindowFlags(windowFlags, systemWindowFlags); mActivityListener = activityListener; + mDeviceProfile = deviceProfile; } /** Register a listener for running applications changes. */ @@ -248,6 +255,18 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController }); } + @Override + public boolean canShowTasksInRecents() { + // TODO(b/234075973) : Remove this once proper API is ready. + switch (mDeviceProfile) { + case DEVICE_PROFILE_AUTOMOTIVE_PROJECTION: + return false; + case DEVICE_PROFILE_APP_STREAMING: + default: + return true; + } + } + /** * Returns true if an app with the given UID has an activity running on the virtual display for * this controller. diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 638b3aeef544b..33fd83bf6bd7a 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -557,7 +557,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mParams.getBlockedActivities(), mParams.getDefaultActivityPolicy(), createListenerAdapter(displayId), - activityInfo -> onActivityBlocked(displayId, activityInfo)); + activityInfo -> onActivityBlocked(displayId, activityInfo), + mAssociationInfo.getDeviceProfile()); gwpc.registerRunningAppsChangedListener(/* listener= */ this); mWindowPolicyControllers.put(displayId, gwpc); return gwpc; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 94f4382eb1b76..a059ac613dded 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -225,6 +225,7 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManagerPolicyConstants.PointerEventListener; +import android.window.DisplayWindowPolicyController; import android.window.IDisplayAreaOrganizer; import android.window.TransitionRequestInfo; @@ -1959,6 +1960,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */); } + /** + * @see DisplayWindowPolicyController#canShowTasksInRecents() + */ + boolean canShowTasksInRecents() { + if (mDwpcHelper == null) { + return true; + } + return mDwpcHelper.canShowTasksInRecents(); + } + /** * Applies the rotation transaction. This must be called after {@link #updateRotationUnchecked} * (if it returned {@code true}) to actually finish the rotation. diff --git a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java index 2deb8286f95f7..5d4904264056a 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java +++ b/services/core/java/com/android/server/wm/DisplayWindowPolicyControllerHelper.java @@ -152,6 +152,16 @@ class DisplayWindowPolicyControllerHelper { return mDisplayWindowPolicyController.isWindowingModeSupported(windowingMode); } + /** + * @see DisplayWindowPolicyController#canShowTasksInRecents() + */ + public final boolean canShowTasksInRecents() { + if (mDisplayWindowPolicyController == null) { + return true; + } + return mDisplayWindowPolicyController.canShowTasksInRecents(); + } + void dump(String prefix, PrintWriter pw) { if (mDisplayWindowPolicyController != null) { pw.println(); diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index e259238c2879c..4860762a5f7f7 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -1390,6 +1390,13 @@ class RecentTasks { return false; } + // Ignore the task if it is started on a display which is not allow to show its tasks on + // Recents. + if (task.getDisplayContent() != null + && !task.getDisplayContent().canShowTasksInRecents()) { + return false; + } + return true; } @@ -1399,13 +1406,19 @@ class RecentTasks { private boolean isInVisibleRange(Task task, int taskIndex, int numVisibleTasks, boolean skipExcludedCheck) { if (!skipExcludedCheck) { - // Keep the most recent task even if it is excluded from recents + // Keep the most recent task of home display even if it is excluded from recents. final boolean isExcludeFromRecents = (task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; if (isExcludeFromRecents) { - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\texcludeFromRecents=true"); - return taskIndex == 0; + if (DEBUG_RECENTS_TRIM_TASKS) { + Slog.d(TAG, + "\texcludeFromRecents=true, taskIndex = " + taskIndex + + ", isOnHomeDisplay: " + task.isOnHomeDisplay()); + } + // The Recents is only supported on default display now, we should only keep the + // most recent task of home display. + return (task.isOnHomeDisplay() && taskIndex == 0); } } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java index cd836c77076db..e2c3a94fc187d 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java @@ -16,6 +16,7 @@ package com.android.server.companion.virtual.audio; +import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; import static android.media.AudioAttributes.FLAG_SECURE; import static android.media.AudioPlaybackConfiguration.PLAYER_STATE_STARTED; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; @@ -81,7 +82,8 @@ public class VirtualAudioControllerTest { /* blockedActivities= */ new ArraySet<>(), VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED, /* activityListener= */ null, - /* activityBlockedCallback= */ null); + /* activityBlockedCallback= */ null, + /* deviceProfile= */ DEVICE_PROFILE_APP_STREAMING); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java index 02009b75e319d..47c217651e786 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java @@ -242,5 +242,10 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase { mRunningUids.clear(); mRunningUids.addAll(runningUids); } + + @Override + public boolean canShowTasksInRecents() { + return true; + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 284728397c9fe..feb1c6fa6b5c4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -690,6 +690,20 @@ public class RecentTasksTest extends WindowTestsBase { assertGetRecentTasksOrder(0 /* flags */, excludedTask1); } + @Test + public void testVisibleTasks_excludedFromRecents_nonDefaultDisplayTaskNotVisible() { + Task excludedTaskOnVirtualDisplay = createTaskBuilder(".excludedTaskOnVirtualDisplay") + .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .build(); + excludedTaskOnVirtualDisplay.mUserSetupComplete = true; + doReturn(false).when(excludedTaskOnVirtualDisplay).isOnHomeDisplay(); + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.add(excludedTaskOnVirtualDisplay); + + // Expect that the first visible excluded-from-recents task is visible + assertGetRecentTasksOrder(0 /* flags */, mTasks.get(0)); + } + @Test public void testVisibleTasks_excludedFromRecents_withExcluded() { // Create some set of tasks, some of which are visible and some are not @@ -801,6 +815,17 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.isVisibleRecentTask(task)); } + @Test + public void testVisibleTask_displayCanNotShowTaskFromRecents_expectNotVisible() { + final DisplayContent displayContent = addNewDisplayContentAt(DisplayContent.POSITION_TOP); + doReturn(false).when(displayContent).canShowTasksInRecents(); + final Task task = displayContent.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + mRecentTasks.add(task); + + assertFalse(mRecentTasks.isVisibleRecentTask(task)); + } + @Test public void testFreezeTaskListOrder_reorderExistingTask() { // Add some tasks