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

Commit bba6bedf authored by wilsonshih's avatar wilsonshih
Browse files

Submit visibility to the activities after the sleep state stable

ActivityRecord#shouldBeVisible can be affected by sleep status.
So in the original ensureActivityVisibility sequence:

beginActivityVisibilityUpdate
  ensureActivityVisibility =>
    ActivityRecord#shouldBeVisible =>
      commit visiblity to the activity if changed
endActivityVisibilityUpdate
KeyguardController#visibilitiesUpdated

Then if the occluding state has changed, execute above sequence again,
which could change the visibility again, so previous loop become
useless. Sometimes the loop cause issue like b/161036653.

An idea is to let KeyguardController to handle
TopActivityOccludesKeyguard and TopDismissingKeyguardActivity together
to update display sleep state. And only commit the visibility to
activities after sleep status is stable.

In this CL the ensureActivityVisibility sequence become
beginActivityVisibilityUpdate
  ensureActivityVisibility =>
    ActivityRecord#updateVisibilityStatus
endActivityVisibilityUpdate
KeyguardController#visibilitiesUpdated

if occluding state has changed, execute ensureActivityVisibility again.
Then after all sleep status has changed, commit the visibility to
activity.

Bug: 163993448
Test: atest ActivityVisibilityTests KeyguardTests KeyguardLockedTests
KeyguardTransitionTests AppConfigurationTests ReplaceWindowTests
Test: atest ActivityRecordTests RecentTasksTest ActivityStackTests
DisplayContentTests

