diff options
4 files changed, 88 insertions, 2 deletions
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 1748b9d38538..8934cf67b380 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -39,7 +39,12 @@ interface ITaskOrganizerController { */ void unregisterTaskOrganizer(ITaskOrganizer organizer); - /** Creates a persistent root task in WM for a particular windowing-mode. */ + /** + * Creates a persistent root task in WM for a particular windowing-mode. + * + * It may be removed using {@link #deleteRootTask} or through + * {@link WindowContainerTransaction#removeRootTask}. + */ void createRootTask(int displayId, int windowingMode, IBinder launchCookie, boolean removeWithTaskOrganizer); diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 02f8e2f59b33..ce0ccd5c6d0d 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -614,6 +614,10 @@ public final class WindowContainerTransaction implements Parcelable { /** * Finds and removes a task and its children using its container token. The task is removed * from recents. + * + * If the task is a root task, its leaves are removed but the root task is not. Use + * {@link #removeRootTask(WindowContainerToken)} to remove the root task. + * * @param containerToken ContainerToken of Task to be removed */ @NonNull @@ -623,6 +627,19 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Finds and removes a root task created by an organizer and its leaves using its container + * token. + * + * @param containerToken ContainerToken of the root task to be removed + * @hide + */ + @NonNull + public WindowContainerTransaction removeRootTask(@NonNull WindowContainerToken containerToken) { + mHierarchyOps.add(HierarchyOp.createForRemoveRootTask(containerToken.asBinder())); + return this; + } + + /** * Sets whether a container is being drag-resized. * When {@code true}, the client will reuse a single (larger) surface size to avoid * continuous allocations on every size change. @@ -1573,6 +1590,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21; public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22; public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23; + public static final int HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK = 24; @IntDef(prefix = {"HIERARCHY_OP_TYPE_"}, value = { HIERARCHY_OP_TYPE_REPARENT, @@ -1598,7 +1616,8 @@ public final class WindowContainerTransaction implements Parcelable { HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION, HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES, HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE, - HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT + HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT, + HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK, }) @Retention(RetentionPolicy.SOURCE) public @interface HierarchyOpType { @@ -1795,6 +1814,18 @@ public final class WindowContainerTransaction implements Parcelable { .build(); } + /** + * Creates a hierarchy op for deleting a root task + * + * @hide + **/ + @NonNull + public static HierarchyOp createForRemoveRootTask(@NonNull IBinder container) { + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK) + .setContainer(container) + .build(); + } + /** Creates a hierarchy op for clearing adjacent root tasks. */ @NonNull public static HierarchyOp createForClearAdjacentRoots(@NonNull IBinder root) { @@ -2012,6 +2043,7 @@ public final class WindowContainerTransaction implements Parcelable { return "removeInsetsFrameProvider"; case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: return "setAlwaysOnTop"; case HIERARCHY_OP_TYPE_REMOVE_TASK: return "removeTask"; + case HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK: return "removeRootTask"; case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: return "finishActivity"; case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "clearAdjacentRoots"; case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH: @@ -2096,6 +2128,9 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_REMOVE_TASK: sb.append("task=").append(mContainer); break; + case HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK: + sb.append("rootTask=").append(mContainer); + break; case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: sb.append("activity=").append(mContainer); break; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index ad19b9a44670..a1755e4d9d3b 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -52,6 +52,7 @@ import static android.window.WindowContainerTransaction.Change.CHANGE_FOCUSABLE; import static android.window.WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT; import static android.window.WindowContainerTransaction.Change.CHANGE_HIDDEN; import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION; @@ -1131,6 +1132,23 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } break; } + case HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK: { + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + if (wc == null || wc.asTask() == null || !wc.isAttached() + || !wc.asTask().isRootTask() || !wc.asTask().mCreatedByOrganizer) { + Slog.e(TAG, "Attempt to remove invalid task: " + wc); + break; + } + final Task task = wc.asTask(); + if (task.isVisibleRequested() || task.isVisible()) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } + // Removes its leaves, but not itself. + mService.mTaskSupervisor.removeRootTask(task); + // Now that the root has no leaves, remove it too. . + task.remove(true /* withTransition */, "remove-root-task-through-hierarchyOp"); + break; + } case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); if (wc == null || !wc.isAttached()) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java index e9ece5dbdcc4..369600c3f8d7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java @@ -79,6 +79,34 @@ public class WindowContainerTransactionTests extends WindowTestsBase { } @Test + public void testRemoveRootTask() { + final Task rootTask = createTask(mDisplayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); + final ActivityRecord activity = createActivityRecord(mDisplayContent, task); + final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + + WindowContainerTransaction wct = new WindowContainerTransaction(); + WindowContainerToken token = rootTask.getTaskInfo().token; + wct.removeTask(token); + applyTransaction(wct); + + // There is still an activity to be destroyed, so the task is not removed immediately. + assertNotNull(task.getParent()); + assertTrue(rootTask.hasChild()); + assertTrue(task.hasChild()); + assertTrue(activity.finishing); + + activity.destroyed("testRemoveRootTask"); + // Assert that the container was removed after the activity is destroyed. + assertNull(task.getParent()); + assertEquals(0, task.getChildCount()); + assertNull(activity.getParent()); + assertNull(taskDisplayArea.getTask(task1 -> task1.mTaskId == rootTask.mTaskId)); + verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(task); + verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(rootTask); + } + + @Test public void testDesktopMode_tasksAreBroughtToFront() { final TestDesktopOrganizer desktopOrganizer = new TestDesktopOrganizer(mAtm); TaskDisplayArea tda = desktopOrganizer.mDefaultTDA; |