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

Commit a24da298 authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

Merge "Remove token behind fullscreen activity from unknown visibility"

parents 0e7de3bb 13c27ae9
Loading
Loading
Loading
Loading
+36 −26
Original line number Diff line number Diff line
@@ -4439,34 +4439,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        removeStartingWindow();
    }

    void notifyUnknownVisibilityLaunched() {

        // No display activities never add a window, so there is no point in waiting them for
        // relayout.
        if (!noDisplay && getDisplayContent() != null) {
            getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(this);
        }
    }

    /**
     * @return true if the input activity should be made visible, ignoring any effect Keyguard
     * might have on the visibility
     *
     * TODO(b/123540470): Combine this method and {@link #shouldBeVisible(boolean)}.
     *
     * @see {@link ActivityStack#checkKeyguardVisibility}
     * Suppress transition until the new activity becomes ready, otherwise the keyguard can appear
     * for a short amount of time before the new process with the new activity had the ability to
     * set its showWhenLocked flags.
     */
    boolean shouldBeVisibleIgnoringKeyguard(boolean behindFullscreenActivity) {
        if (!okToShowLocked()) {
            return false;
    void notifyUnknownVisibilityLaunchedForKeyguardTransition() {
        // No display activities never add a window, so there is no point in waiting them for
        // relayout.
        if (noDisplay || !mStackSupervisor.getKeyguardController().isKeyguardLocked()) {
            return;
        }

        return !behindFullscreenActivity || mLaunchTaskBehind;
        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(this);
    }

    boolean shouldBeVisible(boolean behindFullscreenActivity) {
    /** @return {@code true} if this activity should be made visible. */
    boolean shouldBeVisible(boolean behindFullscreenActivity, boolean ignoringKeyguard) {
        // Check whether activity should be visible without Keyguard influence
        visibleIgnoringKeyguard = shouldBeVisibleIgnoringKeyguard(behindFullscreenActivity);
        visibleIgnoringKeyguard = (!behindFullscreenActivity || mLaunchTaskBehind)
                && okToShowLocked();

        if (ignoringKeyguard) {
            return visibleIgnoringKeyguard;
        }

        final ActivityStack stack = getActivityStack();
        if (stack == null) {
@@ -4495,9 +4491,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            return false;
        }

        // TODO: Use real value of behindFullscreenActivity calculated using the same logic in
        // ActivityStack#ensureActivitiesVisibleLocked().
        return shouldBeVisible(!stack.shouldBeVisible(null /* starting */));
        final boolean behindFullscreenActivity = stack.checkBehindFullscreenActivity(
                this, null /* handleBehindFullscreenActivity */);
        return shouldBeVisible(behindFullscreenActivity, false /* ignoringKeyguard */);
    }

    void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) {
@@ -5538,12 +5534,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
    }

    void removeOrphanedStartingWindow(boolean behindFullscreenActivity) {
        if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
    /**
     * If any activities below the top running one are in the INITIALIZING state and they have a
     * starting window displayed then remove that starting window. It is possible that the activity
     * in this state will never resumed in which case that starting window will be orphaned.
     * <p>
     * It should only be called if this activity is behind other fullscreen activity.
     */
    void cancelInitializing() {
        if (mStartingWindowState == STARTING_WINDOW_SHOWN) {
            // Remove orphaned starting window.
            if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
            mStartingWindowState = STARTING_WINDOW_REMOVED;
            removeStartingWindow();
        }
        if (isState(INITIALIZING) && !shouldBeVisible(
                true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) {
            // Remove the unknown visibility record because an invisible activity shouldn't block
            // the keyguard transition.
            mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
        }
    }

    void postWindowRemoveStartingWindowCleanup(WindowState win) {
+46 −24
Original line number Diff line number Diff line
@@ -162,6 +162,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

/**
 * State and management of a single stack of activities.
@@ -2118,14 +2119,21 @@ class ActivityStack extends ConfigurationContainer {
                    }
                    aboveTop = false;

                    final boolean reallyVisible = r.shouldBeVisible(behindFullscreenActivity,
                            false /* ignoringKeyguard */);
                    // Check whether activity should be visible without Keyguard influence
                    final boolean visibleIgnoringKeyguard = r.shouldBeVisibleIgnoringKeyguard(
                            behindFullscreenActivity);
                    final boolean reallyVisible = r.shouldBeVisible(behindFullscreenActivity);
                    if (visibleIgnoringKeyguard) {
                        behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
                                behindFullscreenActivity, r);
                    if (r.visibleIgnoringKeyguard) {
                        if (r.occludesParent()) {
                            // At this point, nothing else needs to be shown in this task.
                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
                                    + " stackVisible=" + stackShouldBeVisible
                                    + " behindFullscreen=" + behindFullscreenActivity);
                            behindFullscreenActivity = true;
                        } else {
                            behindFullscreenActivity = false;
                        }
                    }

                    if (reallyVisible) {
                        if (r.finishing) {
                            continue;
@@ -2338,18 +2346,6 @@ class ActivityStack extends ConfigurationContainer {
        return false;
    }

    private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
            ActivityRecord r) {
        if (r.occludesParent()) {
            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
                        + " stackInvisible=" + stackInvisible
                        + " behindFullscreenActivity=" + behindFullscreenActivity);
            // At this point, nothing else needs to be shown in this task.
            behindFullscreenActivity = true;
        }
        return behindFullscreenActivity;
    }

    void convertActivityToTranslucent(ActivityRecord r) {
        mTranslucentActivityWaiting = r;
        mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -2400,14 +2396,22 @@ class ActivityStack extends ConfigurationContainer {
        }
    }

    /** If any activities below the top running one are in the INITIALIZING state and they have a
     * starting window displayed then remove that starting window. It is possible that the activity
     * in this state will never resumed in which case that starting window will be orphaned. */
    /** @see ActivityRecord#cancelInitializing() */
    void cancelInitializingActivities() {
        final ActivityRecord topActivity = topRunningActivityLocked();
        boolean aboveTop = true;
        // We don't want to clear starting window for activities that aren't behind fullscreen
        // activities as we need to display their starting window until they are done initializing.
        checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
    }

    /**
     * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
     * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
     * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
     * activities to the function.
     */
    boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
            Consumer<ActivityRecord> handleBehindFullscreenActivity) {
        boolean aboveTop = true;
        boolean behindFullscreenActivity = false;

        if (!shouldBeVisible(null)) {
@@ -2417,22 +2421,40 @@ class ActivityStack extends ConfigurationContainer {
            behindFullscreenActivity = true;
        }

        final boolean handlingOccluded = toCheck == null && handleBehindFullscreenActivity != null;
        if (!handlingOccluded && behindFullscreenActivity) {
            return true;
        }

        final ActivityRecord topActivity = topRunningActivityLocked();
        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
            final TaskRecord task = mTaskHistory.get(taskNdx);
            for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
                final ActivityRecord r = task.getChildAt(activityNdx);
                if (aboveTop) {
                    if (r == topActivity) {
                        if (r == toCheck) {
                            // It is the top activity in a visible stack.
                            return false;
                        }
                        aboveTop = false;
                    }
                    behindFullscreenActivity |= r.occludesParent();
                    continue;
                }

                r.removeOrphanedStartingWindow(behindFullscreenActivity);
                if (handlingOccluded) {
                    handleBehindFullscreenActivity.accept(r);
                } else if (r == toCheck) {
                    return behindFullscreenActivity;
                } else if (behindFullscreenActivity) {
                    // It is occluded before {@param toCheck} is found.
                    return true;
                }
                behindFullscreenActivity |= r.occludesParent();
            }
        }
        return behindFullscreenActivity;
    }

    /**
+2 −9
Original line number Diff line number Diff line
@@ -752,9 +752,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
                andResume = false;
            }

            if (getKeyguardController().isKeyguardLocked()) {
                r.notifyUnknownVisibilityLaunched();
            }
            r.notifyUnknownVisibilityLaunchedForKeyguardTransition();

            // Have the window manager re-evaluate the orientation of the screen based on the new
            // activity order.  Note that as a result of this, it can call back into the activity
@@ -992,12 +990,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
            knownToBeDead = true;
        }

        // Suppress transition until the new activity becomes ready, otherwise the keyguard can
        // appear for a short amount of time before the new process with the new activity had the
        // ability to set its showWhenLocked flags.
        if (getKeyguardController().isKeyguardLocked()) {
            r.notifyUnknownVisibilityLaunched();
        }
        r.notifyUnknownVisibilityLaunchedForKeyguardTransition();

        final boolean isTop = andResume && r.isTopRunningActivity();
        mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
+39 −1
Original line number Diff line number Diff line
@@ -1139,13 +1139,51 @@ public class ActivityStackTests extends ActivityTestsBase {
        assertThat(result).isEqualTo(taskTop);
    }

    @Test
    public void testClearUnknownAppVisibilityBehindFullscreenActivity() {
        final UnknownAppVisibilityController unknownAppVisibilityController =
                mDefaultDisplay.mDisplayContent.mUnknownAppVisibilityController;
        final KeyguardController keyguardController = mSupervisor.getKeyguardController();
        doReturn(true).when(keyguardController).isKeyguardLocked();

        // Start 2 activities that their processes have not yet started.
        final ActivityRecord[] activities = new ActivityRecord[2];
        mSupervisor.beginDeferResume();
        for (int i = 0; i < activities.length; i++) {
            final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
            activities[i] = r;
            doReturn(null).when(mService).getProcessController(
                    eq(r.processName), eq(r.info.applicationInfo.uid));
            r.setState(ActivityStack.ActivityState.INITIALIZING, "test");
            // Ensure precondition that the activity is opaque.
            assertTrue(r.occludesParent());
            mSupervisor.startSpecificActivityLocked(r, false /* andResume */,
                    false /* checkConfig */);
        }
        mSupervisor.endDeferResume();

        doReturn(false).when(mService).isBooting();
        doReturn(true).when(mService).isBooted();
        // 2 activities are started while keyguard is locked, so they are waiting to be resolved.
        assertFalse(unknownAppVisibilityController.allResolved());

        // Assume the top activity is going to resume and
        // {@link RootActivityContainer#cancelInitializingActivities} should clear the unknown
        // visibility records that are occluded.
        mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
        // Assume the top activity relayouted, just remove it directly.
        unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
        // All unresolved records should be removed.
        assertTrue(unknownAppVisibilityController.allResolved());
    }

    @Test
    public void testNonTopVisibleActivityNotResume() {
        final ActivityRecord nonTopVisibleActivity =
                new ActivityBuilder(mService).setTask(mTask).build();
        new ActivityBuilder(mService).setTask(mTask).build();
        doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
        doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleIgnoringKeyguard(anyBoolean());
        doReturn(true).when(nonTopVisibleActivity).shouldBeVisible(anyBoolean(), anyBoolean());
        doNothing().when(mSupervisor).startSpecificActivityLocked(any(), anyBoolean(),
                anyBoolean());