Change-Id: Ifa69ce5cc8069966c6bf2b23bad56e4b2bd286ba
parent 401dc73a
Loading
Loading
Loading
Loading
+31 −21
Original line number Diff line number Diff line
@@ -4628,18 +4628,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(this);
    }

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

    /** @return {@code true} if this activity should be made visible. */
    private boolean shouldBeVisible(boolean behindFullscreenActivity, boolean ignoringKeyguard) {
        updateVisibleIgnoringKeyguard(behindFullscreenActivity);

        if (ignoringKeyguard) {
            return visibleIgnoringKeyguard;
        }

        return shouldBeVisibleUnchecked();
    }

    boolean shouldBeVisibleUnchecked() {
        final Task stack = getRootTask();
        if (stack == null) {
        if (stack == null || !visibleIgnoringKeyguard) {
            return false;
        }

@@ -4652,26 +4660,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            return false;
        }

        // Check if the activity is on a sleeping display, and if it can turn it ON.
        if (mDisplayContent.isSleeping()) {
            final boolean canTurnScreenOn = !mSetToSleep || canTurnScreenOn()
                    || canShowWhenLocked() || containsDismissKeyguardWindow();
            if (!canTurnScreenOn) {
        // Check if the activity is on a sleeping display
        // TODO b/163993448 mSetToSleep is required when restarting an existing activity, try to
        // remove it if possible.
        if (mSetToSleep && mDisplayContent.isSleeping()) {
            return false;
        }

        return mStackSupervisor.getKeyguardController().checkKeyguardVisibility(this);
    }

    void updateVisibility(boolean behindFullscreenActivity) {
        updateVisibleIgnoringKeyguard(behindFullscreenActivity);
        final Task task = getRootTask();
        if (task == null || !visibleIgnoringKeyguard) {
            return;
        }
        // Now check whether it's really visible depending on Keyguard state, and update
        // {@link ActivityStack} internal states.
        // Inform the method if this activity is the top activity of this stack, but exclude the
        // case where this is the top activity in a pinned stack.
        final boolean isTop = this == stack.getTopNonFinishingActivity();
        final boolean isTopNotPinnedStack = stack.isAttached()
                && stack.getDisplayArea().isTopNotFinishNotPinnedStack(stack);
        final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this,
                visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);

        return visibleIgnoringDisplayStatus;
        final boolean isTop = this == task.getTopNonFinishingActivity();
        final boolean isTopNotPinnedStack = task.isAttached()
                && task.getDisplayArea().isTopNotFinishNotPinnedStack(task);
        task.updateKeyguardVisibility(this, isTop && isTopNotPinnedStack);
    }

    boolean shouldBeVisible() {
@@ -7538,10 +7550,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            return false;
        }
        final Task stack = getRootTask();
        return stack != null
                && !stack.inMultiWindowMode()
                && stack.checkKeyguardVisibility(this, true /* shouldBeVisible */,
                        stack.topRunningActivity() == this /* isTop */);
        return stack != null && !stack.inMultiWindowMode()
                && mStackSupervisor.getKeyguardController().checkKeyguardVisibility(this);
    }

    void setTurnScreenOn(boolean turnScreenOn) {
+8 −5
Original line number Diff line number Diff line
@@ -759,8 +759,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
            }

            if (r.getRootTask().checkKeyguardVisibility(r, true /* shouldBeVisible */,
                    true /* isTop */) && r.allowMoveToFront()) {
            if (mKeyguardController.checkKeyguardVisibility(r) && r.allowMoveToFront()) {
                // We only set the visibility to true if the activity is not being launched in
                // background, and is allowed to be visible based on keyguard state. This avoids
                // setting this into motion in window manager that is later cancelled due to later
@@ -2298,11 +2297,15 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
    }

    /** Ends a batch of visibility updates. */
    void endActivityVisibilityUpdate() {
        mVisibilityTransactionDepth--;
        if (mVisibilityTransactionDepth == 0) {
    void endActivityVisibilityUpdate(ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean notifyClients) {
        if (mVisibilityTransactionDepth == 1) {
            getKeyguardController().visibilitiesUpdated();
            // commit visibility to activities
            mRootWindowContainer.commitActivitiesVisible(starting, configChanges, preserveWindows,
                    notifyClients);
        }
        mVisibilityTransactionDepth--;
    }

    /** Returns {@code true} if the caller is on the path to update visibility. */
+9 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static android.os.Build.VERSION_CODES.N;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
@@ -5293,6 +5294,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        mSingleTaskInstance = true;
    }

    /**
     * Check if the display has {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
     */
    boolean canShowWithInsecureKeyguard() {
        final int flags = mDisplay.getFlags();
        return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
    }

    /** Returns true if the display can only contain one task */
    boolean isSingleTaskInstance() {
        return mSingleTaskInstance;
+78 −46
Original line number Diff line number Diff line
@@ -22,12 +22,9 @@ import static com.android.server.wm.Task.TAG_VISIBILITY;
import android.annotation.Nullable;
import android.util.Slog;

import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;

/** Helper class to ensure activities are in the right visible state for a container. */
class EnsureActivitiesVisibleHelper {
    private final Task mContiner;
    private final Task mTask;
    private ActivityRecord mTop;
    private ActivityRecord mStarting;
    private boolean mAboveTop;
@@ -38,11 +35,11 @@ class EnsureActivitiesVisibleHelper {
    private boolean mNotifyClients;

    EnsureActivitiesVisibleHelper(Task container) {
        mContiner = container;
        mTask = container;
    }

    /**
     * Update all attributes except {@link mContiner} to use in subsequent calculations.
     * Update all attributes except {@link mTask} to use in subsequent calculations.
     *
     * @param starting The activity that is being started
     * @param configChanges Parts of the configuration that changed for this activity for evaluating
@@ -54,11 +51,11 @@ class EnsureActivitiesVisibleHelper {
    void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
            boolean notifyClients) {
        mStarting = starting;
        mTop = mContiner.topRunningActivity();
        mTop = mTask.topRunningActivity();
        // If the top activity is not fullscreen, then we need to make sure any activities under it
        // are now visible.
        mAboveTop = mTop != null;
        mContainerShouldBeVisible = mContiner.shouldBeVisible(mStarting);
        mContainerShouldBeVisible = mTask.shouldBeVisible(mStarting);
        mBehindFullscreenActivity = !mContainerShouldBeVisible;
        mConfigChanges = configChanges;
        mPreserveWindows = preserveWindows;
@@ -66,7 +63,26 @@ class EnsureActivitiesVisibleHelper {
    }

    /**
     * Ensure visibility with an option to also update the configuration of visible activities.
     * Update visibility to activities.
     * @see Task#ensureActivitiesVisible(ActivityRecord, int, boolean)
     * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
     * @param starting The top most activity in the task.
     *                 The activity is either starting or resuming.
     *                 Caller should ensure starting activity is visible.
     *
     */
    void processUpdate(@Nullable ActivityRecord starting) {
        reset(starting, 0 /* configChanges */, false /* preserveWindows */,
                false /* notifyClients */);
        if (DEBUG_VISIBILITY) {
            Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible processUpdate behind " + mTop);
        }

        mTask.forAllActivities(this::updateActivityVisibility);
    }

    /**
     * Commit visibility with an option to also update the configuration of visible activities.
     * @see Task#ensureActivitiesVisible(ActivityRecord, int, boolean)
     * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
     * @param starting The top most activity in the task.
@@ -79,54 +95,84 @@ class EnsureActivitiesVisibleHelper {
     * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
     *                      be sent to the clients.
     */
    void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
            boolean notifyClients) {
    void processCommit(ActivityRecord starting, int configChanges,
            boolean preserveWindows, boolean notifyClients) {
        reset(starting, configChanges, preserveWindows, notifyClients);

        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
                + " configChanges=0x" + Integer.toHexString(configChanges));
        if (DEBUG_VISIBILITY) {
            Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible processCommit behind " + mTop);
        }
        if (mTop != null) {
            mContiner.checkTranslucentActivityWaiting(mTop);
            mTask.checkTranslucentActivityWaiting(mTop);
        }

        // We should not resume activities that being launched behind because these
        // activities are actually behind other fullscreen activities, but still required
        // to be visible (such as performing Recents animation).
        final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
                && mContiner.isTopActivityFocusable()
                && (starting == null || !starting.isDescendantOf(mContiner));
                && mTask.isTopActivityFocusable()
                && (starting == null || !starting.isDescendantOf(mTask));

        final PooledConsumer f = PooledLambda.obtainConsumer(
                EnsureActivitiesVisibleHelper::setActivityVisibilityState, this,
                PooledLambda.__(ActivityRecord.class), starting, resumeTopActivity);
        mContiner.forAllActivities(f);
        f.recycle();
        mTask.forAllActivities(a -> {
            commitActivityVisibility(a, starting, resumeTopActivity);
        });
    }

    private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
            final boolean resumeTopActivity) {
        final boolean isTop = r == mTop;
    private boolean isAboveTop(boolean isTop) {
        if (mAboveTop && !isTop) {
            return;
            return true;
        }
        mAboveTop = false;
        return false;
    }

        final boolean reallyVisible = r.shouldBeVisible(
                mBehindFullscreenActivity, false /* ignoringKeyguard */);
    private void updateActivityVisibility(ActivityRecord r) {
        final boolean isTop = r == mTop;
        if (isAboveTop(isTop)) {
            return;
        }

        r.updateVisibility(mBehindFullscreenActivity);

        // Check whether activity should be visible without Keyguard influence
        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
                if (DEBUG_VISIBILITY) {
                    Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
                            + " stackVisible=" + mContainerShouldBeVisible
                            + " behindFullscreen=" + mBehindFullscreenActivity);
                }
                mBehindFullscreenActivity = true;
            } else {
                mBehindFullscreenActivity = false;
            }
        }

        if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) {
            if (DEBUG_VISIBILITY) {
                Slog.v(TAG_VISIBILITY, "Home task: at " + mTask
                        + " stackShouldBeVisible=" + mContainerShouldBeVisible
                        + " behindFullscreenActivity=" + mBehindFullscreenActivity);
            }
            // No other task in the home stack should be visible behind the home activity.
            // Home activities is usually a translucent activity with the wallpaper behind
            // them. However, when they don't have the wallpaper behind them, we want to
            // show activities in the next application stack behind them vs. another
            // task in the home stack like recents.
            mBehindFullscreenActivity = true;
        }
    }

    private void commitActivityVisibility(ActivityRecord r, ActivityRecord starting,
            final boolean resumeTopActivity) {
        final boolean isTop = r == mTop;
        if (isAboveTop(isTop)) {
            return;
        }

        final boolean reallyVisible = r.shouldBeVisibleUnchecked();

        if (reallyVisible) {
            if (r.finishing) {
                return;
@@ -170,20 +216,6 @@ class EnsureActivitiesVisibleHelper {
                    + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
            r.makeInvisible();
        }

        final int windowingMode = mContiner.getWindowingMode();
        if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
                && r.isRootOfTask()) {
            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
                    + " stackShouldBeVisible=" + mContainerShouldBeVisible
                    + " behindFullscreenActivity=" + mBehindFullscreenActivity);
            // No other task in the home stack should be visible behind the home activity.
            // Home activities is usually a translucent activity with the wallpaper behind
            // them. However, when they don't have the wallpaper behind them, we want to
            // show activities in the next application stack behind them vs. another
            // task in the home stack like recents.
            mBehindFullscreenActivity = true;
        }
    }

    private void makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
