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

Commit 85cc1844 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Avoid race of checking screen states and sleep token

The callback *GoingToSleep and screenTurn* run on different threads,
so there could be a race if screen on/off in a short time and the
thread has some delays:
Thread1 gets isScreenOnEarly==false
Thread2 modifies isScreenOnEarly=true
Thread2 releases sleep-token
Thread1 acquire sleep-token
Then the final state is screen on but sleep-token is still acquired.

This change moves the fail-safe check from startedGoingToSleep
to finishedGoingToSleep>setAwake(false) and reuse the existing
SleepTokenAcquirer, so it is easier to acquire/release in
DisplayPolicy inside a lock. Now the check of isScreenOnEarly and
sleep-token in screenTurningOn and setAwake are synchronized.

Bug: 364760760
Flag: EXEMPT bugfix
Test: PhoneWindowManagerTests
Change-Id: I5c47b4a745fcd369d3ec825c41a7c1d35ce9feca
parent 2f4e0476
Loading
Loading
Loading
Loading
+3 −26
Original line number Diff line number Diff line
@@ -529,7 +529,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    volatile boolean mRequestedOrSleepingDefaultDisplay;

    /**
     * This is used to check whether to invoke {@link #updateScreenOffSleepToken} when screen is
     * This is used to check whether to acquire screen-off sleep token when screen is
     * turned off. E.g. if it is false when screen is turned off and the display is swapping, it
     * is expected that the screen will be on in a short time. Then it is unnecessary to acquire
     * screen-off-sleep-token, so it can avoid intermediate visibility or lifecycle changes.
@@ -604,7 +604,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    private boolean mPendingKeyguardOccluded;
    private boolean mKeyguardOccludedChanged;

    private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
    Intent mHomeIntent;
    Intent mCarDockIntent;
    Intent mDeskDockIntent;
@@ -2217,9 +2216,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mLockPatternUtils = new LockPatternUtils(mContext);
        mLogger = new MetricsLogger();

        mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
                .createSleepTokenAcquirer("ScreenOff");

        Resources res = mContext.getResources();
        mWakeOnDpadKeyPress =
                res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
@@ -5526,13 +5522,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mRequestedOrSleepingDefaultDisplay = true;
        mIsGoingToSleepDefaultDisplay = true;

        // In case startedGoingToSleep is called after screenTurnedOff (the source caller is in
        // order but the methods run on different threads) and updateScreenOffSleepToken was
        // skipped. Then acquire sleep token if screen was off.
        if (!mDefaultDisplayPolicy.isScreenOnFully() && !mDefaultDisplayPolicy.isScreenOnEarly()) {
            updateScreenOffSleepToken(true /* acquire */);
        }

        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
        }
@@ -5693,11 +5682,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");

        if (displayId == DEFAULT_DISPLAY) {
            if (!isSwappingDisplay || mIsGoingToSleepDefaultDisplay) {
                updateScreenOffSleepToken(true /* acquire */);
            }
            final boolean acquireSleepToken = !isSwappingDisplay || mIsGoingToSleepDefaultDisplay;
            mRequestedOrSleepingDefaultDisplay = false;
            mDefaultDisplayPolicy.screenTurnedOff();
            mDefaultDisplayPolicy.screenTurnedOff(acquireSleepToken);
            synchronized (mLock) {
                if (mKeyguardDelegate != null) {
                    mKeyguardDelegate.onScreenTurnedOff();
@@ -5753,7 +5740,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        if (displayId == DEFAULT_DISPLAY) {
            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
                    0 /* cookie */);
            updateScreenOffSleepToken(false /* acquire */);
            mDefaultDisplayPolicy.screenTurningOn(screenOnListener);
            mBootAnimationDismissable = false;

@@ -6260,15 +6246,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    }

    // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
    private void updateScreenOffSleepToken(boolean acquire) {
        if (acquire) {
            mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY);
        } else {
            mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY);
        }
    }

    /** {@inheritDoc} */
    @Override
    public void enableScreenAfterBoot() {
+0 −29
Original line number Diff line number Diff line
@@ -129,35 +129,6 @@ public abstract class ActivityTaskManagerInternal {
        void onKeyguardStateChanged(boolean isShowing);
    }

    /**
     * Sleep tokens cause the activity manager to put the top activity to sleep.
     * They are used by components such as dreams that may hide and block interaction
     * with underlying activities.
     * The Acquirer provides an interface that encapsulates the underlying work, so the user does
     * not need to handle the token by him/herself.
     */
    public interface SleepTokenAcquirer {

        /**
         * Acquires a sleep token.
         * @param displayId The display to apply to.
         */
        void acquire(int displayId);

        /**
         * Releases the sleep token.
         * @param displayId The display to apply to.
         */
        void release(int displayId);
    }

    /**
     * Creates a sleep token acquirer for the specified display with the specified tag.
     *
     * @param tag A string identifying the purpose (eg. "Dream").
     */
    public abstract SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag);

    /**
     * Returns home activity for the specified user.
     *
+5 −11
Original line number Diff line number Diff line
@@ -4349,6 +4349,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            mTaskOrganizerController.dump(pw, "  ");
            mVisibleActivityProcessTracker.dump(pw, "  ");
            mActiveUids.dump(pw, "  ");
            pw.println("  SleepTokens=" + mRootWindowContainer.mSleepTokens);
            if (mDemoteTopAppReasons != 0) {
                pw.println("  mDemoteTopAppReasons=" + mDemoteTopAppReasons);
            }
@@ -5064,17 +5065,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        EventLogTags.writeWmSetResumedActivity(r.mUserId, r.shortComponentName, reason);
    }

    final class SleepTokenAcquirerImpl implements ActivityTaskManagerInternal.SleepTokenAcquirer {
    final class SleepTokenAcquirer {
        private final String mTag;
        private final SparseArray<RootWindowContainer.SleepToken> mSleepTokens =
                new SparseArray<>();

        SleepTokenAcquirerImpl(@NonNull String tag) {
        SleepTokenAcquirer(@NonNull String tag) {
            mTag = tag;
        }

        @Override
        public void acquire(int displayId) {
        void acquire(int displayId) {
            synchronized (mGlobalLock) {
                if (!mSleepTokens.contains(displayId)) {
                    mSleepTokens.append(displayId,
@@ -5084,8 +5084,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            }
        }

        @Override
        public void release(int displayId) {
        void release(int displayId) {
            synchronized (mGlobalLock) {
                final RootWindowContainer.SleepToken token = mSleepTokens.get(displayId);
                if (token != null) {
@@ -5948,11 +5947,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    }

    final class LocalService extends ActivityTaskManagerInternal {
        @Override
        public SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag) {
            Objects.requireNonNull(tag);
            return new SleepTokenAcquirerImpl(tag);
        }

        @Override
        public ComponentName getHomeActivityForUser(int userId) {
+2 −5
Original line number Diff line number Diff line
@@ -738,8 +738,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp

    /** All tokens used to put activities on this root task to sleep (including mOffToken) */
    final ArrayList<RootWindowContainer.SleepToken> mAllSleepTokens = new ArrayList<>();
    /** The token acquirer to put root tasks on the display to sleep */
    private final ActivityTaskManagerInternal.SleepTokenAcquirer mOffTokenAcquirer;

    private boolean mSleeping;

