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

Commit d26851d9 authored by Craig Mautner's avatar Craig Mautner Committed by Android (Google) Code Review
Browse files

Merge "Refactor resetTaskIfNeededLocked for tasks."

parents ff4fcdb9 e3a74d5d
Loading
Loading
Loading
Loading
+338 −2
Original line number Diff line number Diff line
@@ -2219,7 +2219,7 @@ final class ActivityStack {
     * Perform a reset of the given task, if needed as part of launching it.
     * Returns the new HistoryRecord at the top of the task.
     */
    private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
/*    private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
            ActivityRecord newActivity) {
        mLastHistoryModifier = "resetTaskIfNeededLocked";

@@ -2563,6 +2563,342 @@ final class ActivityStack {
        }
        return taskTop;
    }
*/
    /**
     * Helper method for #resetTaskIfNeededLocked.
     * We are inside of the task being reset...  we'll either finish this activity, push it out
     * for another task, or leave it as-is.
     * @param task The task containing the Activity (taskTop) that might be reset.
     * @param forceReset
     * @return An ActivityOptions that needs to be processed.
     */
    private final ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task,
            boolean forceReset) {
        ActivityOptions topOptions = null;

        int replyChainEnd = -1;
        boolean canMoveOptions = true;

        // We only do this for activities that are not the root of the task (since if we finish
        // the root, we may no longer have the task!).
        final ArrayList<ActivityRecord> activities = task.mActivities;
        final int numActivities = activities.size();
        for (int i = numActivities - 1; i > 0; --i ) {
            ActivityRecord target = activities.get(i);

            final int flags = target.info.flags;
            final boolean finishOnTaskLaunch =
                    (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
            final boolean allowTaskReparenting =
                    (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;
            final boolean clearWhenTaskReset =
                    (target.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0;

            if (!finishOnTaskLaunch
                    && !clearWhenTaskReset
                    && target.resultTo != null) {
                // If this activity is sending a reply to a previous
                // activity, we can't do anything with it now until
                // we reach the start of the reply chain.
                // XXX note that we are assuming the result is always
                // to the previous activity, which is almost always
                // the case but we really shouldn't count on.
                if (replyChainEnd < 0) {
                    replyChainEnd = i;
                }
            } else if (!finishOnTaskLaunch
                    && !clearWhenTaskReset
                    && allowTaskReparenting
                    && target.taskAffinity != null
                    && !target.taskAffinity.equals(task.affinity)) {
                // If this activity has an affinity for another
                // task, then we need to move it out of here.  We will
                // move it as far out of the way as possible, to the
                // bottom of the activity stack.  This also keeps it
                // correctly ordered with any activities we previously
                // moved.
                TaskRecord bottomTask = mTaskHistory.get(0);
                ActivityRecord p = bottomTask.mActivities.get(0);
                if (target.taskAffinity != null
                        && target.taskAffinity.equals(p.task.affinity)) {
                    // If the activity currently at the bottom has the
                    // same task affinity as the one we are moving,
                    // then merge it into the same task.
                    if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
                        "resetTaskFoundIntended: would reparenting " + target + " to bottom " + p.task);
                    target.setTask(p.task, p.thumbHolder, false);
                    if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
                            + " out to bottom task " + p.task);
                } else {
                    do {
                        mService.mCurTask++;
                        if (mService.mCurTask <= 0) {
                            mService.mCurTask = 1;
                        }
                    } while (mTaskIdToTaskRecord.get(mService.mCurTask) != null);
                    target.setTask(createTaskRecord(mService.mCurTask, target.info, null, false),
                            null, false);
                    target.task.affinityIntent = target.intent;
                    if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
                            + " out to new task " + target.task);
                }

                final TaskRecord targetTask = target.task;
                final int targetTaskId = targetTask.taskId;
                mService.mWindowManager.setAppGroupId(target.appToken, targetTaskId);

                ThumbnailHolder curThumbHolder = target.thumbHolder;
                boolean gotOptions = !canMoveOptions;

                final int start = replyChainEnd < 0 ? i : replyChainEnd;
                for (int srcPos = start; srcPos >= i; --srcPos) {
                    p = activities.get(srcPos);
                    if (p.finishing) {
                        continue;
                    }

                    curThumbHolder = p.thumbHolder;
                    canMoveOptions = false;
                    if (!gotOptions && topOptions == null) {
                        topOptions = p.takeOptionsLocked();
                        if (topOptions != null) {
                            gotOptions = true;
                        }
                    }
                    if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing activity " + p + " from task="
                            + task + " adding to task=" + targetTask,
                            new RuntimeException("here").fillInStackTrace());
                    if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
                            + " out to target's task " + target.task);
                    p.setTask(targetTask, curThumbHolder, false);
                    targetTask.addActivityAtBottom(p);
                    mHistory.remove(p);
                    mHistory.add(0, p);
                    mService.mWindowManager.setAppGroupId(p.appToken, targetTaskId);
                }

                mService.mWindowManager.moveTaskToBottom(targetTaskId);
                if (VALIDATE_TOKENS) {
                    validateAppTokensLocked();
                }

                replyChainEnd = -1;
            } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
                // If the activity should just be removed -- either
                // because it asks for it, or the task should be
                // cleared -- then finish it and anything that is
                // part of its reply chain.
                int end;
                if (clearWhenTaskReset) {
                    // In this case, we want to finish this activity
                    // and everything above it, so be sneaky and pretend
                    // like these are all in the reply chain.
                    end = numActivities - 1;
                } else if (replyChainEnd < 0) {
                    end = i;
                } else {
                    end = replyChainEnd;
                }
                ActivityRecord p = null;
                boolean gotOptions = !canMoveOptions;
                for (int srcPos = i; srcPos <= end; srcPos++) {
                    p = activities.get(srcPos);
                    if (p.finishing) {
                        continue;
                    }
                    canMoveOptions = false;
                    if (!gotOptions && topOptions == null) {
                        topOptions = p.takeOptionsLocked();
                        if (topOptions != null) {
                            gotOptions = true;
                        }
                    }
                    if (DEBUG_TASKS || VALIDATE_TASK_REPLACE) Slog.w(TAG,
                            "resetTaskIntendedTask: would call finishActivity on " + p);
                    if (finishActivityLocked(p, -1, Activity.RESULT_CANCELED, null, "reset",
                            false)) {
                        end--;
                        srcPos--;
                    }
                }
                replyChainEnd = -1;
            } else {
                // If we were in the middle of a chain, well the
                // activity that started it all doesn't want anything
                // special, so leave it all as-is.
                replyChainEnd = -1;
            }
        }

        return topOptions;
    }

    /**
     * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given
     * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop.
     * @param affinityTask The task we are looking for an affinity to.
     * @param task Task that resetTaskIfNeededLocked.taskTop belongs to.
     * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked.
     * @param forceReset Flag passed in to resetTaskIfNeededLocked.
     */
    private final void resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task,
            boolean topTaskIsHigher, boolean forceReset
            , ActivityRecord taskTop
            ) {
        int replyChainEnd = -1;
        final int taskId = task.taskId;
        final String taskAffinity = task.affinity;

        final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
        final int numActivities = activities.size();
        // Do not operate on the root Activity.
        for (int i = numActivities - 1; i > 0; --i) {
            ActivityRecord target = activities.get(i);

            final int flags = target.info.flags;
            boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0;
            boolean allowTaskReparenting = (flags & ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0;

            if (target.resultTo != null) {
                // If this activity is sending a reply to a previous
                // activity, we can't do anything with it now until
                // we reach the start of the reply chain.
                // XXX note that we are assuming the result is always
                // to the previous activity, which is almost always
                // the case but we really shouldn't count on.
                if (replyChainEnd < 0) {
                    replyChainEnd = i;
                }
            } else if (topTaskIsHigher
                    && allowTaskReparenting
                    && taskAffinity != null
                    && taskAffinity.equals(target.taskAffinity)) {
                // This activity has an affinity for our task. Either remove it if we are
                // clearing or move it over to our task.  Note that
                // we currently punt on the case where we are resetting a
                // task that is not at the top but who has activities above
                // with an affinity to it...  this is really not a normal
                // case, and we will need to later pull that task to the front
                // and usually at that point we will do the reset and pick
                // up those remaining activities.  (This only happens if
                // someone starts an activity in a new task from an activity
                // in a task that is not currently on top.)
                if (forceReset || finishOnTaskLaunch) {
                    final int start = replyChainEnd >= 0 ? replyChainEnd : i;
                    if (DEBUG_TASKS) Slog.v(TAG, "Finishing task at index " + start + " to " + i);
                    for (int srcPos = start; srcPos >= i; --srcPos) {
                        final ActivityRecord p = activities.get(srcPos);
                        if (p.finishing) {
                            continue;
                        }
                        if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
                                "resetAffinityTaskIfNeededLocked: calling finishActivity on " + p);
                        finishActivityLocked(p, -1, Activity.RESULT_CANCELED, null, "reset",
                                false);
                    }
                } else {
                    int taskTopI = mHistory.indexOf(taskTop);
                    final int end = replyChainEnd >= 0 ? replyChainEnd : i;
                    if (DEBUG_TASKS) Slog.v(TAG, "Reparenting task at index " + i + " to " + end);
                    for (int srcPos = i; srcPos <= end; ++srcPos) {
                        final ActivityRecord p = activities.get(srcPos);
                        if (p.finishing) {
                            continue;
                        }
                        p.setTask(task, null, false);
                        task.addActivityToTop(p);

                        mHistory.remove(p);
                        mHistory.add(taskTopI--, p);

                        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + p
                                + " to stack at " + task,
                                new RuntimeException("here").fillInStackTrace());
                        if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " from " + srcPos
                                + " in to resetting task " + task);
                        mService.mWindowManager.setAppGroupId(p.appToken, taskId);
                    }
                    mService.mWindowManager.moveTaskToTop(taskId);
                    if (VALIDATE_TOKENS) {
                        validateAppTokensLocked();
                    }
                    if (VALIDATE_TASK_REPLACE) {
                        verifyActivityRecords(false);
                    }

                    // Now we've moved it in to place...  but what if this is
                    // a singleTop activity and we have put it on top of another
                    // instance of the same activity?  Then we drop the instance
                    // below so it remains singleTop.
                    if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
                        ArrayList<ActivityRecord> taskActivities = task.mActivities;
                        boolean found = false;
                        int targetNdx = taskActivities.indexOf(target);
                        if (targetNdx > 0) {
                            ActivityRecord p = taskActivities.get(targetNdx - 1);
                            if (p.intent.getComponent().equals(target.intent.getComponent())) {
                                if (finishActivityLocked(p, -1, Activity.RESULT_CANCELED, null,
                                        "replace", false)) {
                                    taskTopI--;
                                }
                            }
                        }
                    }
                }

                replyChainEnd = -1;
            }
        }
    }

    private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
            ActivityRecord newActivity) {
        boolean forceReset =
                (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
        if (ACTIVITY_INACTIVE_RESET_TIME > 0
                && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) {
            if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) {
                forceReset = true;
            }
        }

        final TaskRecord task = taskTop.task;

        /** False until we evaluate the TaskRecord associated with taskTop. Switches to true
         * for remaining tasks. Used for later tasks to reparent to task. */
        boolean taskFound = false;

        /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */
        ActivityOptions topOptions = null;

        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
            final TaskRecord targetTask = mTaskHistory.get(i);

            if (targetTask == task) {
                topOptions = resetTargetTaskIfNeededLocked(task, forceReset);
                taskFound = true;
            } else {
                resetAffinityTaskIfNeededLocked(targetTask, task, taskFound, forceReset, taskTop);
            }
        }

        taskTop = task.getTopActivity();
        if (topOptions != null) {
            // If we got some ActivityOptions from an activity on top that
            // was removed from the task, propagate them to the new real top.
            if (taskTop != null) {
                if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
                    "newResetTaskIfNeededLocked: would call updateOptionsLocked " + topOptions);
                taskTop.updateOptionsLocked(topOptions);
            } else {
                if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
                    "newResetTaskIfNeededLocked: would call " + topOptions + " abort");
                topOptions.abort();
            }
        }

        return taskTop;
    }

    /**
     * Perform clear operation as requested by