@@ -207,7 +239,7 @@ class EnsureActivitiesVisibleHelper {
            r.setVisibility(true);
        }
        if (r != starting) {
            mContiner.mStackSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
            mTask.mStackSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
        }
    }
}
+48 −10
Original line number Diff line number Diff line
@@ -277,6 +277,27 @@ class KeyguardController {
                && !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
    }

    /**
     * Checks whether {@param r} should be visible depending on Keyguard state.
     *
     * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
     */
    boolean checkKeyguardVisibility(ActivityRecord r) {
        if (r.mDisplayContent.canShowWithInsecureKeyguard() && canDismissKeyguard()) {
            return true;
        }

        if (isKeyguardOrAodShowing(r.mDisplayContent.getDisplayId())) {
            // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
            // right away and AOD isn't visible.
            return canShowActivityWhileKeyguardShowing(r, r.containsDismissKeyguardWindow());
        } else if (isKeyguardLocked()) {
            return canShowWhileOccluded(r.containsDismissKeyguardWindow(), r.canShowWhenLocked());
        } else {
            return true;
        }
    }

    /**
     * Makes sure to update lockscreen occluded/dismiss state if needed after completing all
     * visibility updates ({@link ActivityStackSupervisor#endActivityVisibilityUpdate}).
@@ -442,6 +463,7 @@ class KeyguardController {
        private final int mDisplayId;
        private boolean mOccluded;
        private ActivityRecord mDismissingKeyguardActivity;
        private ActivityRecord mTopTurnScreenOnActivity;
        private boolean mRequestDismissKeyguard;
        private final ActivityTaskManagerService mService;
        private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
@@ -455,30 +477,38 @@ class KeyguardController {

        void onRemoved() {
            mDismissingKeyguardActivity = null;
            mTopTurnScreenOnActivity = null;
            mSleepTokenAcquirer.release(mDisplayId);
        }

        void visibilitiesUpdated(KeyguardController controller, DisplayContent display) {
            final boolean lastOccluded = mOccluded;
            final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
            final ActivityRecord lastTurnScreenOnActivity = mTopTurnScreenOnActivity;
            mRequestDismissKeyguard = false;
            mOccluded = false;
            mDismissingKeyguardActivity = null;
            mTopTurnScreenOnActivity = null;

            // only top + focusable + visible task can control occluding.
            final Task stack = getStackForControllingOccluding(display);
            if (stack != null) {
                final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
                final ActivityRecord topTurnScreenOn = stack.getTopTurnScreenOnActivity();
                mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
                        && stack.topRunningActivity() == topDismissing
                        && controller.canShowWhileOccluded(
                                true /* dismissKeyguard */,
                                false /* showWhenLocked */));
                if (stack.getTopDismissingKeyguardActivity() != null) {
                    mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
                if (topDismissing != null) {
                    mDismissingKeyguardActivity = topDismissing;
                }
                if (topTurnScreenOn != null) {
                    mTopTurnScreenOnActivity = topTurnScreenOn;
                }
                // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
                if (mDisplayId != DEFAULT_DISPLAY) {
                    mOccluded |= stack.canShowWithInsecureKeyguard()
                if (mDisplayId != DEFAULT_DISPLAY && stack.mDisplayContent != null) {
                    mOccluded |= stack.mDisplayContent.canShowWithInsecureKeyguard()
                            && controller.canDismissKeyguard();
                }
            }
@@ -488,14 +518,20 @@ class KeyguardController {
                        .getDisplayPolicy().isShowingDreamLw();
            }

            if (lastOccluded != mOccluded) {
                controller.handleOccludedChanged(mDisplayId);
            }
            if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
            mRequestDismissKeyguard = lastDismissActivity != mDismissingKeyguardActivity
                    && !mOccluded
                    && mDismissingKeyguardActivity != null
                    && controller.mWindowManager.isKeyguardSecure(
                            controller.mService.getCurrentUserId())) {
                mRequestDismissKeyguard = true;
                    controller.mService.getCurrentUserId());

            if (mTopTurnScreenOnActivity != null
                    && mTopTurnScreenOnActivity != lastTurnScreenOnActivity
                    && !mService.mWindowManager.mPowerManager.isInteractive()) {
                controller.mStackSupervisor.wakeUp("handleTurnScreenOn");
            }

            if (lastOccluded != mOccluded) {
                controller.handleOccludedChanged(mDisplayId);
            }
        }

@@ -525,6 +561,8 @@ class KeyguardController {
            sb.append("  Occluded=").append(mOccluded)
                    .append(" DismissingKeyguardActivity=")
                    .append(mDismissingKeyguardActivity)
                    .append(" TurnScreenOnActivity=")
                    .append(mTopTurnScreenOnActivity)
                    .append(" at display=")
                    .append(mDisplayId);
            pw.println(sb.toString());
Loading