diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 1da1aa3e5e1d1..c9f3356180138 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2784,7 +2784,8 @@ class ActivityStarter { } } else { // Use the child TaskFragment (if any) as the new parent if the activity can be embedded - final ActivityRecord top = task.topRunningActivity(); + final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */, + false /* includingEmbeddedTask */); newParent = top != null ? top.getTaskFragment() : task; } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 3516c75493e29..da3f9839ad1c7 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -365,8 +365,26 @@ class RootWindowContainer extends WindowContainer return false; } + if (matchingCandidate(task)) { + return true; + } + + // Looking for the embedded tasks (if any) + return !task.isLeafTaskFragment() && task.forAllLeafTaskFragments( + this::matchingCandidate); + } + + boolean matchingCandidate(TaskFragment taskFragment) { + final Task task = taskFragment.asTask(); + if (task == null) { + return false; + } + // Overlays should not be considered as the task's logical top activity. - final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */); + // Activities of the tasks that embedded from this one should not be used. + final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */, + false /* includingEmbeddedTask */); + if (r == null || r.finishing || r.mUserId != userId || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7cb8109f432b9..7d911bc049f0a 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1394,14 +1394,6 @@ class Task extends TaskFragment { return mFindRootHelper.findRoot(ignoreRelinquishIdentity, setToBottomIfNone); } - ActivityRecord getTopNonFinishingActivity() { - return getTopNonFinishingActivity(true /* includeOverlays */); - } - - ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) { - return getTopActivity(false /*includeFinishing*/, includeOverlays); - } - ActivityRecord topRunningActivityLocked() { if (getParent() == null) { return null; @@ -2976,11 +2968,53 @@ class Task extends TaskFragment { /** Returns the top-most activity that occludes the given one, or {@code null} if none. */ @Nullable ActivityRecord getOccludingActivityAbove(ActivityRecord activity) { - final ActivityRecord top = getActivity(ActivityRecord::occludesParent, - true /* traverseTopToBottom */, activity); + final ActivityRecord top = getActivity(r -> { + if (!r.occludesParent()) { + return false; + } + if (r == activity) { + // Reached the given activity, return the activity to stop searching. + return true; + } + + TaskFragment parent = r.getTaskFragment(); + if (parent == activity.getTaskFragment()) { + // Found it. This activity on top of the given activity on the same TaskFragment. + return true; + } + if (isSelfOrNonEmbeddedTask(parent.asTask())) { + // Found it. This activity is the direct child of a leaf Task without being + // embedded. + return true; + } + // The candidate activity is being embedded. Checking if the bounds of the containing + // TaskFragment equals to the outer TaskFragment. + TaskFragment grandParent = parent.getParent().asTaskFragment(); + while (grandParent != null) { + if (!parent.getBounds().equals(grandParent.getBounds())) { + // Not occluding the grandparent. + break; + } + if (isSelfOrNonEmbeddedTask(grandParent.asTask())) { + // Found it. The activity occludes its parent TaskFragment and the parent + // TaskFragment also occludes its parent all the way up. + return true; + } + parent = grandParent; + grandParent = parent.getParent().asTaskFragment(); + } + return false; + }); return top != activity ? top : null; } + private boolean isSelfOrNonEmbeddedTask(Task task) { + if (task == this) { + return true; + } + return task != null && !task.isEmbedded(); + } + @Override public SurfaceControl.Builder makeAnimationLeash() { return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 17499060870c3..8a7688f93c2b6 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -614,17 +614,68 @@ class TaskFragment extends WindowContainer { return false; } + ActivityRecord getTopNonFinishingActivity() { + return getTopNonFinishingActivity(true /* includeOverlays */); + } + + ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) { + return getTopNonFinishingActivity(includeOverlays, true /* includingEmbeddedTask */); + } + + /** + * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to + * the current user. + * @param includeOverlays whether the task overlay activity should be included. + * @param includingEmbeddedTask whether the activity in a task that being embedded from this + * one should be included. + * @see #topRunningActivity(boolean, boolean) + * @see ActivityRecord#okToShowLocked() + */ + ActivityRecord getTopNonFinishingActivity(boolean includeOverlays, + boolean includingEmbeddedTask) { + // Split into 4 to avoid object creation due to variable capture. + if (includeOverlays) { + if (includingEmbeddedTask) { + return getActivity((r) -> !r.finishing); + } + return getActivity((r) -> !r.finishing && r.getTask() == this.getTask()); + } + + if (includingEmbeddedTask) { + return getActivity((r) -> !r.finishing && !r.isTaskOverlay()); + } + return getActivity( + (r) -> !r.finishing && !r.isTaskOverlay() && r.getTask() == this.getTask()); + } + ActivityRecord topRunningActivity() { return topRunningActivity(false /* focusableOnly */); } ActivityRecord topRunningActivity(boolean focusableOnly) { - // Split into 2 to avoid object creation due to variable capture. + return topRunningActivity(focusableOnly, true /* includingEmbeddedTask */); + } + + /** + * Returns the top-most running activity, which the activity is non-finishing and ok to show + * to the current user. + * + * @see ActivityRecord#canBeTopRunning() + */ + ActivityRecord topRunningActivity(boolean focusableOnly, boolean includingEmbeddedTask) { + // Split into 4 to avoid object creation due to variable capture. if (focusableOnly) { - return getActivity((r) -> r.canBeTopRunning() && r.isFocusable()); - } else { + if (includingEmbeddedTask) { + return getActivity((r) -> r.canBeTopRunning() && r.isFocusable()); + } + return getActivity( + (r) -> r.canBeTopRunning() && r.isFocusable() && r.getTask() == this.getTask()); + } + + if (includingEmbeddedTask) { return getActivity(ActivityRecord::canBeTopRunning); } + return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask()); } boolean isTopActivityFocusable() {