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

Commit 88e11081 authored by Andrii Kulian's avatar Andrii Kulian Committed by Android (Google) Code Review
Browse files

Merge changes from topic "multi-resume_with_fix"

* changes:
  Don't make active activities that are launched behind
  Multiple resumed activities
parents 5e3e101e 996df0db
Loading
Loading
Loading
Loading
+10 −6
Original line number Diff line number Diff line
@@ -558,22 +558,26 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
    }

    /**
     * Pause all activities in either all of the stacks or just the back stacks.
     * Pause all activities in either all of the stacks or just the back stacks. This is done before
     * resuming a new activity and to make sure that previously active activities are
     * paused in stacks that are no longer visible or in pinned windowing mode. This does not
     * pause activities in visible stacks, so if an activity is launched within the same stack/task,
     * then we should explicitly pause that stack's top activity.
     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
     * @param resuming The resuming activity.
     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
     *                 before resuming.
     * @return true if any activity was paused as a result of this call.
     * @return {@code true} if any activity was paused as a result of this call.
     */
    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
        boolean someActivityPaused = false;
        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityStack stack = mStacks.get(stackNdx);
            // TODO(b/111541062): Check if resumed activity on this display instead
            if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
                    && stack.getResumedActivity() != null) {
            final ActivityRecord resumedActivity = stack.getResumedActivity();
            if (resumedActivity != null
                    && (!stack.shouldBeVisible(resuming) || !stack.isFocusable())) {
                if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
                        " mResumedActivity=" + stack.getResumedActivity());
                        " mResumedActivity=" + resumedActivity);
                someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
                        dontWait);
            }
