diff options
4 files changed, 124 insertions, 36 deletions
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 8b21d98045dd..800155d4305e 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -790,15 +790,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void overrideDozeScreenState(int displayState, @Display.StateReason int reason) { Slog.i(TAG, "New offload doze override: " + Display.stateToString(displayState)); - mHandler.postAtTime(() -> { - if (mDisplayOffloadSession == null - || !(DisplayOffloadSession.isSupportedOffloadState(displayState) - || displayState == Display.STATE_UNKNOWN)) { - return; + if (mDisplayOffloadSession != null + && DisplayOffloadSession.isSupportedOffloadState(displayState) + && displayState != Display.STATE_UNKNOWN) { + if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) { + boolean acquired = mWakelockController.acquireWakelock( + WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE); + if (!acquired) { + Slog.i(TAG, "A request to override the doze screen state is already " + + "under process"); + return; + } } - mDisplayStateController.overrideDozeScreenState(displayState, reason); - updatePowerState(); - }, mClock.uptimeMillis()); + mHandler.postAtTime(() -> { + mDisplayStateController.overrideDozeScreenState(displayState, reason); + updatePowerState(); + if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) { + mWakelockController.releaseWakelock( + WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE); + } + }, mClock.uptimeMillis()); + } } @Override @@ -1325,30 +1337,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN); } - if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) { - // Sometimes, a display-state change can come without an associated PowerRequest, - // as with DisplayOffload. For those cases, we have to make sure to also mark the - // display as "not ready" so that we can inform power-manager when the state-change is - // complete. - if (mPowerState.getScreenState() != state) { - final boolean wasReady; - synchronized (mLock) { - wasReady = mDisplayReadyLocked; - mDisplayReadyLocked = false; - mustNotify = true; - } - - if (wasReady) { - // If we went from ready to not-ready from the state-change (instead of a - // PowerRequest) there's a good chance that nothing is keeping PowerManager - // from suspending. Grab the unfinished business suspend blocker to keep the - // device awake until the display-state change goes into effect. - mWakelockController.acquireWakelock( - WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); - } - } - } - // Animate the screen state change unless already animating. // The transition may be deferred, so after this point we will use the // actual state instead of the desired one. diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java index 7bc797125b0e..5b0229cbb393 100644 --- a/services/core/java/com/android/server/display/WakelockController.java +++ b/services/core/java/com/android/server/display/WakelockController.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.hardware.display.DisplayManagerInternal; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.utils.DebugUtils; @@ -37,7 +38,8 @@ public final class WakelockController { public static final int WAKE_LOCK_PROXIMITY_NEGATIVE = 2; public static final int WAKE_LOCK_PROXIMITY_DEBOUNCE = 3; public static final int WAKE_LOCK_STATE_CHANGED = 4; - public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 5; + public static final int WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE = 5; + public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 6; @VisibleForTesting static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS; @@ -53,18 +55,23 @@ public final class WakelockController { WAKE_LOCK_PROXIMITY_NEGATIVE, WAKE_LOCK_PROXIMITY_DEBOUNCE, WAKE_LOCK_STATE_CHANGED, + WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE, WAKE_LOCK_UNFINISHED_BUSINESS }) @Retention(RetentionPolicy.SOURCE) public @interface WAKE_LOCK_TYPE { } + private final Object mLock = new Object(); + // Asynchronous callbacks into the power manager service. // Only invoked from the handler thread while no locks are held. private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks; // Identifiers for suspend blocker acquisition requests private final String mSuspendBlockerIdUnfinishedBusiness; + @GuardedBy("mLock") + private final String mSuspendBlockerOverrideDozeScreenState; private final String mSuspendBlockerIdOnStateChanged; private final String mSuspendBlockerIdProxPositive; private final String mSuspendBlockerIdProxNegative; @@ -73,6 +80,10 @@ public final class WakelockController { // True if we have unfinished business and are holding a suspend-blocker. private boolean mUnfinishedBusiness; + // True if we have are holding a suspend-blocker to override the doze screen state. + @GuardedBy("mLock") + private boolean mIsOverrideDozeScreenStateAcquired; + // True if we have have debounced the proximity change impact and are holding a suspend-blocker. private boolean mHasProximityDebounced; @@ -108,6 +119,7 @@ public final class WakelockController { mTag = TAG + "[" + mDisplayId + "]"; mDisplayPowerCallbacks = callbacks; mSuspendBlockerIdUnfinishedBusiness = "[" + displayId + "]unfinished business"; + mSuspendBlockerOverrideDozeScreenState = "[" + displayId + "]override doze screen state"; mSuspendBlockerIdOnStateChanged = "[" + displayId + "]on state changed"; mSuspendBlockerIdProxPositive = "[" + displayId + "]prox positive"; mSuspendBlockerIdProxNegative = "[" + displayId + "]prox negative"; @@ -154,6 +166,10 @@ public final class WakelockController { return acquireProxDebounceSuspendBlocker(); case WAKE_LOCK_STATE_CHANGED: return acquireStateChangedSuspendBlocker(); + case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE: + synchronized (mLock) { + return acquireOverrideDozeScreenStateSuspendBlockerLocked(); + } case WAKE_LOCK_UNFINISHED_BUSINESS: return acquireUnfinishedBusinessSuspendBlocker(); default: @@ -171,6 +187,10 @@ public final class WakelockController { return releaseProxDebounceSuspendBlocker(); case WAKE_LOCK_STATE_CHANGED: return releaseStateChangedSuspendBlocker(); + case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE: + synchronized (mLock) { + return releaseOverrideDozeScreenStateSuspendBlockerLocked(); + } case WAKE_LOCK_UNFINISHED_BUSINESS: return releaseUnfinishedBusinessSuspendBlocker(); default: @@ -220,6 +240,42 @@ public final class WakelockController { } /** + * Acquires the suspend blocker to override the doze screen state and notifies the + * PowerManagerService about the changes. Note that this utility is syncronized because a + * request to override the doze screen state can come from a non-power thread. + */ + @GuardedBy("mLock") + private boolean acquireOverrideDozeScreenStateSuspendBlockerLocked() { + // Grab a wake lock if we have unfinished business. + if (!mIsOverrideDozeScreenStateAcquired) { + if (DEBUG) { + Slog.d(mTag, "Acquiring suspend blocker to override the doze screen state..."); + } + mDisplayPowerCallbacks.acquireSuspendBlocker(mSuspendBlockerOverrideDozeScreenState); + mIsOverrideDozeScreenStateAcquired = true; + return true; + } + return false; + } + + /** + * Releases the override doze screen state suspend blocker and notifies the PowerManagerService + * about the changes. + */ + @GuardedBy("mLock") + private boolean releaseOverrideDozeScreenStateSuspendBlockerLocked() { + if (mIsOverrideDozeScreenStateAcquired) { + if (DEBUG) { + Slog.d(mTag, "Finished overriding doze screen state..."); + } + mDisplayPowerCallbacks.releaseSuspendBlocker(mSuspendBlockerOverrideDozeScreenState); + mIsOverrideDozeScreenStateAcquired = false; + return true; + } + return false; + } + + /** * Acquires the unfinished business wakelock and notifies the PowerManagerService about the * changes. */ @@ -366,6 +422,7 @@ public final class WakelockController { pw.println(" mOnStateChangePending=" + isOnStateChangedPending()); pw.println(" mOnProximityPositiveMessages=" + isProximityPositiveAcquired()); pw.println(" mOnProximityNegativeMessages=" + isProximityNegativeAcquired()); + pw.println(" mIsOverrideDozeScreenStateAcquired=" + isOverrideDozeScreenStateAcquired()); } @VisibleForTesting @@ -394,6 +451,13 @@ public final class WakelockController { } @VisibleForTesting + String getSuspendBlockerOverrideDozeScreenState() { + synchronized (mLock) { + return mSuspendBlockerOverrideDozeScreenState; + } + } + + @VisibleForTesting boolean hasUnfinishedBusiness() { return mUnfinishedBusiness; } @@ -417,4 +481,11 @@ public final class WakelockController { boolean hasProximitySensorDebounced() { return mHasProximityDebounced; } + + @VisibleForTesting + boolean isOverrideDozeScreenStateAcquired() { + synchronized (mLock) { + return mIsOverrideDozeScreenStateAcquired; + } + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 8ed38a6d0cad..a7c8d314bcc2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1589,16 +1589,21 @@ public final class DisplayPowerControllerTest { advanceTime(1); // Run updatePowerState reset(mHolder.wakelockController); + when(mHolder.wakelockController + .acquireWakelock(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE)) + .thenReturn(true); mHolder.dpc.overrideDozeScreenState( supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY); - advanceTime(1); // Run updatePowerState // Should get a wakelock to notify powermanager - verify(mHolder.wakelockController, atLeastOnce()).acquireWakelock( - eq(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS)); + verify(mHolder.wakelockController).acquireWakelock( + eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE)); + advanceTime(1); // Run updatePowerState verify(mHolder.displayPowerState) .setScreenState(supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY); + verify(mHolder.wakelockController).releaseWakelock( + eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE)); } @Test diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java index c23d4b19cd3a..019b70ef1424 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java @@ -64,6 +64,8 @@ public final class WakelockControllerTest { "[" + DISPLAY_ID + "]prox negative"); assertEquals(mWakelockController.getSuspendBlockerProxDebounceId(), "[" + DISPLAY_ID + "]prox debounce"); + assertEquals(mWakelockController.getSuspendBlockerOverrideDozeScreenState(), + "[" + DISPLAY_ID + "]override doze screen state"); } @Test @@ -162,6 +164,28 @@ public final class WakelockControllerTest { } @Test + public void acquireOverrideDozeScreenStateSuspendBlocker() throws Exception { + // Acquire the suspend blocker + verifyWakelockAcquisitionAndReaquisition(WakelockController + .WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE, + () -> mWakelockController.isOverrideDozeScreenStateAcquired()); + + // Verify acquire happened only once + verify(mDisplayPowerCallbacks, times(1)) + .acquireSuspendBlocker(mWakelockController + .getSuspendBlockerOverrideDozeScreenState()); + + // Release the suspend blocker + verifyWakelockReleaseAndRerelease(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE, + () -> mWakelockController.isOverrideDozeScreenStateAcquired()); + + // Verify suspend blocker was released only once + verify(mDisplayPowerCallbacks, times(1)) + .releaseSuspendBlocker(mWakelockController + .getSuspendBlockerOverrideDozeScreenState()); + } + + @Test public void proximityPositiveRunnableWorksAsExpected() { // Acquire the suspend blocker twice assertTrue(mWakelockController.acquireWakelock( |