@@ -1134,7 +1132,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        mDisplay = display;
        mDisplayId = display.getDisplayId();
        mCurrentUniqueDisplayId = display.getUniqueId();
        mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
        mWallpaperController = new WallpaperController(mWmService, this);
        mWallpaperController.resetLargestDisplay(display);
        display.getDisplayInfo(mDisplayInfo);
@@ -6167,9 +6164,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        final int displayState = mDisplayInfo.state;
        if (displayId != DEFAULT_DISPLAY) {
            if (displayState == Display.STATE_OFF) {
                mOffTokenAcquirer.acquire(mDisplayId);
                mRootWindowContainer.mDisplayOffTokenAcquirer.acquire(mDisplayId);
            } else if (displayState == Display.STATE_ON) {
                mOffTokenAcquirer.release(mDisplayId);
                mRootWindowContainer.mDisplayOffTokenAcquirer.release(mDisplayId);
            }
            ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
                    "Content Recording: Display %d state was (%d), is now (%d), so update "
+14 −1
Original line number Diff line number Diff line
@@ -804,6 +804,14 @@ public class DisplayPolicy {
                    mAwake /* waiting */);
            if (!awake) {
                onDisplaySwitchFinished();
                // In case PhoneWindowManager's startedGoingToSleep is called after screenTurnedOff
                // (the source caller is in order but the methods run on different threads) and
                // updateScreenOffSleepToken was skipped by mIsGoingToSleepDefaultDisplay. Then
                // acquire sleep token if screen is off.
                if (!mScreenOnEarly && !mScreenOnFully && !mDisplayContent.isSleeping()) {
                    Slog.w(TAG, "Late acquire sleep token for " + mDisplayContent);
                    mService.mRoot.mDisplayOffTokenAcquirer.acquire(mDisplayContent.mDisplayId);
                }
            }
        }
    }
@@ -851,6 +859,7 @@ public class DisplayPolicy {
    public void screenTurningOn(ScreenOnListener screenOnListener) {
        WindowProcessController visibleDozeUiProcess = null;
        synchronized (mLock) {
            mService.mRoot.mDisplayOffTokenAcquirer.release(mDisplayContent.mDisplayId);
            mScreenOnEarly = true;
            mScreenOnFully = false;
            mKeyguardDrawComplete = false;
@@ -875,8 +884,12 @@ public class DisplayPolicy {
        onDisplaySwitchFinished();
    }

    public void screenTurnedOff() {
    /** It is called after {@link #screenTurningOn}. This runs on PowerManager's thread. */
    public void screenTurnedOff(boolean acquireSleepToken) {
        synchronized (mLock) {
            if (acquireSleepToken) {
                mService.mRoot.mDisplayOffTokenAcquirer.acquire(mDisplayContent.mDisplayId);
            }
            mScreenOnEarly = false;
            mScreenOnFully = false;
            mKeyguardDrawComplete = false;
Loading