Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77805422 authored by Louis Chang's avatar Louis Chang
Browse files

Prevent the launching activity be reparented into embedded task

- Fixing the issue by ignoring the activities in the embedded task
  while searching for the task top activity.

- Also making the embedded tasks to be the candidate while
  looking for a reusable task for the launching activity.

- Updated Task#getOccludingActivityAbove that the activity
  on the same TaskFragment or the activity bounds occludes
  all of the parents up to a non-embedded leaf task.

Bug: 194315692
Test: start activity while another task embedded

Change-Id: Ib8c435ce5a212070d9fe1711c539501d394c33ae
parent 3acc328b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -2779,7 +2779,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;
        }

+19 −1
Original line number Diff line number Diff line
@@ -365,8 +365,26 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
                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);
+44 −10
Original line number Diff line number Diff line
@@ -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);
+54 −3
Original line number Diff line number Diff line
@@ -607,17 +607,68 @@ class TaskFragment extends WindowContainer<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) {
            if (includingEmbeddedTask) {
                return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
        } else {
            }
            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() {