+78 −18
Original line number Diff line number Diff line
@@ -1946,30 +1946,90 @@ final class ActivityRecord extends ConfigurationContainer {
        try {
            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                    WindowVisibilityItem.obtain(true /* showWindow */));
            if (shouldPauseWhenBecomingVisible()) {
            makeActiveIfNeeded(null /* activeActivity*/);
        } catch (Exception e) {
            Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
        }
    }

    /**
     * Make activity resumed or paused if needed.
     * @param activeActivity an activity that is resumed or just completed pause action.
     *                       We won't change the state of this activity.
     */
    boolean makeActiveIfNeeded(ActivityRecord activeActivity) {
        if (shouldResumeActivity(activeActivity)) {
            if (DEBUG_VISIBILITY) {
                Slog.v("TAG_VISIBILITY", "Resume visible activity, " + this);
            }
            return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
                    null /* options */);
        } else if (shouldPauseActivity(activeActivity)) {
            if (DEBUG_VISIBILITY) {
                Slog.v("TAG_VISIBILITY", "Pause visible activity, " + this);
            }
            // An activity must be in the {@link PAUSING} state for the system to validate
            // the move to {@link PAUSED}.
            setState(PAUSING, "makeVisibleIfNeeded");
            try {
                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                        PauseActivityItem.obtain(finishing, false /* userLeaving */,
                                configChangeFlags, false /* dontReport */));
            }
            } catch (Exception e) {
            Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
                Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
            }
        }
        return false;
    }

    /**
     * Check if activity should be moved to PAUSED state. The activity:
     * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
     * - should be non-focusable
     * - should not be currently pausing or paused
     * @param activeActivity the activity that is active or just completed pause action. We won't
     *                       resume if this activity is active.
     */
    private boolean shouldPauseActivity(ActivityRecord activeActivity) {
        return shouldMakeActive(activeActivity) && !isFocusable() && !isState(PAUSING, PAUSED);
    }

    /**
     * Check if activity should be moved to RESUMED state. The activity:
     * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
     * - should be focusable
     * @param activeActivity the activity that is active or just completed pause action. We won't
     *                       resume if this activity is active.
     */
    private boolean shouldResumeActivity(ActivityRecord activeActivity) {
        return shouldMakeActive(activeActivity) && isFocusable() && !isState(RESUMED);
    }

    /** Check if activity should be moved to PAUSED state when it becomes visible. */
    private boolean shouldPauseWhenBecomingVisible() {
        // If the activity is stopped or stopping, cycle to the paused state. We avoid doing
    /**
     * Check if activity is eligible to be made active (resumed of paused). The activity:
     * - should be paused, stopped or stopping
     * - should not be the currently active one or launching behind other tasks
     * - should be either the topmost in task, or right below the top activity that is finishing
     * If all of these conditions are not met at the same time, the activity cannot be made active.
     */
    private boolean shouldMakeActive(ActivityRecord activeActivity) {
        // If the activity is stopped, stopping, cycle to an active state. We avoid doing
        // this when there is an activity waiting to become translucent as the extra binder
        // calls will lead to noticeable jank. A later call to
        // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper
        // paused state. We also avoid doing this for the activity the stack supervisor
        // considers the resumed activity, as normal means will bring the activity from STOPPED
        // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
        if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null
                || isResumedActivityOnDisplay()) {
        // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to a proper
        // active state.
        if (!isState(RESUMED, PAUSED, STOPPED, STOPPING)
                || getActivityStack().mTranslucentActivityWaiting != null) {
            return false;
        }

        if (this == activeActivity) {
            return false;
        }

        if (this.mLaunchTaskBehind) {
            // This activity is being launched from behind, which means that it's not intended to be
            // presented to user right now, even if it's set to be visible.
            return false;
        }

@@ -1979,14 +2039,14 @@ final class ActivityRecord extends ConfigurationContainer {
            throw new IllegalStateException("Activity not found in its task");
        }
        if (positionInTask == task.mActivities.size() - 1) {
            // It's the topmost activity in the task - should become paused now
            // It's the topmost activity in the task - should become resumed now
            return true;
        }
        // Check if activity above is finishing now and this one becomes the topmost in task.
        final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
        if (activityAbove.finishing && results == null) {
            // We will only allow pausing if activity above wasn't launched for result. Otherwise it
            // will cause this activity to resume before getting result.
            // We will only allow making active if activity above wasn't launched for result.
            // Otherwise it will cause this activity to resume before getting result.
            return true;
        }
        return false;
+31 −8
Original line number Diff line number Diff line
@@ -357,6 +357,11 @@ class ActivityStack extends ConfigurationContainer {
     */
    boolean mForceHidden = false;

    /**
     * Used to keep resumeTopActivityUncheckedLocked() from being entered recursively
     */
    boolean mInResumeTopActivity = false;

    private boolean mUpdateBoundsDeferred;
    private boolean mUpdateBoundsDeferredCalled;
    private boolean mUpdateDisplayedBoundsDeferredCalled;
@@ -1732,6 +1737,7 @@ class ActivityStack extends ConfigurationContainer {
            "Activity paused: token=" + token + ", timeout=" + timeout);

        final ActivityRecord r = isInStackLocked(token);

        if (r != null) {
            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
            if (mPausingActivity == r) {
@@ -2088,8 +2094,7 @@ class ActivityStack extends ConfigurationContainer {
            boolean aboveTop = top != null;
            final boolean stackShouldBeVisible = shouldBeVisible(starting);
            boolean behindFullscreenActivity = !stackShouldBeVisible;
            boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
                    && (isInStackLocked(starting) == null);
            boolean resumeNextActivity = isFocusable() && isInStackLocked(starting) == null;
            final boolean isTopNotPinnedStack =
                    isAttached() && getDisplay().isTopNotPinnedStack(this);
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -2150,6 +2155,10 @@ class ActivityStack extends ConfigurationContainer {
                            if (r.handleAlreadyVisible()) {
                                resumeNextActivity = false;
                            }

                            if (notifyClients) {
                                r.makeActiveIfNeeded(starting);
                            }
                        } else {
                            r.makeVisibleIfNeeded(starting, notifyClients);
                        }
@@ -2327,7 +2336,7 @@ class ActivityStack extends ConfigurationContainer {
                r.setVisible(true);
            }
            if (r != starting) {
                mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
                mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
                return true;
            }
        }
@@ -2505,7 +2514,7 @@ class ActivityStack extends ConfigurationContainer {
     */
    @GuardedBy("mService")
    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mStackSupervisor.inResumeTopActivity) {
        if (mInResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }
@@ -2513,7 +2522,7 @@ class ActivityStack extends ConfigurationContainer {
        boolean result = false;
        try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true;
            mInResumeTopActivity = true;
            result = resumeTopActivityInnerLocked(prev, options);

            // When resuming the top activity, it may be necessary to pause the top activity (for
@@ -2528,7 +2537,7 @@ class ActivityStack extends ConfigurationContainer {
                checkReadyForSleep();
            }
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
            mInResumeTopActivity = false;
        }

        return result;
@@ -2561,7 +2570,7 @@ class ActivityStack extends ConfigurationContainer {
        // Find the next top-most activity to resume in this stack that is not finishing and is
        // focusable. If it is not focusable, we will fall into the case below to resume the
        // top activity in the next focusable task.
        final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
        ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);

        final boolean hasRunningActivity = next != null;

@@ -2649,6 +2658,12 @@ class ActivityStack extends ConfigurationContainer {
        if (!mRootActivityContainer.allPausedActivitiesComplete()) {
            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                    "resumeTopActivityLocked: Skip resume: some activity pausing.");

            // Adding previous activity to the waiting visible list, or it would be stopped
            // before top activity being visible.
            if (prev != null && !next.nowVisible) {
                mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
            }
            return false;
        }

@@ -2858,7 +2873,9 @@ class ActivityStack extends ConfigurationContainer {
            // the screen based on the new activity order.
            boolean notUpdated = true;

            if (isFocusedStackOnDisplay()) {
            // Activity should also be visible if set mLaunchTaskBehind to true (see
            // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
            if (shouldBeVisible(next)) {
                // We have special rotation behavior when here is some active activity that
                // requests specific orientation or Keyguard is locked. Make sure all activity
                // visibilities are set correctly as well as the transition is updated if needed
@@ -4087,6 +4104,12 @@ class ActivityStack extends ConfigurationContainer {
        mStackSupervisor.mFinishingActivities.add(r);
        r.resumeKeyDispatchingLocked();
        mRootActivityContainer.resumeFocusedStacksTopActivities();
        // If activity was not paused at this point - explicitly pause it to start finishing
        // process. Finishing will be completed once it reports pause back.
        if (r.isState(RESUMED) && mPausingActivity != null) {
            startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */,
                    false /* dontWait */);
        }
        return r;
    }

+0 −3
Original line number Diff line number Diff line
@@ -327,9 +327,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
     */
    PowerManager.WakeLock mGoingToSleep;

    /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
    boolean inResumeTopActivity;

    /**
     * Temporary rect used during docked stack resize calculation so we don't need to create a new
     * object each time.
+4 −1
Original line number Diff line number Diff line
@@ -179,7 +179,10 @@ public class ActivityStartController {
                .setActivityOptions(options.toBundle())
                .execute();
        mLastHomeActivityStartRecord = tmpOutRecord[0];
        if (mSupervisor.inResumeTopActivity) {
        final ActivityDisplay display =
                mService.mRootActivityContainer.getActivityDisplay(displayId);
        final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
        if (homeStack != null && homeStack.mInResumeTopActivity) {
            // If we are in resume section already, home activity will be initialized, but not
            // resumed (to avoid recursive resume) and will stay that way until something pokes it
            // again. We need to schedule another resume.
Loading