diff options
author | 2024-12-11 03:33:26 +0000 | |
---|---|---|
committer | 2024-12-11 03:33:26 +0000 | |
commit | 3ffdc22a059a7e0520f11c4036c1c98e6fa1a36c (patch) | |
tree | 1358c31ed1f4f0ef14d0557010f862d1c1e49f63 | |
parent | 6591b9a7220603271694719f176327622754945b (diff) | |
parent | 7073aa9d179def871222c52d0e36dabc1fbda762 (diff) |
Merge "Allow multiple adjacent TFs (3/n)" into main
8 files changed, 241 insertions, 43 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 3f814f9db1c8..417d6a5d12ee 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -110,6 +110,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.apphibernation.AppHibernationManagerInternal; import com.android.server.apphibernation.AppHibernationService; +import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.concurrent.TimeUnit; @@ -778,11 +779,12 @@ class ActivityMetricsLogger { */ private void updateSplitPairLaunches(@NonNull TransitionInfo info) { final Task launchedActivityTask = info.mLastLaunchedActivity.getTask(); - final Task adjacentToLaunchedTask = launchedActivityTask.getAdjacentTask(); - if (adjacentToLaunchedTask == null) { + final Task launchedSplitRootTask = launchedActivityTask.getTaskWithAdjacent(); + if (launchedSplitRootTask == null) { // Not a part of a split pair return; } + for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) { final TransitionInfo otherInfo = mTransitionInfoList.get(i); if (otherInfo == info) { @@ -790,7 +792,15 @@ class ActivityMetricsLogger { } final Task otherTask = otherInfo.mLastLaunchedActivity.getTask(); // The adjacent task is the split root in which activities are started - if (otherTask.isDescendantOf(adjacentToLaunchedTask)) { + final boolean isDescendantOfAdjacent; + if (Flags.allowMultipleAdjacentTaskFragments()) { + isDescendantOfAdjacent = launchedSplitRootTask.forOtherAdjacentTasks( + otherTask::isDescendantOf); + } else { + isDescendantOfAdjacent = otherTask.isDescendantOf( + launchedSplitRootTask.getAdjacentTask()); + } + if (isDescendantOfAdjacent) { if (DEBUG_METRICS) { Slog.i(TAG, "Found adjacent tasks t1=" + launchedActivityTask.mTaskId + " t2=" + otherTask.mTaskId); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 741eefae4462..492d84f4a12f 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -996,8 +996,7 @@ public class AppTransitionController { // If the current window container is a task with adjacent task set, the both // adjacent tasks will be opened or closed together. To get their opening or // closing animation target independently, skip promoting their animation targets. - if (current.asTask() != null - && current.asTask().getAdjacentTask() != null) { + if (current.asTask() != null && current.asTask().hasAdjacentTask()) { canPromote = false; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 659bb6784c89..01e00e9d67f7 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2485,7 +2485,7 @@ public class DisplayPolicy { final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); final boolean adjacentTasksVisible = defaultTaskDisplayArea.getRootTask(task -> task.isVisible() - && task.getTopLeafTask().getAdjacentTask() != null) + && task.getTopLeafTask().hasAdjacentTask()) != null; final Task topFreeformTask = defaultTaskDisplayArea .getTopRootTaskInWindowingMode(WINDOWING_MODE_FREEFORM); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b3b8c6eab25e..3d2868540334 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1796,12 +1796,24 @@ class RootWindowContainer extends WindowContainer<DisplayContent> activityAssistInfos.clear(); activityAssistInfos.add(new ActivityAssistInfo(top)); // Check if the activity on the split screen. - final Task adjacentTask = top.getTask().getAdjacentTask(); - if (adjacentTask != null) { - final ActivityRecord adjacentActivityRecord = - adjacentTask.getTopNonFinishingActivity(); - if (adjacentActivityRecord != null) { - activityAssistInfos.add(new ActivityAssistInfo(adjacentActivityRecord)); + if (Flags.allowMultipleAdjacentTaskFragments()) { + top.getTask().forOtherAdjacentTasks(task -> { + final ActivityRecord adjacentActivityRecord = + task.getTopNonFinishingActivity(); + if (adjacentActivityRecord != null) { + activityAssistInfos.add( + new ActivityAssistInfo(adjacentActivityRecord)); + } + }); + } else { + final Task adjacentTask = top.getTask().getAdjacentTask(); + if (adjacentTask != null) { + final ActivityRecord adjacentActivityRecord = + adjacentTask.getTopNonFinishingActivity(); + if (adjacentActivityRecord != null) { + activityAssistInfos.add( + new ActivityAssistInfo(adjacentActivityRecord)); + } } } if (rootTask == topFocusedRootTask) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c8befb21fe13..76d8861022bb 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2500,20 +2500,79 @@ class Task extends TaskFragment { return parentTask == null ? null : parentTask.getCreatedByOrganizerTask(); } - /** @return the first adjacent task of this task or its parent. */ + /** @deprecated b/373709676 replace with {@link #forOtherAdjacentTasks(Consumer)} ()}. */ + @Deprecated @Nullable Task getAdjacentTask() { - final TaskFragment adjacentTaskFragment = getAdjacentTaskFragment(); - if (adjacentTaskFragment != null && adjacentTaskFragment.asTask() != null) { - return adjacentTaskFragment.asTask(); + if (Flags.allowMultipleAdjacentTaskFragments()) { + throw new IllegalStateException("allowMultipleAdjacentTaskFragments is enabled. " + + "Use #forOtherAdjacentTasks instead"); + } + final Task taskWithAdjacent = getTaskWithAdjacent(); + if (taskWithAdjacent == null) { + return null; } + return taskWithAdjacent.getAdjacentTaskFragment().asTask(); + } + /** Finds the first Task parent (or itself) that has adjacent. */ + @Nullable + Task getTaskWithAdjacent() { + if (hasAdjacentTaskFragment()) { + return this; + } final WindowContainer parent = getParent(); if (parent == null || parent.asTask() == null) { return null; } + return parent.asTask().getTaskWithAdjacent(); + } + + /** Returns true if this or its parent has adjacent Task. */ + boolean hasAdjacentTask() { + return getTaskWithAdjacent() != null; + } - return parent.asTask().getAdjacentTask(); + /** + * Finds the first Task parent (or itself) that has adjacent. Runs callback on all the adjacent + * Tasks. The invoke order is not guaranteed. + */ + void forOtherAdjacentTasks(@NonNull Consumer<Task> callback) { + if (!Flags.allowMultipleAdjacentTaskFragments()) { + throw new IllegalStateException("allowMultipleAdjacentTaskFragments is not enabled. " + + "Use #getAdjacentTask instead"); + } + + final Task taskWithAdjacent = getTaskWithAdjacent(); + if (taskWithAdjacent == null) { + return; + } + final AdjacentSet adjacentTasks = taskWithAdjacent.getAdjacentTaskFragments(); + adjacentTasks.forAllTaskFragments(tf -> { + // We don't support Task adjacent to non-Task TaskFragment. + callback.accept(tf.asTask()); + }, taskWithAdjacent /* exclude */); + } + + /** + * Finds the first Task parent (or itself) that has adjacent. Runs callback on all the adjacent + * Tasks. Returns early if callback returns true on any of them. The invoke order is not + * guaranteed. + */ + boolean forOtherAdjacentTasks(@NonNull Predicate<Task> callback) { + if (!Flags.allowMultipleAdjacentTaskFragments()) { + throw new IllegalStateException("allowMultipleAdjacentTaskFragments is not enabled. " + + "Use getAdjacentTask instead"); + } + final Task taskWithAdjacent = getTaskWithAdjacent(); + if (taskWithAdjacent == null) { + return false; + } + final AdjacentSet adjacentTasks = taskWithAdjacent.getAdjacentTaskFragments(); + return adjacentTasks.forAllTaskFragments(tf -> { + // We don't support Task adjacent to non-Task TaskFragment. + return callback.test(tf.asTask()); + }, taskWithAdjacent /* exclude */); } // TODO(task-merge): Figure out what's the right thing to do for places that used it. @@ -2907,7 +2966,7 @@ class Task extends TaskFragment { Rect outSurfaceInsets) { // If this task has its adjacent task, it means they should animate together. Use display // bounds for them could move same as full screen task. - if (getAdjacentTask() != null) { + if (hasAdjacentTask()) { super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets); return; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index c1a33c468cee..9564c5959d98 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -61,6 +61,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.LaunchParamsController.LaunchParams; +import com.android.window.flags.Flags; import java.io.PrintWriter; import java.util.ArrayList; @@ -1088,8 +1089,19 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Use launch-adjacent-flag-root if launching with launch-adjacent flag. if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0 && mLaunchAdjacentFlagRootTask != null) { - final Task launchAdjacentRootAdjacentTask = - mLaunchAdjacentFlagRootTask.getAdjacentTask(); + final Task launchAdjacentRootAdjacentTask; + if (Flags.allowMultipleAdjacentTaskFragments()) { + final Task[] tmpTask = new Task[1]; + mLaunchAdjacentFlagRootTask.forOtherAdjacentTasks(task -> { + // TODO(b/382208145): enable FLAG_ACTIVITY_LAUNCH_ADJACENT for 3+. + // Find the first adjacent for now. + tmpTask[0] = task; + return true; + }); + launchAdjacentRootAdjacentTask = tmpTask[0]; + } else { + launchAdjacentRootAdjacentTask = mLaunchAdjacentFlagRootTask.getAdjacentTask(); + } if (sourceTask != null && (sourceTask == candidateTask || sourceTask.topRunningActivity() == null)) { // Do nothing when task that is getting opened is same as the source or when @@ -1114,15 +1126,26 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) { final Task launchRootTask = mLaunchRootTasks.get(i).task; - final Task adjacentRootTask = launchRootTask != null - ? launchRootTask.getAdjacentTask() : null; - if (sourceTask != null && adjacentRootTask != null - && (sourceTask == adjacentRootTask - || sourceTask.isDescendantOf(adjacentRootTask))) { - return adjacentRootTask; - } else { + if (launchRootTask == null || sourceTask == null) { + return launchRootTask; + } + if (!Flags.allowMultipleAdjacentTaskFragments()) { + final Task adjacentRootTask = launchRootTask.getAdjacentTask(); + if (adjacentRootTask != null && (sourceTask == adjacentRootTask + || sourceTask.isDescendantOf(adjacentRootTask))) { + return adjacentRootTask; + } return launchRootTask; } + final Task[] adjacentRootTask = new Task[1]; + launchRootTask.forOtherAdjacentTasks(task -> { + if (sourceTask == task || sourceTask.isDescendantOf(task)) { + adjacentRootTask[0] = task; + return true; + } + return false; + }); + return adjacentRootTask[0] != null ? adjacentRootTask[0] : launchRootTask; } } @@ -1133,12 +1156,31 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // A pinned task relaunching should be handled by its task organizer. Skip fallback // launch target of a pinned task from source task. || candidateTask.getWindowingMode() != WINDOWING_MODE_PINNED)) { - final Task adjacentTarget = sourceTask.getAdjacentTask(); - if (adjacentTarget != null) { - if (candidateTask != null - && (candidateTask == adjacentTarget - || candidateTask.isDescendantOf(adjacentTarget))) { - return adjacentTarget; + final Task taskWithAdjacent = sourceTask.getTaskWithAdjacent(); + if (taskWithAdjacent != null) { + // Has adjacent. + if (candidateTask == null) { + return sourceTask.getCreatedByOrganizerTask(); + } + // Check if the candidate is already positioned in the adjacent Task. + if (Flags.allowMultipleAdjacentTaskFragments()) { + final Task[] adjacentRootTask = new Task[1]; + sourceTask.forOtherAdjacentTasks(task -> { + if (candidateTask == task || candidateTask.isDescendantOf(task)) { + adjacentRootTask[0] = task; + return true; + } + return false; + }); + if (adjacentRootTask[0] != null) { + return adjacentRootTask[0]; + } + } else { + final Task adjacentTarget = taskWithAdjacent.getAdjacentTask(); + if (candidateTask == adjacentTarget + || candidateTask.isDescendantOf(adjacentTarget)) { + return adjacentTarget; + } } return sourceTask.getCreatedByOrganizerTask(); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 9fb5bea132ef..cb6b69072e14 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -531,6 +531,28 @@ class TaskFragment extends WindowContainer<WindowContainer> { return mAdjacentTaskFragments; } + /** + * Runs callback on all TaskFragments that are adjacent to this. The invoke order is not + * guaranteed. + */ + void forOtherAdjacentTaskFragments(@NonNull Consumer<TaskFragment> callback) { + if (mAdjacentTaskFragments == null) { + return; + } + mAdjacentTaskFragments.forAllTaskFragments(callback, this /* exclude */); + } + + /** + * Runs callback on all TaskFragments that are adjacent to this. Returns early if callback + * returns true on any of them. The invoke order is not guaranteed. + */ + boolean forOtherAdjacentTaskFragments(@NonNull Predicate<TaskFragment> callback) { + if (mAdjacentTaskFragments == null) { + return false; + } + return mAdjacentTaskFragments.forAllTaskFragments(callback, this /* exclude */); + } + boolean hasAdjacentTaskFragment() { if (!Flags.allowMultipleAdjacentTaskFragments()) { return mAdjacentTaskFragment != null; @@ -3198,24 +3220,47 @@ class TaskFragment extends WindowContainer<WindowContainer> { return false; } - final TaskFragment adjacentTf = getAdjacentTaskFragment(); - if (adjacentTf == null) { + if (!hasAdjacentTaskFragment()) { // early return if no adjacent TF. return false; } - if (getParent().mChildren.indexOf(adjacentTf) < getParent().mChildren.indexOf(this)) { - // early return if this TF already has higher z-ordering. - return false; + final ArrayList<WindowContainer> siblings = getParent().mChildren; + final int zOrder = siblings.indexOf(this); + + if (!Flags.allowMultipleAdjacentTaskFragments()) { + if (siblings.indexOf(getAdjacentTaskFragment()) < zOrder) { + // early return if this TF already has higher z-ordering. + return false; + } + } else { + final boolean hasAdjacentOnTop = forOtherAdjacentTaskFragments( + tf -> siblings.indexOf(tf) > zOrder); + if (!hasAdjacentOnTop) { + // early return if this TF already has higher z-ordering. + return false; + } } - ToBooleanFunction<WindowState> getDimBehindWindow = + final ToBooleanFunction<WindowState> getDimBehindWindow = (w) -> (w.mAttrs.flags & FLAG_DIM_BEHIND) != 0 && w.mActivityRecord != null && w.mActivityRecord.isEmbedded() && (w.mActivityRecord.isVisibleRequested() || w.mActivityRecord.isVisible()); - if (adjacentTf.forAllWindows(getDimBehindWindow, true)) { - // early return if the adjacent Tf has a dimming window. - return false; + + if (!Flags.allowMultipleAdjacentTaskFragments()) { + final TaskFragment adjacentTf = getAdjacentTaskFragment(); + if (adjacentTf.forAllWindows(getDimBehindWindow, true)) { + // early return if the adjacent Tf has a dimming window. + return false; + } + } else { + final boolean adjacentHasDimmingWindow = forOtherAdjacentTaskFragments(tf -> { + return tf.forAllWindows(getDimBehindWindow, true); + }); + if (adjacentHasDimmingWindow) { + // early return if the adjacent Tf has a dimming window. + return false; + } } // boost if there's an Activity window that has FLAG_DIM_BEHIND flag. @@ -3528,6 +3573,37 @@ class TaskFragment extends WindowContainer<WindowContainer> { return mAdjacentSet.contains(taskFragment); } + /** + * Runs the callback on all adjacent TaskFragments. Skips the exclude one if not null. + */ + void forAllTaskFragments(@NonNull Consumer<TaskFragment> callback, + @Nullable TaskFragment exclude) { + for (int i = mAdjacentSet.size() - 1; i >= 0; i--) { + final TaskFragment taskFragment = mAdjacentSet.valueAt(i); + if (taskFragment != exclude) { + callback.accept(taskFragment); + } + } + } + + /** + * Runs the callback on all adjacent TaskFragments until one returns {@code true}. Skips the + * exclude one if not null. + */ + boolean forAllTaskFragments(@NonNull Predicate<TaskFragment> callback, + @Nullable TaskFragment exclude) { + for (int i = mAdjacentSet.size() - 1; i >= 0; i--) { + final TaskFragment taskFragment = mAdjacentSet.valueAt(i); + if (taskFragment == exclude) { + continue; + } + if (callback.test(taskFragment)) { + return true; + } + } + return false; + } + @Override public boolean equals(@Nullable Object o) { if (this == o) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 7f0c33657144..80e4c30b73f8 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -1263,7 +1263,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio if (windowingMode == WINDOWING_MODE_MULTI_WINDOW && com.android.window.flags.Flags .processPriorityPolicyForMultiWindowMode() - && task.getAdjacentTask() != null) { + && task.hasAdjacentTask()) { stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN; } else if (windowingMode == WINDOWING_MODE_FREEFORM) { hasResumedFreeform = true; |