diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c2016decdbe93..913c3e580adfa 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7584,6 +7584,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET; } + String getProcessName() { + return info.applicationInfo.processName; + } + int getUid() { return info.applicationInfo.uid; } @@ -7596,6 +7600,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return app != null ? app.getPid() : 0; } + int getLaunchedFromPid() { + return launchedFromPid; + } + + int getLaunchedFromUid() { + return launchedFromUid; + } + /** * Determines whether this ActivityRecord can turn the screen on. It checks whether the flag * {@link ActivityRecord#getTurnScreenOnFlag} is set and checks whether the ActivityRecord diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index acee7b26725e7..90cd682d6a8f5 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -57,9 +57,6 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; -import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; -import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -71,6 +68,9 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; +import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.Task.ActivityState.RESUMED; @@ -1870,7 +1870,7 @@ class ActivityStarter { } mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r, - sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams); + sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams, mRequest); mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea() ? mLaunchParams.mPreferredTaskDisplayArea : mRootWindowContainer.getDefaultTaskDisplayArea(); @@ -2254,7 +2254,7 @@ class ActivityStarter { // Preferred display id is the only state we need for now and it could be updated again // after we located a reusable task (which might be resided in another display). mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r, - sourceRecord, options, PHASE_DISPLAY, mLaunchParams); + sourceRecord, options, PHASE_DISPLAY, mLaunchParams, mRequest); mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea() ? mLaunchParams.mPreferredTaskDisplayArea : mRootWindowContainer.getDefaultTaskDisplayArea(); diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java index f1ae921c87f58..b6b172eeae5b1 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsController.java +++ b/services/core/java/com/android/server/wm/LaunchParamsController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; @@ -73,9 +74,10 @@ class LaunchParamsController { * @param source The {@link ActivityRecord} from which activity was started from. * @param options The {@link ActivityOptions} specified for the activity. * @param result The resulting params. + * @param request The optional request from the activity starter. */ - void calculate(Task task, WindowLayout layout, ActivityRecord activity, - ActivityRecord source, ActivityOptions options, int phase, LaunchParams result) { + void calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, + ActivityOptions options, int phase, LaunchParams result, @Nullable Request request) { result.reset(); if (task != null || activity != null) { @@ -91,7 +93,7 @@ class LaunchParamsController { final LaunchParamsModifier modifier = mModifiers.get(i); switch(modifier.onCalculate(task, layout, activity, source, options, phase, mTmpCurrent, - mTmpResult)) { + mTmpResult, request)) { case RESULT_SKIP: // Do not apply any results when we are told to skip continue; @@ -128,7 +130,8 @@ class LaunchParamsController { boolean layoutTask(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options) { - calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams); + calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams, + null /* request */); // No changes, return. if (mTmpParams.isEmpty()) { @@ -305,15 +308,17 @@ class LaunchParamsController { * launched should have this be non-null. * @param source the Activity that launched a new task. Could be {@code null}. * @param options {@link ActivityOptions} used to start the activity with. - * @param phase the calculation phase, see {@link LaunchParamsModifier.Phase} + * @param phase the calculation phase, see {@link Phase} * @param currentParams launching params after the process of last {@link * LaunchParamsModifier}. * @param outParams the result params to be set. + * @param request Optional data to give more context on the launch * @return see {@link LaunchParamsModifier.Result} */ @Result - int onCalculate(Task task, WindowLayout layout, ActivityRecord activity, + int onCalculate(@Nullable Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, @Phase int phase, - LaunchParams currentParams, LaunchParams outParams); + LaunchParams currentParams, LaunchParams outParams, + @Nullable Request request); } } diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 6dc957c1d1186..8b2fa52afd221 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.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -99,23 +100,25 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { ActivityRecord source, ActivityOptions options, LaunchParams currentParams, LaunchParams outParams) { return onCalculate(task, layout, activity, source, options, PHASE_BOUNDS, currentParams, - outParams); + outParams, null); } @Override - public int onCalculate(Task task, ActivityInfo.WindowLayout layout, - ActivityRecord activity, ActivityRecord source, ActivityOptions options, - int phase, LaunchParams currentParams, LaunchParams outParams) { + public int onCalculate(@Nullable Task task, @NonNull ActivityInfo.WindowLayout layout, + @NonNull ActivityRecord activity, @Nullable ActivityRecord source, + ActivityOptions options, int phase, LaunchParams currentParams, LaunchParams outParams, + @Nullable Request request) { initLogBuilder(task, activity); final int result = calculate(task, layout, activity, source, options, phase, currentParams, - outParams); + outParams, request); outputLog(); return result; } - private int calculate(Task task, ActivityInfo.WindowLayout layout, - ActivityRecord activity, ActivityRecord source, ActivityOptions options, int phase, - LaunchParams currentParams, LaunchParams outParams) { + private int calculate(@Nullable Task task, @NonNull ActivityInfo.WindowLayout layout, + @NonNull ActivityRecord activity, @Nullable ActivityRecord source, + ActivityOptions options, int phase, LaunchParams currentParams, LaunchParams outParams, + @Nullable Request request) { final ActivityRecord root; if (task != null) { root = task.getRootActivity() == null ? activity : task.getRootActivity(); @@ -138,7 +141,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // STEP 1: Determine the display area to launch the activity/task. final TaskDisplayArea taskDisplayArea = getPreferredLaunchTaskDisplayArea(task, - options, source, currentParams); + options, source, currentParams, activity, request); outParams.mPreferredTaskDisplayArea = taskDisplayArea; // TODO(b/152116619): Update the usages of display to use taskDisplayArea below. final DisplayContent display = taskDisplayArea.mDisplayContent; @@ -298,7 +301,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } private TaskDisplayArea getPreferredLaunchTaskDisplayArea(@Nullable Task task, - @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams) { + @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams, + @NonNull ActivityRecord activityRecord, @Nullable Request request) { TaskDisplayArea taskDisplayArea = null; final WindowContainerToken optionLaunchTaskDisplayAreaToken = options != null @@ -369,7 +373,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { taskDisplayArea = currentParams.mPreferredTaskDisplayArea; } - // Fallback to default display if the device didn't declare support for multi-display + // Re-route to default display if the device didn't declare support for multi-display if (taskDisplayArea != null && !mSupervisor.mService.mSupportsMultiDisplay && taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY) { taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); @@ -377,7 +381,53 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { return (taskDisplayArea != null) ? taskDisplayArea - : mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); + : getFallbackDisplayAreaForActivity(activityRecord, request); + } + + /** + * Calculates the default {@link TaskDisplayArea} for a task. We attempt to put the activity + * within the same display area if possible. The strategy is to find the display in the + * following order: + * + *
    + *
  1. The display area of the top activity from the launching process will be used
  2. + *
  3. The display area of the top activity from the real launching process will be used + *
  4. + *
  5. Default display area from the associated root window container.
  6. + *
+ * @param activityRecord the activity being started + * @param request optional {@link Request} made to start the activity record + * @return {@link TaskDisplayArea} to house the task + */ + private TaskDisplayArea getFallbackDisplayAreaForActivity( + @NonNull ActivityRecord activityRecord, @Nullable Request request) { + + WindowProcessController controllerFromLaunchingRecord = mSupervisor.mService + .getProcessController(activityRecord.launchedFromPid, + activityRecord.launchedFromUid); + final TaskDisplayArea displayAreaForLaunchingRecord = controllerFromLaunchingRecord == null + ? null : controllerFromLaunchingRecord.getTopActivityDisplayArea(); + if (displayAreaForLaunchingRecord != null) { + return displayAreaForLaunchingRecord; + } + + WindowProcessController controllerFromProcess = mSupervisor.mService.getProcessController( + activityRecord.getProcessName(), activityRecord.getUid()); + final TaskDisplayArea displayAreaForRecord = controllerFromProcess == null ? null + : controllerFromProcess.getTopActivityDisplayArea(); + if (displayAreaForRecord != null) { + return displayAreaForRecord; + } + + WindowProcessController controllerFromRequest = request == null ? null : mSupervisor + .mService.getProcessController(request.realCallingPid, request.realCallingUid); + final TaskDisplayArea displayAreaFromSourceProcess = controllerFromRequest == null ? null + : controllerFromRequest.getTopActivityDisplayArea(); + if (displayAreaFromSourceProcess != null) { + return displayAreaFromSourceProcess; + } + + return mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); } private boolean canInheritWindowingModeFromSource(@NonNull DisplayContent display, diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index c55f059cae160..8f8fea34e620f 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -730,6 +730,28 @@ public class WindowProcessController extends ConfigurationContainer= 0; --index) { + ActivityRecord nextRecord = mActivities.get(index); + TaskDisplayArea nextDisplayArea = nextRecord.getDisplayArea(); + if (nextRecord.compareTo(topRecord) > 0 && nextDisplayArea != null) { + topRecord = nextRecord; + displayArea = nextDisplayArea; + } + } + + return displayArea; + } + private boolean hasActivityInVisibleTask() { for (int i = mActivities.size() - 1; i >= 0; --i) { Task task = mActivities.get(i).getTask(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index bded3f9de9bad..565bf8b615c78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -422,7 +422,7 @@ public class ActivityStarterTests extends WindowTestsBase { // verify that values are passed to the modifier. Values are passed thrice -- two for // setting initial state, another when task is created. verify(modifier, times(3)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options), - anyInt(), any(), any()); + anyInt(), any(), any(), any()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index e514ac04efbeb..cd428e10a4378 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -33,6 +33,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; @@ -92,11 +93,12 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final ActivityRecord source = new ActivityBuilder(mAtm).build(); final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0); final ActivityOptions options = mock(ActivityOptions.class); + final Request request = new Request(); mController.calculate(record.getTask(), layout, record, source, options, PHASE_BOUNDS, - new LaunchParams()); + new LaunchParams(), request); verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record), - eq(source), eq(options), anyInt(), any(), any()); + eq(source), eq(options), anyInt(), any(), any(), eq(request)); } /** @@ -119,9 +121,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mPersister.putLaunchParams(userId, name, expected); mController.calculate(activity.getTask(), null /*layout*/, activity, null /*source*/, - null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*options*/, PHASE_BOUNDS, new LaunchParams(), null /* request */); verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - eq(expected), any()); + eq(expected), any(), any()); } /** @@ -132,16 +134,17 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final LaunchParamsModifier ignoredPositioner = mock(LaunchParamsModifier.class); final LaunchParamsModifier earlyExitPositioner = - (task, layout, activity, source, options, phase, currentParams, outParams) + (task, layout, activity, source, options, phase, currentParams, outParams, request) -> RESULT_DONE; mController.registerModifier(ignoredPositioner); mController.registerModifier(earlyExitPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams(), + null /* request */); verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); } /** @@ -157,20 +160,22 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mController.registerModifier(firstPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams(), + null /* request */); verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner); mController.registerModifier(secondPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams(), + null /* request */); verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); } /** @@ -192,10 +197,10 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mController.registerModifier(positioner2); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*options*/, PHASE_BOUNDS, new LaunchParams(), null /* request */); verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - eq(positioner2.getLaunchParams()), any()); + eq(positioner2.getLaunchParams()), any(), any()); } /** @@ -218,7 +223,7 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final LaunchParams result = new LaunchParams(); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, PHASE_BOUNDS, result); + null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(result, positioner2.getLaunchParams()); } @@ -237,17 +242,17 @@ public class LaunchParamsControllerTests extends WindowTestsBase { // VR activities should always land on default display. mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, result); + null /*source*/, null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(mRootWindowContainer.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea); // Otherwise, always lands on VR 2D display. final ActivityRecord vr2dActivity = new ActivityBuilder(mAtm).build(); mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, result); + null /*source*/, null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(vrDisplay.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, PHASE_BOUNDS, result); + null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(vrDisplay.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea); mAtm.mVr2dDisplayId = INVALID_DISPLAY; @@ -269,9 +274,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final ActivityOptions options = mock(ActivityOptions.class); mController.calculate(record.getTask(), layout, record, source, options, PHASE_BOUNDS, - new LaunchParams()); + new LaunchParams(), null /* request */); verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record), - eq(source), eq(options), eq(PHASE_BOUNDS), any(), any()); + eq(source), eq(options), eq(PHASE_BOUNDS), any(), any(), any()); } /** @@ -403,8 +408,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase { @Override public int onCalculate(Task task, WindowLayout layout, ActivityRecord activity, - ActivityRecord source, ActivityOptions options, int phase, - LaunchParams currentParams, LaunchParams outParams) { + ActivityRecord source, ActivityOptions options, int phase, + LaunchParams currentParams, LaunchParams outParams, + Request request) { outParams.set(mParams); return mReturnVal; } 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 98520bb7a20bf..4f55322e2085a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -35,6 +35,7 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; @@ -42,6 +43,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.app.ActivityOptions; import android.content.pm.ActivityInfo; @@ -265,6 +269,180 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { mResult.mPreferredTaskDisplayArea); } + @Test + public void testUsesDisplayAreaFromTopMostActivityInApplicationIfAvailable() { + final String processName = "processName"; + final int uid = 124214; + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea expectedDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final WindowProcessController controller = mock(WindowProcessController.class); + + when(controller.getTopActivityDisplayArea()).thenReturn(expectedDisplayArea); + + when(mActivity.getProcessName()).thenReturn(processName); + when(mActivity.getUid()).thenReturn(uid); + doReturn(controller) + .when(mSupervisor.mService) + .getProcessController(processName, uid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(expectedDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testUsesDisplayAreaFromLaunchingActivityIfApplicationLaunching() { + final String processName = "processName"; + final int uid = 124214; + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea expectedTaskDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final WindowProcessController controller = mock(WindowProcessController.class); + + when(controller.getTopActivityDisplayArea()).thenReturn(expectedTaskDisplayArea); + + when(mActivity.getProcessName()).thenReturn(processName); + when(mActivity.getUid()).thenReturn(uid); + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(processName, uid); + + doReturn(controller) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(expectedTaskDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testDisplayAreaFromLaunchingActivityTakesPrecedence() { + final String processName = "processName"; + final int uid = 124214; + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea firstTaskDisplayArea = firstScreen.getDefaultTaskDisplayArea(); + final TaskDisplayArea expectedTaskDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final WindowProcessController controllerForLaunching = mock(WindowProcessController.class); + final WindowProcessController controllerForApplication = + mock(WindowProcessController.class); + + when(mActivity.getProcessName()).thenReturn(processName); + when(mActivity.getUid()).thenReturn(uid); + + when(controllerForApplication.getTopActivityDisplayArea()).thenReturn(firstTaskDisplayArea); + when(controllerForLaunching.getTopActivityDisplayArea()) + .thenReturn(expectedTaskDisplayArea); + + doReturn(controllerForApplication) + .when(mSupervisor.mService) + .getProcessController(processName, uid); + doReturn(controllerForLaunching) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(expectedTaskDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testUsesDisplayAreaOriginalProcessAsLastResort() { + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea expectedTaskDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final Request request = new Request(); + request.realCallingPid = 12412413; + request.realCallingUid = 235424; + + final WindowProcessController controller = mock(WindowProcessController.class); + + when(controller.getTopActivityDisplayArea()).thenReturn(expectedTaskDisplayArea); + + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.processName, mActivity.info.applicationInfo.uid); + + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + doReturn(controller) + .when(mSupervisor.mService) + .getProcessController(request.realCallingPid, request.realCallingUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + request + )); + + assertEquals(expectedTaskDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testUsesDefaultDisplayAreaIfWindowProcessControllerIsNotPresent() { + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.processName, mActivity.info.applicationInfo.uid); + + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(DEFAULT_DISPLAY, mResult.mPreferredTaskDisplayArea.getDisplayId()); + } + // ===================================== // Launch Windowing Mode Related Tests // ===================================== diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 5afcedb10dd65..3057558a6e984 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -27,10 +27,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; import android.Manifest; import android.app.IApplicationThread; @@ -327,6 +330,45 @@ public class WindowProcessControllerTests extends WindowTestsBase { return new ActivityBuilder(mAtm).setCreateTask(true).setUseProcess(wpc).build(); } + @Test + public void testTopActivityDisplayAreaMatchesTopMostActivity_noActivities() { + assertNull(mWpc.getTopActivityDisplayArea()); + } + + @Test + public void testTopActivityDisplayAreaMatchesTopMostActivity_singleActivity() { + final ActivityRecord activityRecord = new ActivityBuilder(mSupervisor.mService).build(); + final TaskDisplayArea expectedDisplayArea = mock(TaskDisplayArea.class); + + when(activityRecord.getDisplayArea()) + .thenReturn(expectedDisplayArea); + + mWpc.addActivityIfNeeded(activityRecord); + + assertEquals(expectedDisplayArea, mWpc.getTopActivityDisplayArea()); + } + + /** + * Test that top most activity respects z-order. + */ + @Test + public void testTopActivityDisplayAreaMatchesTopMostActivity_multipleActivities() { + final ActivityRecord bottomRecord = new ActivityBuilder(mSupervisor.mService).build(); + final TaskDisplayArea bottomDisplayArea = mock(TaskDisplayArea.class); + final ActivityRecord topRecord = new ActivityBuilder(mSupervisor.mService).build(); + final TaskDisplayArea topDisplayArea = mock(TaskDisplayArea.class); + + when(bottomRecord.getDisplayArea()).thenReturn(bottomDisplayArea); + when(topRecord.getDisplayArea()).thenReturn(topDisplayArea); + doReturn(-1).when(bottomRecord).compareTo(topRecord); + doReturn(1).when(topRecord).compareTo(bottomRecord); + + mWpc.addActivityIfNeeded(topRecord); + mWpc.addActivityIfNeeded(bottomRecord); + + assertEquals(topDisplayArea, mWpc.getTopActivityDisplayArea()); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mAtm, 1000, 1500).build(); }