From 0a434776b836f8fbea1f84b7bfe158b4ddafc876 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 30 Sep 2014 14:42:27 -0700 Subject: [PATCH] Reduce latency from doze to screen on. Don't wait for the brightness ramp to complete before reporting display ready. Keep track of whether we have any unfinished brightness changes and take care to grab a wakelock to ensure they are eventually applied. Ideally we would rewrite the whole state machine to more carefully coordinate screen state and brightness changes but that's too risky for now. The pixel fairies are having a bad day. Bug: 17718416 (cherry picked from commit 875f80c2732a3fbe652a6e8fc14031041f791308) Change-Id: I7a2d8ba4591a12b773653d3dbf86c7db016f967e --- .../display/DisplayPowerController.java | 57 ++++++-- .../server/display/DisplayPowerState.java | 130 +++++++++--------- .../android/server/display/RampAnimator.java | 42 ++++-- 3 files changed, 142 insertions(+), 87 deletions(-) diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index e2a548ace4ac8..e3a25c066d9fd 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -227,6 +227,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // turning off the screen. private boolean mPendingScreenOff; + // True if we have unfinished business and are holding a suspend blocker. + private boolean mUnfinishedBusiness; + // The elapsed real time when the screen on was blocked. private long mScreenOnBlockStartRealTime; @@ -633,22 +636,42 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAppliedLowPower = true; } - // Animate the screen brightness when the screen is on. - if (state != Display.STATE_OFF) { - animateScreenBrightness(brightness, slowChange - ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST); - } - // Animate the screen state change unless already animating. animateScreenStateChange(state, performScreenOffTransition); - // Report whether the display is ready for use and all changes have been applied. - if (mustNotify - && mPendingScreenOnUnblocker == null + // Animate the screen brightness when the screen is on or dozing. + // Skip the animation when the screen is off or suspended. + final int actualState = mPowerState.getScreenState(); + if (actualState == Display.STATE_ON || actualState == Display.STATE_DOZE) { + animateScreenBrightness(brightness, + slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST); + } else { + animateScreenBrightness(brightness, 0); + } + + // Determine whether the display is ready for use in the newly requested state. + // Note that we do not wait for the brightness ramp animation to complete before + // reporting the display is ready because we only need to ensure the screen is in the + // right power state even as it continues to converge on the desired brightness. + final boolean ready = mPendingScreenOnUnblocker == null && !mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted() - && !mScreenBrightnessRampAnimator.isAnimating() - && mPowerState.waitUntilClean(mCleanListener)) { + && mPowerState.waitUntilClean(mCleanListener); + final boolean finished = ready + && !mScreenBrightnessRampAnimator.isAnimating(); + + // Grab a wake lock if we have unfinished business. + if (!finished && !mUnfinishedBusiness) { + if (DEBUG) { + Slog.d(TAG, "Unfinished business..."); + } + mCallbacks.acquireSuspendBlocker(); + mUnfinishedBusiness = true; + } + + // Notify the power manager when ready. + if (ready && mustNotify) { + // Send state change. synchronized (mLock) { if (!mPendingRequestChangedLocked) { mDisplayReadyLocked = true; @@ -660,6 +683,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } sendOnStateChangedWithWakelock(); } + + // Release the wake lock when we have no unfinished business. + if (finished && mUnfinishedBusiness) { + if (DEBUG) { + Slog.d(TAG, "Finished business..."); + } + mUnfinishedBusiness = false; + mCallbacks.releaseSuspendBlocker(); + } } @Override @@ -723,6 +755,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void animateScreenBrightness(int target, int rate) { + if (DEBUG) { + Slog.d(TAG, "Animating brightness: target=" + target +", rate=" + rate); + } if (mScreenBrightnessRampAnimator.animateTo(target, rate)) { try { mBatteryStats.noteScreenBrightness(target); diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index a7651e42107ed..fc068afd59a11 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -80,6 +80,7 @@ final class DisplayPowerState { mBacklight = backlight; mColorFade = electronBeam; mPhotonicModulator = new PhotonicModulator(); + mPhotonicModulator.start(); // At boot time, we know that the screen is on and the electron beam // animation is not playing. We don't know the screen's brightness though, @@ -336,7 +337,7 @@ final class DisplayPowerState { /** * Updates the state of the screen and backlight asynchronously on a separate thread. */ - private final class PhotonicModulator { + private final class PhotonicModulator extends Thread { private static final int INITIAL_SCREEN_STATE = Display.STATE_OFF; // unknown, assume off private static final int INITIAL_BACKLIGHT = -1; // unknown @@ -361,7 +362,7 @@ final class DisplayPowerState { if (!mChangeInProgress) { mChangeInProgress = true; - AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask); + mLock.notifyAll(); } } return !mChangeInProgress; @@ -369,75 +370,78 @@ final class DisplayPowerState { } public void dump(PrintWriter pw) { - pw.println(); - pw.println("Photonic Modulator State:"); - pw.println(" mPendingState=" + Display.stateToString(mPendingState)); - pw.println(" mPendingBacklight=" + mPendingBacklight); - pw.println(" mActualState=" + Display.stateToString(mActualState)); - pw.println(" mActualBacklight=" + mActualBacklight); - pw.println(" mChangeInProgress=" + mChangeInProgress); + synchronized (mLock) { + pw.println(); + pw.println("Photonic Modulator State:"); + pw.println(" mPendingState=" + Display.stateToString(mPendingState)); + pw.println(" mPendingBacklight=" + mPendingBacklight); + pw.println(" mActualState=" + Display.stateToString(mActualState)); + pw.println(" mActualBacklight=" + mActualBacklight); + pw.println(" mChangeInProgress=" + mChangeInProgress); + } } - private final Runnable mTask = new Runnable() { - @Override - public void run() { - // Apply pending changes until done. - for (;;) { - final int state; - final boolean stateChanged; - final int backlight; - final boolean backlightChanged; - synchronized (mLock) { - state = mPendingState; - stateChanged = (state != mActualState); - backlight = mPendingBacklight; - backlightChanged = (backlight != mActualBacklight); - if (!stateChanged && !backlightChanged) { - mChangeInProgress = false; - break; - } - mActualState = state; - mActualBacklight = backlight; - } - - if (DEBUG) { - Slog.d(TAG, "Updating screen state: state=" - + Display.stateToString(state) + ", backlight=" + backlight); - } - boolean suspending = Display.isSuspendedState(state); - if (stateChanged && !suspending) { - requestDisplayState(state); - } - if (backlightChanged) { - setBrightness(backlight); - } - if (stateChanged && suspending) { - requestDisplayState(state); + @Override + public void run() { + for (;;) { + // Get pending change. + final int state; + final boolean stateChanged; + final int backlight; + final boolean backlightChanged; + synchronized (mLock) { + state = mPendingState; + stateChanged = (state != mActualState); + backlight = mPendingBacklight; + backlightChanged = (backlight != mActualBacklight); + if (!stateChanged && !backlightChanged) { + // All changed applied, notify outer class and wait for more. + mChangeInProgress = false; + postScreenUpdateThreadSafe(); + try { + mLock.wait(); + } catch (InterruptedException ex) { } + continue; } + mActualState = state; + mActualBacklight = backlight; } - // Let the outer class know that all changes have been applied. - postScreenUpdateThreadSafe(); - } - - private void requestDisplayState(int state) { - Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" - + Display.stateToString(state) + ")"); - try { - mBlanker.requestDisplayState(state); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); + // Apply pending change. + if (DEBUG) { + Slog.d(TAG, "Updating screen state: state=" + + Display.stateToString(state) + ", backlight=" + backlight); + } + boolean suspending = Display.isSuspendedState(state); + if (stateChanged && !suspending) { + requestDisplayState(state); + } + if (backlightChanged) { + setBrightness(backlight); + } + if (stateChanged && suspending) { + requestDisplayState(state); } } + } - private void setBrightness(int backlight) { - Trace.traceBegin(Trace.TRACE_TAG_POWER, "setBrightness(" + backlight + ")"); - try { - mBacklight.setBrightness(backlight); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); - } + private void requestDisplayState(int state) { + Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" + + Display.stateToString(state) + ")"); + try { + mBlanker.requestDisplayState(state); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } - }; + } + + private void setBrightness(int backlight) { + Trace.traceBegin(Trace.TRACE_TAG_POWER, "setBrightness(" + backlight + ")"); + try { + mBacklight.setBrightness(backlight); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } + } } } diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java index ad1e85717ab17..d71269f9eaeb1 100644 --- a/services/core/java/com/android/server/display/RampAnimator.java +++ b/services/core/java/com/android/server/display/RampAnimator.java @@ -50,20 +50,32 @@ final class RampAnimator { /** * Starts animating towards the specified value. * - * If this is the first time the property is being set, the value jumps - * directly to the target. + * If this is the first time the property is being set or if the rate is 0, + * the value jumps directly to the target. * * @param target The target value. - * @param rate The convergence rate, in units per second. + * @param rate The convergence rate in units per second, or 0 to set the value immediately. * @return True if the target differs from the previous target. */ public boolean animateTo(int target, int rate) { // Immediately jump to the target the first time. - if (mFirstTime) { - mFirstTime = false; - mProperty.setValue(mObject, target); - mCurrentValue = target; - return true; + if (mFirstTime || rate <= 0) { + if (mFirstTime || target != mCurrentValue) { + mFirstTime = false; + mRate = 0; + mTargetValue = target; + mCurrentValue = target; + mProperty.setValue(mObject, target); + if (mAnimating) { + mAnimating = false; + cancelAnimationCallback(); + } + if (mListener != null) { + mListener.onAnimationEnd(); + } + return true; + } + return false; } // Adjust the rate based on the closest target. @@ -88,7 +100,7 @@ final class RampAnimator { mAnimating = true; mAnimatedValue = mCurrentValue; mLastFrameTimeNanos = System.nanoTime(); - postCallback(); + postAnimationCallback(); } return changed; @@ -108,11 +120,15 @@ final class RampAnimator { mListener = listener; } - private void postCallback() { - mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mCallback, null); + private void postAnimationCallback() { + mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null); } - private final Runnable mCallback = new Runnable() { + private void cancelAnimationCallback() { + mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null); + } + + private final Runnable mAnimationCallback = new Runnable() { @Override // Choreographer callback public void run() { final long frameTimeNanos = mChoreographer.getFrameTimeNanos(); @@ -144,7 +160,7 @@ final class RampAnimator { } if (mTargetValue != mCurrentValue) { - postCallback(); + postAnimationCallback(); } else { mAnimating = false; if (mListener != null) {