diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 7cea4587bec57..d7c9bc78e72f5 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2453,46 +2453,43 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mService.getTaskChangeNotificationController() .notifyActivityLaunchOnSecondaryDisplayFailed(task.getTaskInfo(), preferredDisplayId); - return; - } else if (!forceNonResizable && handleForcedResizableTask(task, - FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) { - return; + } else if (!forceNonResizable) { + handleForcedResizableTaskIfNeeded(task, FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY); } + // The information about not support secondary display should already be notified, we + // don't want to show another message on default display about split-screen. And it may + // be the case that a resizable activity is launched on a non-resizable task. + return; } if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) { - // Display a warning toast that we tried to put an app that doesn't support split-screen - // in split-screen. - mService.getTaskChangeNotificationController().notifyActivityDismissingDockedStack(); - // Dismiss docked stack. If task appeared to be in docked stack but is not resizable - // we need to move it to top of fullscreen stack, otherwise it will be covered. final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenPrimaryStack(); if (dockedStack != null) { + // Display a warning toast that we tried to put an app that doesn't support + // split-screen in split-screen. + mService.getTaskChangeNotificationController() + .notifyActivityDismissingDockedStack(); moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack); } return; } - handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN); + handleForcedResizableTaskIfNeeded(task, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN); } - /** - * @return {@code true} if the top activity of the task is forced to be resizable and the user - * was notified about activity being forced resized. - */ - private boolean handleForcedResizableTask(TaskRecord task, int reason) { + /** Notifies that the top activity of the task is forced to be resizeable. */ + private void handleForcedResizableTaskIfNeeded(TaskRecord task, int reason) { final ActivityRecord topActivity = task.getTopActivity(); - if (topActivity != null && topActivity.isNonResizableOrForcedResizable() - && !topActivity.noDisplay) { - final String packageName = topActivity.appInfo.packageName; - mService.getTaskChangeNotificationController().notifyActivityForcedResizable( - task.taskId, reason, packageName); - return true; + if (topActivity == null || topActivity.noDisplay + || !topActivity.isNonResizableOrForcedResizable()) { + return; } - return false; + mService.getTaskChangeNotificationController().notifyActivityForcedResizable( + task.taskId, reason, topActivity.appInfo.packageName); } void activityRelaunchedLocked(IBinder token) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index 59e71c4174391..9583b8acb14ac 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -18,32 +18,26 @@ package com.android.server.wm; import static android.app.ActivityManager.START_DELIVERED_TO_TOP; import static android.app.ActivityManager.START_TASK_TO_FRONT; +import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import android.app.WaitResult; +import android.content.pm.ActivityInfo; import android.platform.test.annotations.Presubmit; import androidx.test.filters.MediumTest; @@ -112,4 +106,44 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent); } } + + /** + * Ensures that {@link TaskChangeNotificationController} notifies only when an activity is + * forced to resize on secondary display. + */ + @Test + public void testHandleNonResizableTaskOnSecondaryDisplay() { + // Create an unresizable task on secondary display. + final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); + final ActivityStack stack = new StackBuilder(mRootActivityContainer) + .setDisplay(newDisplay).build(); + final ActivityRecord unresizableActivity = stack.getTopActivity(); + final TaskRecord task = unresizableActivity.getTaskRecord(); + unresizableActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + task.setResizeMode(unresizableActivity.info.resizeMode); + + final TaskChangeNotificationController taskChangeNotifier = + mService.getTaskChangeNotificationController(); + spyOn(taskChangeNotifier); + + mSupervisor.handleNonResizableTaskIfNeeded(task, newDisplay.getWindowingMode(), + newDisplay.mDisplayId, stack); + // The top activity is unresizable, so it should notify the activity is forced resizing. + verify(taskChangeNotifier).notifyActivityForcedResizable(eq(task.taskId), + eq(FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY), + eq(unresizableActivity.packageName)); + reset(taskChangeNotifier); + + // Put a resizable activity on top of the unresizable task. + final ActivityRecord resizableActivity = new ActivityBuilder(mService) + .setTask(task).build(); + resizableActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + + mSupervisor.handleNonResizableTaskIfNeeded(task, newDisplay.getWindowingMode(), + newDisplay.mDisplayId, stack); + // For the resizable activity, it is no need to force resizing or dismiss the docked stack. + verify(taskChangeNotifier, never()).notifyActivityForcedResizable(anyInt() /* taskId */, + anyInt() /* reason */, anyString() /* packageName */); + verify(taskChangeNotifier, never()).notifyActivityDismissingDockedStack(); + } }