// 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;
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;
}
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
}
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);
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,
/**
* 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
if (!mChangeInProgress) {
mChangeInProgress = true;
- AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask);
+ mLock.notifyAll();
}
}
return !mChangeInProgress;
}
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();
+ // 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 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 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);
- }
+ private void setBrightness(int backlight) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "setBrightness(" + backlight + ")");
+ try {
+ mBacklight.setBrightness(backlight);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
- };
+ }
}
}
/**
* 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.
mAnimating = true;
mAnimatedValue = mCurrentValue;
mLastFrameTimeNanos = System.nanoTime();
- postCallback();
+ postAnimationCallback();
}
return changed;
mListener = listener;
}
- private void postCallback() {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mCallback, null);
+ private void postAnimationCallback() {
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
+ }
+
+ private void cancelAnimationCallback() {
+ mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
}
- private final Runnable mCallback = new Runnable() {
+ private final Runnable mAnimationCallback = new Runnable() {
@Override // Choreographer callback
public void run() {
final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
}
if (mTargetValue != mCurrentValue) {
- postCallback();
+ postAnimationCallback();
} else {
mAnimating = false;
if (mListener != null) {