diff --git a/core/api/test-current.txt b/core/api/test-current.txt index c03461aa77422..1422561e33470 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2512,7 +2512,6 @@ package android.window { method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List registerOrganizer(); method @BinderThread public void removeStartingWindow(int); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken); method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer(); } @@ -2527,6 +2526,7 @@ package android.window { method public int describeContents(); method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean); + method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean); method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int); method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); @@ -2534,6 +2534,7 @@ package android.window { method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction); method @NonNull public android.window.WindowContainerTransaction setFocusable(@NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction setHidden(@NonNull android.window.WindowContainerToken, boolean); + method @NonNull public android.window.WindowContainerTransaction setLaunchRoot(@NonNull android.window.WindowContainerToken, @Nullable int[], @Nullable int[]); method @NonNull public android.window.WindowContainerTransaction setScreenSizeDp(@NonNull android.window.WindowContainerToken, int, int); method @NonNull public android.window.WindowContainerTransaction setSmallestScreenWidthDp(@NonNull android.window.WindowContainerToken, int); method @NonNull public android.window.WindowContainerTransaction setWindowingMode(@NonNull android.window.WindowContainerToken, int); diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 4a43a438b69d6..2d0211e129bfd 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -55,12 +55,6 @@ interface ITaskOrganizerController { /** Get the root task which contains the current ime target */ WindowContainerToken getImeTarget(int display); - /** - * Set's the root task to launch new tasks into on a display. {@code null} means no launch root - * and thus new tasks just end up directly on the display. - */ - void setLaunchRoot(int displayId, in WindowContainerToken root); - /** * Requests that the given task organizer is notified when back is pressed on the root activity * of one of its controlled tasks. diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 73b2fe1ff4d52..f29eb39045aed 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -183,19 +183,6 @@ public class TaskOrganizer extends WindowOrganizer { } } - /** - * Set's the root task to launch new tasks into on a display. {@code null} means no launch - * root and thus new tasks just end up directly on the display. - */ - @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) - public void setLaunchRoot(int displayId, @NonNull WindowContainerToken root) { - try { - mTaskOrganizerController.setLaunchRoot(displayId, root); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** * Requests that the given task organizer is notified when back is pressed on the root activity * of one of its controlled tasks. diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index eba4fd21166d2..6bc31107bddfe 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -30,6 +30,7 @@ import android.util.ArrayMap; import android.view.SurfaceControl; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -263,8 +264,9 @@ public final class WindowContainerTransaction implements Parcelable { @NonNull public WindowContainerTransaction reparent(@NonNull WindowContainerToken child, @Nullable WindowContainerToken parent, boolean onTop) { - mHierarchyOps.add(new HierarchyOp(child.asBinder(), - parent == null ? null : parent.asBinder(), onTop)); + mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(), + parent == null ? null : parent.asBinder(), + onTop)); return this; } @@ -276,7 +278,47 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) { - mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop)); + mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop)); + return this; + } + + /** + * Reparent's all children tasks of {@param currentParent} in the specified + * {@param windowingMode} and {@param activityType} to {@param newParent} in their current + * z-order. + * + * @param currentParent of the tasks to perform the operation no. + * {@code null} will perform the operation on the display. + * @param newParent for the tasks. {@code null} will perform the operation on the display. + * @param windowingModes of the tasks to reparent. + * @param activityTypes of the tasks to reparent. + * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to + * the bottom. + */ + @NonNull + public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent, + @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, + @Nullable int[] activityTypes, boolean onTop) { + mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent( + currentParent != null ? currentParent.asBinder() : null, + newParent != null ? newParent.asBinder() : null, + windowingModes, + activityTypes, + onTop)); + return this; + } + + /** + * Sets whether a container should be the launch root for the specified windowing mode and + * activity type. This currently only applies to Task containers created by organizer. + */ + @NonNull + public WindowContainerTransaction setLaunchRoot(@NonNull WindowContainerToken container, + @Nullable int[] windowingModes, @Nullable int[] activityTypes) { + mHierarchyOps.add(HierarchyOp.createForSetLaunchRoot( + container.asBinder(), + windowingModes, + activityTypes)); return this; } @@ -363,6 +405,7 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mFocusable = true; private boolean mHidden = false; private boolean mIgnoreOrientationRequest = false; + private int mChangeMask = 0; private @ActivityInfo.Config int mConfigSetMask = 0; private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; @@ -595,6 +638,14 @@ public final class WindowContainerTransaction implements Parcelable { * @hide */ public static class HierarchyOp implements Parcelable { + public static final int HIERARCHY_OP_TYPE_REPARENT = 0; + public static final int HIERARCHY_OP_TYPE_REORDER = 1; + public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2; + public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3; + + private final int mType; + + // Container we are performing the operation on. private final IBinder mContainer; // If this is same as mContainer, then only change position, don't reparent. @@ -603,32 +654,68 @@ public final class WindowContainerTransaction implements Parcelable { // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. private final boolean mToTop; - public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { - mContainer = container; - mReparent = reparent; - mToTop = toTop; + final private int[] mWindowingModes; + final private int[] mActivityTypes; + + public static HierarchyOp createForReparent( + @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { + return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT, + container, reparent, null, null, toTop); } - public HierarchyOp(@NonNull IBinder container, boolean toTop) { + public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) { + return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER, + container, container, null, null, toTop); + } + + public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent, + IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) { + return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT, + currentParent, newParent, windowingModes, activityTypes, onTop); + } + + public static HierarchyOp createForSetLaunchRoot(IBinder container, + int[] windowingModes, int[] activityTypes) { + return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT, + container, null, windowingModes, activityTypes, false); + } + + private HierarchyOp(int type, @NonNull IBinder container, @Nullable IBinder reparent, + int[] windowingModes, int[] activityTypes, boolean toTop) { + mType = type; mContainer = container; - mReparent = container; + mReparent = reparent; + mWindowingModes = windowingModes != null ? + Arrays.copyOf(windowingModes, windowingModes.length) : null; + mActivityTypes = activityTypes != null ? + Arrays.copyOf(activityTypes, activityTypes.length) : null; mToTop = toTop; } public HierarchyOp(@NonNull HierarchyOp copy) { + mType = copy.mType; mContainer = copy.mContainer; mReparent = copy.mReparent; mToTop = copy.mToTop; + mWindowingModes = copy.mWindowingModes; + mActivityTypes = copy.mActivityTypes; } protected HierarchyOp(Parcel in) { + mType = in.readInt(); mContainer = in.readStrongBinder(); mReparent = in.readStrongBinder(); mToTop = in.readBoolean(); + mWindowingModes = in.createIntArray(); + mActivityTypes = in.createIntArray(); + } + + public int getType() { + return mType; } public boolean isReparent() { - return mContainer != mReparent; + return mType == HIERARCHY_OP_TYPE_REPARENT; } @Nullable @@ -645,21 +732,45 @@ public final class WindowContainerTransaction implements Parcelable { return mToTop; } + public int[] getWindowingModes() { + return mWindowingModes; + } + + public int[] getActivityTypes() { + return mActivityTypes; + } + @Override public String toString() { - if (isReparent()) { - return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") - + mReparent + "}"; - } else { - return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; + switch (mType) { + case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: + return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent + + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes + + " mActivityType=" + mActivityTypes + "}"; + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: + return "{SetLaunchRoot: container=" + mContainer + + " mWindowingMode=" + mWindowingModes + + " mActivityType=" + mActivityTypes + "}"; + case HIERARCHY_OP_TYPE_REPARENT: + return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") + + mReparent + "}"; + case HIERARCHY_OP_TYPE_REORDER: + return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; + default: + return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes + + " mActivityType=" + mActivityTypes + "}"; } } @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); dest.writeStrongBinder(mContainer); dest.writeStrongBinder(mReparent); dest.writeBoolean(mToTop); + dest.writeIntArray(mWindowingModes); + dest.writeIntArray(mActivityTypes); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java index ad05e6d3e6c48..a8cd1dd4fbde6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java @@ -18,7 +18,10 @@ package com.android.wm.shell.legacysplitscreen; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -54,6 +57,17 @@ class WindowManagerProxy { private static final String TAG = "WindowManagerProxy"; private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS}; + private static final int[] CONTROLLED_ACTIVITY_TYPES = { + ACTIVITY_TYPE_STANDARD, + ACTIVITY_TYPE_HOME, + ACTIVITY_TYPE_RECENTS, + ACTIVITY_TYPE_UNDEFINED + }; + private static final int[] CONTROLLED_WINDOWING_MODES = { + WINDOWING_MODE_FULLSCREEN, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, + WINDOWING_MODE_UNDEFINED + }; @GuardedBy("mDockedRect") private final Rect mDockedRect = new Rect(); @@ -191,8 +205,9 @@ class WindowManagerProxy { // Set launchtile first so that any stack created after // getAllRootTaskInfos and before reparent (even if unlikely) are placed // correctly. - mTaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token); WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setLaunchRoot(tiles.mSecondary.token, CONTROLLED_WINDOWING_MODES, + CONTROLLED_ACTIVITY_TYPES); final boolean isHomeResizable = buildEnterSplit(wct, tiles, layout); applySyncTransaction(wct); return isHomeResizable; @@ -251,12 +266,12 @@ class WindowManagerProxy { /** @see #buildDismissSplit */ void applyDismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, boolean dismissOrMaximize) { - // Set launch root first so that any task created after getChildContainers and - // before reparent (pretty unlikely) are put into fullscreen. - mTaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished // plus specific APIs to clean this up. final WindowContainerTransaction wct = new WindowContainerTransaction(); + // Set launch root first so that any task created after getChildContainers and + // before reparent (pretty unlikely) are put into fullscreen. + wct.setLaunchRoot(tiles.mSecondary.token, null, null); buildDismissSplit(wct, tiles, layout, dismissOrMaximize); applySyncTransaction(wct); } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 5ab1c390a92b4..854385020f039 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -43,6 +43,18 @@ fun LayersAssertion.appPairsDividerIsInvisible( } } +@JvmOverloads +fun LayersAssertion.appPairsDividerBecomesVisible( + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + all("dividerLayerBecomesVisible") { + this.hidesLayer(FlickerTestBase.DOCKED_STACK_DIVIDER) + .then() + .showsLayer(FlickerTestBase.DOCKED_STACK_DIVIDER) + } +} + @JvmOverloads fun LayersAssertion.dockedStackDividerIsVisible( bugId: Int = 0, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt index b33fa55c2be15..85bf4a1f8c255 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt @@ -21,7 +21,6 @@ import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.canSplitScreen import com.android.server.wm.flicker.helpers.exitSplitScreen import com.android.server.wm.flicker.helpers.isInSplitScreen @@ -35,6 +34,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible + import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import org.junit.Assert @@ -59,8 +59,6 @@ class EnterLegacySplitScreenTest( rotationName: String, rotation: Int ) : SplitScreenTestBase(rotationName, rotation) { - private val letterBox = "Letterbox" - private val splitScreenSetup: FlickerBuilder get() = FlickerBuilder(instrumentation).apply { val testLaunchActivity = "launch_splitScreen_test_activity" @@ -91,7 +89,6 @@ class EnterLegacySplitScreenTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName)) } } } @@ -114,7 +111,8 @@ class EnterLegacySplitScreenTest( rotation, splitScreenApp.defaultWindowName, 169271943) dockedStackDividerBecomesVisible() visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName) + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + LIVE_WALLPAPER_PACKAGE_NAME) ) } windowManagerTrace { @@ -148,7 +146,7 @@ class EnterLegacySplitScreenTest( rotation, secondaryApp.defaultWindowName, 169271943) dockedStackDividerBecomesVisible() visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName, + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName) ) } @@ -157,6 +155,7 @@ class EnterLegacySplitScreenTest( showsAppWindow(splitScreenApp.defaultWindowName) .and().showsAppWindow(secondaryApp.defaultWindowName) } + visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME)) } } } @@ -181,85 +180,14 @@ class EnterLegacySplitScreenTest( layersTrace { dockedStackDividerIsInvisible() visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, nonResizeableApp.defaultWindowName) + listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName) ) } windowManagerTrace { end { hidesAppWindow(nonResizeableApp.defaultWindowName) } - } - } - } - } - - @Test - fun testNonResizeableWhenAlreadyInSplitScreenPrimary() { - val testTag = "testNonResizeableWhenAlreadyInSplitScreenPrimary" - runWithFlicker(splitScreenSetup) { - withTestName { testTag } - repeat { - TEST_REPETITIONS - } - transitions { - nonResizeableApp.launchViaIntent() - splitScreenApp.launchViaIntent() - uiDevice.launchSplitScreen() - nonResizeableApp.reopenAppFromOverview() - } - assertions { - layersTrace { - dockedStackDividerIsInvisible() - end("appsEndingBounds", enabled = false) { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) - } - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName, letterBox) - ) - } - windowManagerTrace { - end { - showsAppWindow(nonResizeableApp.defaultWindowName) - hidesAppWindow(splitScreenApp.defaultWindowName) - } - } - } - } - } - - @Test - fun testNonResizeableWhenAlreadyInSplitScreenSecondary() { - val testTag = "testNonResizeableWhenAlreadyInSplitScreenSecondary" - runWithFlicker(splitScreenSetup) { - withTestName { testTag } - repeat { - TEST_REPETITIONS - } - transitions { - splitScreenApp.launchViaIntent() - uiDevice.launchSplitScreen() - uiDevice.pressBack() - nonResizeableApp.launchViaIntent() - } - assertions { - layersTrace { - dockedStackDividerIsInvisible() - end("appsEndingBounds", enabled = false) { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) - } - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName, letterBox) - ) - } - windowManagerTrace { - end { - showsAppWindow(nonResizeableApp.defaultWindowName) - hidesAppWindow(splitScreenApp.defaultWindowName) - } + visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt index 573ffc6c32992..9586fd139eb5e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt @@ -22,16 +22,20 @@ import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.Flicker import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.exitSplitScreen import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen + import com.android.server.wm.flicker.repetitions +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.testapp.Components import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -53,23 +57,24 @@ class ExitLegacySplitScreenFromBottomTest( @JvmStatic fun getParams(): Collection> { val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = StandardAppHelper(instrumentation, - "com.android.wm.shell.flicker.testapp", "SimpleApp") + val splitScreenApp = SplitScreenHelper(instrumentation, + TEST_APP_SPLITSCREEN_PRIMARY_LABEL, + Components.SplitScreenActivity()) - // b/161435597 causes the test not to work on 90 degrees - return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) + // TODO(b/162923992) Use of multiple segments of flicker spec for testing + return FlickerTestRunnerFactory(instrumentation, + listOf(Surface.ROTATION_0, Surface.ROTATION_90)) .buildTest { configuration -> withTestName { - buildTestTag("exitSplitScreenFromBottom", testApp, + buildTestTag("exitSplitScreenFromBottom", splitScreenApp, configuration) } repeat { configuration.repetitions } setup { - test { - device.wakeUpAndGoToHomeScreen() - } eachRun { - testApp.open() + device.wakeUpAndGoToHomeScreen() + device.openQuickStepAndClearRecentAppsFromOverview() + splitScreenApp.launchViaIntent() device.launchSplitScreen() device.waitForIdle() this.setRotation(configuration.endRotation) @@ -77,12 +82,10 @@ class ExitLegacySplitScreenFromBottomTest( } teardown { eachRun { - testApp.exit() - } - test { if (device.isInSplitScreen()) { device.exitSplitScreen() } + splitScreenApp.exit() } } transitions { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt index c51c73a9e2487..84bfe9451e0a7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt @@ -82,7 +82,7 @@ class ExitLegacySplitScreenTest( } layersTrace { visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName)) + listOf(LAUNCHER_PACKAGE_NAME)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt new file mode 100644 index 0000000000000..e9d3eb7f475dc --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.legacysplitscreen + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.dockedStackDividerIsInvisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreenTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class NonResizableDismissInLegacySplitScreenTest( + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + + @Test + fun testNonResizableDismissInLegacySplitScreenTest() { + val testTag = "testNonResizableDismissInLegacySplitScreenTest" + + runWithFlicker(transitionSetup) { + withTestName { testTag } + repeat { SplitScreenHelper.TEST_REPETITIONS } + transitions { + nonResizeableApp.launchViaIntent() + splitScreenApp.launchViaIntent() + device.launchSplitScreen() + nonResizeableApp.reopenAppFromOverview() + } + assertions { + layersTrace { + dockedStackDividerIsInvisible() + end("appsEndingBounds", enabled = false) { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) + } + visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName, LETTER_BOX_NAME) + ) + } + windowManagerTrace { + end { + showsAppWindow(nonResizeableApp.defaultWindowName) + hidesAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt new file mode 100644 index 0000000000000..b5a36f5a31d4f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.legacysplitscreen + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.dockedStackDividerIsInvisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreenTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class NonResizableLaunchInLegacySplitScreenTest( + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + + @Test + fun testNonResizableLaunchInLegacySplitScreenTest() { + val testTag = "NonResizableLaunchInLegacySplitScreenTest" + + runWithFlicker(transitionSetup) { + withTestName { testTag } + repeat { SplitScreenHelper.TEST_REPETITIONS } + transitions { + nonResizeableApp.launchViaIntent() + splitScreenApp.launchViaIntent() + device.launchSplitScreen() + nonResizeableApp.reopenAppFromOverview() + } + assertions { + layersTrace { + dockedStackDividerIsInvisible() + end("appsEndingBounds", enabled = false) { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) + } + visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName, LETTER_BOX_NAME) + ) + } + windowManagerTrace { + end { + showsAppWindow(nonResizeableApp.defaultWindowName) + hidesAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt index af038698de800..90577ef19c1a0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt @@ -17,37 +17,22 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.Flicker -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.appWindowBecomesVisible -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.layerBecomesVisible -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.focusChanges +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.appPairsDividerBecomesVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -56,85 +41,59 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreenTest` */ -@Presubmit +// TODO: Add back to pre-submit when stable. +//@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class OpenAppToLegacySplitScreenTest( - testName: String, - flickerSpec: Flicker -) : FlickerTestRunner(testName, flickerSpec) { + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + @Test + fun OpenAppToLegacySplitScreenTest() { + val testTag = "OpenAppToLegacySplitScreenTest" + + runWithFlicker(transitionSetup) { + withTestName { testTag } + repeat { SplitScreenHelper.TEST_REPETITIONS } + transitions { + splitScreenApp.launchViaIntent() + device.pressHome() + this.setRotation(rotation) + device.launchSplitScreen() + } + assertions { + windowManagerTrace { + visibleWindowsShownMoreThanOneConsecutiveEntry() + appWindowBecomesVisible(splitScreenApp.getPackage()) + } + + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + noUncoveredRegions(rotation, enabled = false) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME)) + appPairsDividerBecomesVisible() + layerBecomesVisible(splitScreenApp.getPackage()) + } + + eventLog { + focusChanges(splitScreenApp.`package`, + "recents_animation_input_consumer", "NexusLauncherActivity", + bugId = 151179149) + } + } + } + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) - .launcherStrategy.supportedLauncherPackage - val testApp = StandardAppHelper(instrumentation, - "com.android.wm.shell.flicker.testapp", "SimpleApp") - - // b/161435597 causes the test not to work on 90 degrees - return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) - .buildTest { configuration -> - withTestName { - buildTestTag("appToSplitScreen", testApp, configuration) - } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview() - } - eachRun { - testApp.open() - device.pressHome() - this.setRotation(configuration.endRotation) - } - } - teardown { - eachRun { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - test { - testApp.exit() - } - } - transitions { - device.launchSplitScreen() - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - appWindowBecomesVisible(testApp.getPackage()) - } - - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.endRotation, enabled = false) - navBarLayerRotatesAndScales(configuration.endRotation, - bugId = 140855415) - statusBarLayerRotatesScales(configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName)) - - dockedStackDividerBecomesVisible() - layerBecomesVisible(testApp.getPackage()) - } - - eventLog { - focusChanges(testApp.`package`, - "recents_animation_input_consumer", "NexusLauncherActivity", - bugId = 151179149) - } - } - } + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt index a536ec8e2e0be..2b94c5f3fee96 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt @@ -17,6 +17,15 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.support.test.launcherhelper.LauncherStrategyFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.NonRotationTestBase import com.android.wm.shell.flicker.TEST_APP_NONRESIZEABLE_LABEL import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL @@ -37,6 +46,39 @@ abstract class SplitScreenTestBase( protected val nonResizeableApp = SplitScreenHelper(instrumentation, TEST_APP_NONRESIZEABLE_LABEL, Components.NonResizeableActivity()) - protected val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) + + protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) .launcherStrategy.supportedLauncherPackage + protected val LIVE_WALLPAPER_PACKAGE_NAME = + "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" + protected val LETTER_BOX_NAME = "Letterbox" + + protected val transitionSetup: FlickerBuilder + get() = FlickerBuilder(instrumentation).apply { + setup { + eachRun { + uiDevice.wakeUpAndGoToHomeScreen() + uiDevice.openQuickStepAndClearRecentAppsFromOverview() + } + } + teardown { + eachRun { + if (uiDevice.isInSplitScreen()) { + uiDevice.exitSplitScreen() + } + splitScreenApp.exit() + nonResizeableApp.exit() + } + } + assertions { + layersTrace { + navBarLayerIsAlwaysVisible() + statusBarLayerIsAlwaysVisible() + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index c6cc83b4c3bb7..7a4bcb1b8c37f 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1646,7 +1646,7 @@ class ActivityStarter { if (startedActivityStack != null && startedActivityStack.isAttached() && !startedActivityStack.hasActivity() && !startedActivityStack.isActivityTypeHome()) { - startedActivityStack.removeIfPossible(); + startedActivityStack.removeIfPossible("handleStartResult"); startedActivityStack = null; } return startedActivityStack; @@ -1834,7 +1834,7 @@ class ActivityStarter { return top.getTask(); } else { // Remove the stack if no activity in the stack. - stack.removeIfPossible(); + stack.removeIfPossible("computeTargetTask"); } } return null; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c3a754279f80d..4494d99b0deec 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5490,7 +5490,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!hasNonEmptyHomeRootTask && getRootTaskCount() > 0) { // Release this display if only empty home root task(s) are left. This display will be // released along with the root task(s) removal. - forAllRootTasks(Task::removeIfPossible); + forAllRootTasks(t -> {t.removeIfPossible("releaseSelfIfNeeded");}); } else if (getTopRootTask() == null) { removeIfPossible(); mRootWindowContainer.mTaskSupervisor diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a88e4537d1a25..a2a69855b8396 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -916,21 +916,26 @@ class Task extends WindowContainer { mTaskSupervisor.mRecentTasks.remove(this); } - removeIfPossible(); + removeIfPossible("cleanUpResourcesForDestroy"); } @VisibleForTesting @Override void removeIfPossible() { + removeIfPossible("removeTaskIfPossible"); + } + + void removeIfPossible(String reason) { final boolean isRootTask = isRootTask(); if (!isRootTask) { mAtmService.getLockTaskController().clearLockedTask(this); } if (shouldDeferRemoval()) { - if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId); + if (DEBUG_ROOT_TASK) Slog.i(TAG, + "removeTask:" + reason + " deferring removing taskId=" + mTaskId); return; } - removeImmediately(); + removeImmediately(reason); if (isLeafTask()) { mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId); @@ -1774,8 +1779,8 @@ class Task extends WindowContainer { getRootTask().removeChild(this, reason); } EventLogTags.writeWmTaskRemoved(mTaskId, - "removeChild: last r=" + r + " in t=" + this); - removeIfPossible(); + "removeChild:" + reason + " last r=" + r + " in t=" + this); + removeIfPossible(reason); } } @@ -1818,7 +1823,7 @@ class Task extends WindowContainer { if (r.finishing) return; // Task was restored from persistent storage. r.takeFromHistory(); - removeChild(r); + removeChild(r, reason); }); } else { forAllActivities((r) -> { @@ -3214,8 +3219,12 @@ class Task extends WindowContainer { @Override void removeImmediately() { - if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId); - EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask"); + removeImmediately("removeTask"); + } + + void removeImmediately(String reason) { + if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId); + EventLogTags.writeWmTaskRemoved(mTaskId, reason); // If applicable let the TaskOrganizer know the Task is vanishing. setTaskOrganizer(null); @@ -4986,7 +4995,7 @@ class Task extends WindowContainer { mTaskAppearedSent = false; setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */); if (mCreatedByOrganizer) { - removeImmediately(); + removeImmediately("setTaskOrganizer"); } } @@ -6708,7 +6717,7 @@ class Task extends WindowContainer { /** Finish all activities in the stack without waiting. */ void finishAllActivitiesImmediately() { if (!hasChild()) { - removeIfPossible(); + removeIfPossible("finishAllActivitiesImmediately"); return; } forAllActivities((r) -> { @@ -8002,7 +8011,7 @@ class Task extends WindowContainer { // Task created by organizer are added as root. final Task launchRootTask = mCreatedByOrganizer - ? null : tda.updateLaunchRootTask(mWindowingMode); + ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType); if (launchRootTask != null) { // Since this task will be put into a root task, its windowingMode will be // inherited. diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 3f4150b211781..2ebdda69eb61f 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -28,7 +28,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -54,6 +53,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.ToBooleanFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; @@ -117,8 +117,18 @@ final class TaskDisplayArea extends DisplayArea { private RootWindowContainer mRootWindowContainer; - // When non-null, new tasks get put into this root task. - Task mLaunchRootTask = null; + // Launch root tasks by activityType then by windowingMode. + static private class LaunchRootTaskDef { + Task task; + int[] windowingModes; + int[] activityTypes; + + boolean contains(int windowingMode, int activityType) { + return ArrayUtils.contains(windowingModes, windowingMode) + && ArrayUtils.contains(activityTypes, activityType); + } + } + private final ArrayList mLaunchRootTasks = new ArrayList<>(); /** * A focusable stack that is purposely to be positioned at the top. Although the stack may not @@ -1017,7 +1027,7 @@ final class TaskDisplayArea extends DisplayArea { } else if (candidateTask != null) { final Task stack = candidateTask; final int position = onTop ? POSITION_TOP : POSITION_BOTTOM; - Task launchRootTask = updateLaunchRootTask(windowingMode); + final Task launchRootTask = getLaunchRootTask(windowingMode, activityType); if (launchRootTask != null) { if (stack.getParent() == null) { @@ -1096,40 +1106,41 @@ final class TaskDisplayArea extends DisplayArea { .build(); } - /** @return the root task to create the next task in. */ - Task updateLaunchRootTask(int windowingMode) { - if (!isSplitScreenWindowingMode(windowingMode)) { - // Only split-screen windowing modes can do this currently... - return null; + // TODO: Also clear when task is removed from system? + void setLaunchRootTask(Task rootTask, int[] windowingModes, int[] activityTypes) { + if (!rootTask.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Can't set not mCreatedByOrganizer as launch root tr=" + rootTask); } - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer child = mChildren.get(i); - if (child.asTaskDisplayArea() != null) { - final Task t = child.asTaskDisplayArea().updateLaunchRootTask(windowingMode); - if (t != null) { - return t; - } - continue; - } - final Task t = mChildren.get(i).asTask(); - if (t == null || !t.mCreatedByOrganizer - || t.getRequestedOverrideWindowingMode() != windowingMode) { - continue; - } - // If not already set, pick a launch root which is not the one we are launching into. - if (mLaunchRootTask == null) { - for (int j = 0, n = mChildren.size(); j < n; ++j) { - final Task tt = mChildren.get(j).asTask(); - if (tt != null && tt.mCreatedByOrganizer && tt != t) { - mLaunchRootTask = tt; - break; - } - } - } - return t; + LaunchRootTaskDef def = null; + for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { + if (mLaunchRootTasks.get(i).task.mTaskId != rootTask.mTaskId) continue; + def = mLaunchRootTasks.get(i); } - return mLaunchRootTask; + + if (def != null) { + // Remove so we add to the end of the list. + mLaunchRootTasks.remove(def); + } else { + def = new LaunchRootTaskDef(); + def.task = rootTask; + } + + def.activityTypes = activityTypes; + def.windowingModes = windowingModes; + if (!ArrayUtils.isEmpty(windowingModes) || !ArrayUtils.isEmpty(activityTypes)) { + mLaunchRootTasks.add(def); + } + } + + Task getLaunchRootTask(int windowingMode, int activityType) { + for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { + if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) { + return mLaunchRootTasks.get(i).task; + } + } + return null; } /** @@ -1321,7 +1332,6 @@ final class TaskDisplayArea extends DisplayArea { void onSplitScreenModeDismissed(Task toTop) { mAtmService.deferWindowLayout(); try { - mLaunchRootTask = null; moveSplitScreenTasksToFullScreen(); } finally { final Task topFullscreenStack = toTop != null @@ -1919,7 +1929,20 @@ final class TaskDisplayArea extends DisplayArea { if (mLastFocusedRootTask != null) { pw.println(doublePrefix + "mLastFocusedRootTask=" + mLastFocusedRootTask); } + final String triplePrefix = doublePrefix + " "; + + if (mLaunchRootTasks.size() > 0) { + pw.println(doublePrefix + "mLaunchRootTasks:"); + for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { + final LaunchRootTaskDef def = mLaunchRootTasks.get(i); + pw.println(triplePrefix + + def.activityTypes + " " + + def.windowingModes + " " + + " task=" + def.task); + } + } + pw.println(doublePrefix + "Application tokens in top down Z order:"); for (int index = getChildCount() - 1; index >= 0; --index) { final WindowContainer child = getChildAt(index); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 089071f5de54b..9a83ac7ffe001 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -510,7 +510,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d", task.getDisplayId(), task.getWindowingMode()); - task.removeImmediately(); + task.removeImmediately("deleteRootTask"); return true; } } finally { @@ -600,45 +600,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - @Override - public void setLaunchRoot(int displayId, @Nullable WindowContainerToken token) { - enforceTaskPermission("setLaunchRoot()"); - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - TaskDisplayArea defaultTaskDisplayArea = mService.mRootWindowContainer - .getDisplayContent(displayId).getDefaultTaskDisplayArea(); - if (defaultTaskDisplayArea == null) { - return; - } - WindowContainer wc = null; - if (token != null) { - wc = WindowContainer.fromBinder(token.asBinder()); - if (wc == null) { - throw new IllegalArgumentException("Can't resolve window from token"); - } - } - final Task task = wc == null ? null : wc.asTask(); - if (task == null) { - defaultTaskDisplayArea.mLaunchRootTask = null; - return; - } - if (!task.mCreatedByOrganizer) { - throw new IllegalArgumentException("Attempt to set task not created by " - + "organizer as launch root task=" + task); - } - if (task.getDisplayArea() == null - || task.getDisplayArea().getDisplayId() != displayId) { - throw new RuntimeException("Can't set launch root for display " + displayId - + " to task on display " + task.getDisplayContent().getDisplayId()); - } - task.getDisplayArea().mLaunchRootTask = task; - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - @Override public List getChildTasks(WindowContainerToken parent, @Nullable int[] activityTypes) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index b0e67ce177118..be1f7e16cd3c2 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,6 +17,10 @@ package com.android.server.wm; import static android.Manifest.permission.READ_FRAME_BUFFER; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; @@ -49,13 +53,16 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Consumer; /** * Server side implementation for the interface for organizing windows @@ -256,34 +263,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final List hops = t.getHierarchyOps(); for (int i = 0, n = hops.size(); i < n; ++i) { final WindowContainerTransaction.HierarchyOp hop = hops.get(i); - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - if (wc == null || !wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); - continue; - } - if (syncId >= 0) { - addToSyncSet(syncId, wc); - } - if (transition != null) { - transition.collect(wc); - if (hop.isReparent()) { - if (wc.getParent() != null) { - // Collect the current parent. It's visibility may change as a result - // of this reparenting. - transition.collect(wc.getParent()); + switch (hop.getType()) { + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: + final Task task = WindowContainer.fromBinder(hop.getContainer()).asTask(); + task.getDisplayArea().setLaunchRootTask(task, + hop.getWindowingModes(), hop.getActivityTypes()); + break; + case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: + effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); + break; + case HIERARCHY_OP_TYPE_REORDER: + case HIERARCHY_OP_TYPE_REPARENT: + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + continue; } - if (hop.getNewParent() != null) { - final WindowContainer parentWc = - WindowContainer.fromBinder(hop.getNewParent()); - if (parentWc == null) { - Slog.e(TAG, "Can't resolve parent window from token"); - continue; + if (syncId >= 0) { + addToSyncSet(syncId, wc); + } + if (transition != null) { + transition.collect(wc); + if (hop.isReparent()) { + if (wc.getParent() != null) { + // Collect the current parent. It's visibility may change as + // a result of this reparenting. + transition.collect(wc.getParent()); + } + if (hop.getNewParent() != null) { + final WindowContainer parentWc = + WindowContainer.fromBinder(hop.getNewParent()); + if (parentWc == null) { + Slog.e(TAG, "Can't resolve parent window from token"); + continue; + } + transition.collect(parentWc); + } } - transition.collect(parentWc); } - } + effects |= sanitizeAndApplyHierarchyOp(wc, hop); } - effects |= sanitizeAndApplyHierarchyOp(wc, hop); } // Queue-up bounds-change transactions for tasks which are now organized. Do // this after hierarchy ops so we have the final organized state. @@ -492,6 +511,85 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return TRANSACT_EFFECTS_LIFECYCLE; } + private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, + @Nullable Transition transition, int syncId) { + WindowContainer currentParent = hop.getContainer() != null + ? WindowContainer.fromBinder(hop.getContainer()) : null; + WindowContainer newParent = hop.getNewParent() != null + ? WindowContainer.fromBinder(hop.getNewParent()) : null; + if (currentParent == null && newParent == null) { + throw new IllegalArgumentException("reparentChildrenTasksHierarchyOp: " + hop); + } else if (currentParent == null) { + currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); + } else if (newParent == null) { + newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); + } + + if (currentParent == newParent) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop); + return 0; + } + if (!currentParent.isAttached()) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached=" + + currentParent + " hop=" + hop); + return 0; + } + if (!newParent.isAttached()) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached=" + + newParent + " hop=" + hop); + return 0; + } + + final boolean newParentInMultiWindow = newParent.inMultiWindowMode(); + final WindowContainer finalCurrentParent = currentParent; + Slog.i(TAG, "reparentChildrenTasksHierarchyOp" + + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop); + + // We want to collect the tasks first before re-parenting to avoid array shifting on us. + final ArrayList tasksToReparent = new ArrayList<>(); + + currentParent.forAllTasks((Consumer) (task) -> { + Slog.i(TAG, " Processing task=" + task); + if (task.mCreatedByOrganizer + || task.getParent() != finalCurrentParent) { + // We only care about non-organized task that are direct children of the thing we + // are reparenting from. + return; + } + + if (newParentInMultiWindow && !task.isResizeable()) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task=" + task); + } + + if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) return; + if (!ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) return; + + tasksToReparent.add(task); + }, !hop.getToTop()); + + final int count = tasksToReparent.size(); + for (int i = 0; i < count; ++i) { + final Task task = tasksToReparent.get(i); + if (syncId >= 0) { + addToSyncSet(syncId, task); + } + if (transition != null) transition.collect(task); + + if (newParent instanceof TaskDisplayArea) { + // For now, reparenting to display area is different from other reparents... + task.reparent((TaskDisplayArea) newParent, hop.getToTop()); + } else { + task.reparent((Task) newParent, + hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, + false /*moveParents*/, "processChildrenTaskReparentHierarchyOp"); + } + } + + if (transition != null) transition.collect(newParent); + + return TRANSACT_EFFECTS_LIFECYCLE; + } + private void sanitizeWindowContainer(WindowContainer wc) { if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) { throw new RuntimeException("Invalid token in task or displayArea transaction"); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 21aa6bf8750a4..fc96b69a568bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -156,8 +156,10 @@ public class ActivityStackTests extends WindowTestsBase { organizer.setMoveToSecondaryOnEnter(false); // Create primary splitscreen stack. - final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor) + .setParentTask(organizer.mPrimary) + .setOnTop(true) + .build(); // Assert windowing mode. assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode()); @@ -205,14 +207,15 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testSplitScreenMoveToBack() { TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); - // Set up split-screen with primary on top and secondary containing the home task below - // another stack. + // Explicitly reparent task to primary split root to enter split mode, in which implies + // primary on top and secondary containing the home task below another stack. final Task primaryTask = mDefaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task homeRoot = mDefaultTaskDisplayArea.getRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); - final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + primaryTask.reparent(organizer.mPrimary, POSITION_TOP); mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary, false /* includingParents */); 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 fee848b1a10dd..4bfc83742c04b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -485,8 +485,10 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityRecord splitSecondActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor) - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).setCreateActivity(true) - .build().getTopMostActivity(); + .setParentTask(splitOrg.mPrimary) + .setCreateActivity(true) + .build() + .getTopMostActivity(); splitPrimaryActivity.mVisibleRequested = splitSecondActivity.mVisibleRequested = true; assertEquals(splitOrg.mPrimary, splitPrimaryActivity.getRootTask()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 31e2dce008deb..8b93372e5a764 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -17,6 +17,8 @@ package com.android.server.wm; import static android.app.AppOpsManager.OP_NONE; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -1107,6 +1109,17 @@ class WindowTestsBase extends SystemServiceTestsBase { // moves everything to secondary. Most tests expect this since sysui usually does it. boolean mMoveToSecondaryOnEnter = true; int mDisplayId; + private static final int[] CONTROLLED_ACTIVITY_TYPES = { + ACTIVITY_TYPE_STANDARD, + ACTIVITY_TYPE_HOME, + ACTIVITY_TYPE_RECENTS, + ACTIVITY_TYPE_UNDEFINED + }; + private static final int[] CONTROLLED_WINDOWING_MODES = { + WINDOWING_MODE_FULLSCREEN, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, + WINDOWING_MODE_UNDEFINED + }; TestSplitOrganizer(ActivityTaskManagerService service, DisplayContent display) { mService = service; mDisplayId = display.mDisplayId; @@ -1151,9 +1164,9 @@ class WindowTestsBase extends SystemServiceTestsBase { if (!mMoveToSecondaryOnEnter) { return; } - mService.mTaskOrganizerController.setLaunchRoot(mDisplayId, - mSecondary.mRemoteToken.toWindowContainerToken()); DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId); + dc.getDefaultTaskDisplayArea().setLaunchRootTask( + mSecondary, CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES); dc.forAllRootTasks(rootTask -> { if (!WindowConfiguration.isSplitScreenWindowingMode(rootTask.getWindowingMode())) { rootTask.reparent(mSecondary, POSITION_BOTTOM);