diff options
author | 2012-10-22 10:36:58 -0700 | |
---|---|---|
committer | 2012-10-22 10:36:59 -0700 | |
commit | d6e3ad54907ec085aa41e5c77296e9f385c22e67 (patch) | |
tree | 4d7c33a31b75757d823c54253f606555cad0f827 | |
parent | f584f1112772fdffce13cba38995b9e28adca191 (diff) | |
parent | 32dafe25ac2e06f127f48d6a5826537e11575f52 (diff) |
Merge "Reduce screen on/off latency." into jb-mr1-dev
4 files changed, 218 insertions, 201 deletions
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index 661b949895dc..ba5a47540e2f 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -30,7 +30,6 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; import android.hardware.display.DisplayManager; -import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -44,7 +43,6 @@ import android.util.TimeUtils; import android.view.Display; import java.io.PrintWriter; -import java.util.concurrent.Executor; /** * Controls the power state of the display. @@ -161,9 +159,6 @@ final class DisplayPowerController { // Notifier for sending asynchronous notifications. private final Notifier mNotifier; - // A suspend blocker. - private final SuspendBlocker mSuspendBlocker; - // The display blanker. private final DisplayBlanker mDisplayBlanker; @@ -339,12 +334,11 @@ final class DisplayPowerController { * Creates the display power controller. */ public DisplayPowerController(Looper looper, Context context, Notifier notifier, - LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker, + LightsService lights, TwilightService twilight, DisplayBlanker displayBlanker, Callbacks callbacks, Handler callbackHandler) { mHandler = new DisplayControllerHandler(looper); mNotifier = notifier; - mSuspendBlocker = suspendBlocker; mDisplayBlanker = displayBlanker; mCallbacks = callbacks; mCallbackHandler = callbackHandler; @@ -513,14 +507,10 @@ final class DisplayPowerController { } private void initialize() { - final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR; Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); mPowerState = new DisplayPowerState( - new ElectronBeam(display), - new PhotonicModulator(executor, - mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT), - mSuspendBlocker), - mDisplayBlanker); + new ElectronBeam(display), mDisplayBlanker, + mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT)); mElectronBeamOnAnimator = ObjectAnimator.ofFloat( mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f); diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java index fdfcacca0dba..6bb7ec5946e8 100644 --- a/services/java/com/android/server/power/DisplayPowerState.java +++ b/services/java/com/android/server/power/DisplayPowerState.java @@ -16,6 +16,10 @@ package com.android.server.power; +import com.android.server.LightsService; + +import android.os.AsyncTask; +import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.util.FloatProperty; @@ -26,47 +30,55 @@ import android.view.Choreographer; import java.io.PrintWriter; /** - * Represents the current display power state and realizes it. - * + * Controls the display power state. + * <p> * This component is similar in nature to a {@link View} except that it describes * the properties of a display. When properties are changed, the component - * invalidates itself and posts a callback to the {@link Choreographer} to - * apply the changes. This mechanism enables the display power state to be - * animated smoothly by the animation framework. - * + * invalidates itself and posts a callback to apply the changes in a consistent order. + * This mechanism enables multiple properties of the display power state to be animated + * together smoothly by the animation framework. Some of the work to blank or unblank + * the display is done on a separate thread to avoid blocking the looper. + * </p><p> * This component must only be created or accessed by the {@link Looper} thread * that belongs to the {@link DisplayPowerController}. - * + * </p><p> * We don't need to worry about holding a suspend blocker here because the - * {@link DisplayPowerController} does that for us whenever there is a pending invalidate. + * {@link PowerManagerService} does that for us whenever there is a change + * in progress. + * </p> */ final class DisplayPowerState { private static final String TAG = "DisplayPowerState"; private static boolean DEBUG = false; - private static final int DIRTY_SCREEN_ON = 1 << 0; - private static final int DIRTY_ELECTRON_BEAM = 1 << 1; - private static final int DIRTY_BRIGHTNESS = 1 << 2; - + private final Handler mHandler; private final Choreographer mChoreographer; private final ElectronBeam mElectronBeam; - private final PhotonicModulator mPhotonicModulator; private final DisplayBlanker mDisplayBlanker; + private final LightsService.Light mBacklight; + private final PhotonicModulator mPhotonicModulator; - private int mDirty; private boolean mScreenOn; - private float mElectronBeamLevel; private int mScreenBrightness; + private boolean mScreenReady; + private boolean mScreenUpdatePending; + + private boolean mElectronBeamPrepared; + private float mElectronBeamLevel; + private boolean mElectronBeamReady; + private boolean mElectronBeamDrawPending; private Runnable mCleanListener; public DisplayPowerState(ElectronBeam electronBean, - PhotonicModulator photonicModulator, DisplayBlanker displayBlanker) { + DisplayBlanker displayBlanker, LightsService.Light backlight) { + mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); mElectronBeam = electronBean; - mPhotonicModulator = photonicModulator; mDisplayBlanker = displayBlanker; + mBacklight = backlight; + mPhotonicModulator = new PhotonicModulator(); // 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, @@ -75,9 +87,12 @@ final class DisplayPowerState { // will reset the brightness to a new level immediately before the changes // actually have a chance to be applied. mScreenOn = true; - mElectronBeamLevel = 1.0f; mScreenBrightness = PowerManager.BRIGHTNESS_ON; - invalidate(DIRTY_BRIGHTNESS); + scheduleScreenUpdate(); + + mElectronBeamPrepared = false; + mElectronBeamLevel = 1.0f; + mElectronBeamReady = true; } public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL = @@ -116,7 +131,8 @@ final class DisplayPowerState { } mScreenOn = on; - invalidate(DIRTY_SCREEN_ON); + mScreenReady = false; + scheduleScreenUpdate(); } } @@ -128,6 +144,32 @@ final class DisplayPowerState { } /** + * Sets the display brightness. + * + * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). + */ + public void setScreenBrightness(int brightness) { + if (mScreenBrightness != brightness) { + if (DEBUG) { + Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); + } + + mScreenBrightness = brightness; + if (mScreenOn) { + mScreenReady = false; + scheduleScreenUpdate(); + } + } + } + + /** + * Gets the screen brightness. + */ + public int getScreenBrightness() { + return mScreenBrightness; + } + + /** * Prepares the electron beam to turn on or off. * This method should be called before starting an animation because it * can take a fair amount of time to prepare the electron beam surface. @@ -136,8 +178,16 @@ final class DisplayPowerState { * @return True if the electron beam was prepared. */ public boolean prepareElectronBeam(int mode) { - invalidate(DIRTY_ELECTRON_BEAM); - return mElectronBeam.prepare(mode); + if (!mElectronBeam.prepare(mode)) { + mElectronBeamPrepared = false; + mElectronBeamReady = true; + return false; + } + + mElectronBeamPrepared = true; + mElectronBeamReady = false; + scheduleElectronBeamDraw(); + return true; } /** @@ -145,6 +195,8 @@ final class DisplayPowerState { */ public void dismissElectronBeam() { mElectronBeam.dismiss(); + mElectronBeamPrepared = false; + mElectronBeamReady = true; } /** @@ -167,7 +219,14 @@ final class DisplayPowerState { } mElectronBeamLevel = level; - invalidate(DIRTY_ELECTRON_BEAM); + if (mScreenOn) { + mScreenReady = false; + scheduleScreenUpdate(); // update backlight brightness + } + if (mElectronBeamPrepared) { + mElectronBeamReady = false; + scheduleElectronBeamDraw(); + } } } @@ -179,36 +238,13 @@ final class DisplayPowerState { } /** - * Sets the display brightness. - * - * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). - */ - public void setScreenBrightness(int brightness) { - if (mScreenBrightness != brightness) { - if (DEBUG) { - Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); - } - - mScreenBrightness = brightness; - invalidate(DIRTY_BRIGHTNESS); - } - } - - /** - * Gets the screen brightness. - */ - public int getScreenBrightness() { - return mScreenBrightness; - } - - /** * Returns true if no properties have been invalidated. * Otherwise, returns false and promises to invoke the specified listener * when the properties have all been applied. * The listener always overrides any previously set listener. */ public boolean waitUntilClean(Runnable listener) { - if (mDirty != 0) { + if (!mScreenReady || !mElectronBeamReady) { mCleanListener = listener; return false; } else { @@ -220,58 +256,159 @@ final class DisplayPowerState { public void dump(PrintWriter pw) { pw.println(); pw.println("Display Power State:"); - pw.println(" mDirty=" + Integer.toHexString(mDirty)); pw.println(" mScreenOn=" + mScreenOn); pw.println(" mScreenBrightness=" + mScreenBrightness); + pw.println(" mScreenReady=" + mScreenReady); + pw.println(" mScreenUpdatePending=" + mScreenUpdatePending); + pw.println(" mElectronBeamPrepared=" + mElectronBeamPrepared); pw.println(" mElectronBeamLevel=" + mElectronBeamLevel); + pw.println(" mElectronBeamReady=" + mElectronBeamReady); + pw.println(" mElectronBeamDrawPending=" + mElectronBeamDrawPending); + mPhotonicModulator.dump(pw); mElectronBeam.dump(pw); } - private void invalidate(int dirty) { - if (mDirty == 0) { + private void scheduleScreenUpdate() { + if (!mScreenUpdatePending) { + mScreenUpdatePending = true; + postScreenUpdateThreadSafe(); + } + } + + private void postScreenUpdateThreadSafe() { + mHandler.removeCallbacks(mScreenUpdateRunnable); + mHandler.post(mScreenUpdateRunnable); + } + + private void scheduleElectronBeamDraw() { + if (!mElectronBeamDrawPending) { + mElectronBeamDrawPending = true; mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, - mTraversalRunnable, null); + mElectronBeamDrawRunnable, null); } + } - mDirty |= dirty; + private void invokeCleanListenerIfNeeded() { + final Runnable listener = mCleanListener; + if (listener != null && mScreenReady && mElectronBeamReady) { + mCleanListener = null; + listener.run(); + } } - private void apply() { - if (mDirty != 0) { - if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) { - mPhotonicModulator.setBrightness(0, true /*sync*/); - mDisplayBlanker.blankAllDisplays(); - } + private final Runnable mScreenUpdateRunnable = new Runnable() { + @Override + public void run() { + mScreenUpdatePending = false; - if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) { - mElectronBeam.draw(mElectronBeamLevel); + if (mPhotonicModulator.setState(mScreenOn, + mScreenOn ? (int)(mScreenBrightness * mElectronBeamLevel) : 0)) { + mScreenReady = true; + invokeCleanListenerIfNeeded(); } + } + }; - if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) { - mDisplayBlanker.unblankAllDisplays(); - } + private final Runnable mElectronBeamDrawRunnable = new Runnable() { + @Override + public void run() { + mElectronBeamDrawPending = false; - if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0 - && mScreenOn) { - mPhotonicModulator.setBrightness( - (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/); + if (mElectronBeamPrepared) { + mElectronBeam.draw(mElectronBeamLevel); } - mDirty = 0; + mElectronBeamReady = true; + invokeCleanListenerIfNeeded(); + } + }; - if (mCleanListener != null) { - mCleanListener.run(); + /** + * Updates the state of the screen and backlight asynchronously on a separate thread. + */ + private final class PhotonicModulator { + private static final boolean INITIAL_SCREEN_ON = false; // unknown, assume off + private static final int INITIAL_BACKLIGHT = -1; // unknown + + private final Object mLock = new Object(); + + private boolean mPendingOn = INITIAL_SCREEN_ON; + private int mPendingBacklight = INITIAL_BACKLIGHT; + private boolean mActualOn = INITIAL_SCREEN_ON; + private int mActualBacklight = INITIAL_BACKLIGHT; + private boolean mChangeInProgress; + + public boolean setState(boolean on, int backlight) { + synchronized (mLock) { + if (on != mPendingOn || backlight != mPendingBacklight) { + if (DEBUG) { + Slog.d(TAG, "Requesting new screen state: on=" + on + + ", backlight=" + backlight); + } + + mPendingOn = on; + mPendingBacklight = backlight; + + if (!mChangeInProgress) { + mChangeInProgress = true; + AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask); + } + } + return mChangeInProgress; } } - } - private final Runnable mTraversalRunnable = new Runnable() { - @Override - public void run() { - if (mDirty != 0) { - apply(); - } + public void dump(PrintWriter pw) { + pw.println(); + pw.println("Photonic Modulator State:"); + pw.println(" mPendingOn=" + mPendingOn); + pw.println(" mPendingBacklight=" + mPendingBacklight); + pw.println(" mActualOn=" + mActualOn); + 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 boolean on; + final boolean onChanged; + final int backlight; + final boolean backlightChanged; + synchronized (mLock) { + on = mPendingOn; + onChanged = (on != mActualOn); + backlight = mPendingBacklight; + backlightChanged = (backlight != mActualBacklight); + if (!onChanged && !backlightChanged) { + mChangeInProgress = false; + break; + } + mActualOn = on; + mActualBacklight = backlight; + } + + if (DEBUG) { + Slog.d(TAG, "Updating screen state: on=" + on + + ", backlight=" + backlight); + } + if (onChanged && on) { + mDisplayBlanker.unblankAllDisplays(); + } + if (backlightChanged) { + mBacklight.setBrightness(backlight); + } + if (onChanged && !on) { + mDisplayBlanker.blankAllDisplays(); + } + } + + // Let the outer class know that all changes have been applied. + postScreenUpdateThreadSafe(); + } + }; + } } diff --git a/services/java/com/android/server/power/PhotonicModulator.java b/services/java/com/android/server/power/PhotonicModulator.java deleted file mode 100644 index 648c0c59de4f..000000000000 --- a/services/java/com/android/server/power/PhotonicModulator.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.power; - -import android.util.Slog; - -import com.android.server.LightsService; - -import java.util.concurrent.Executor; - -/** - * Sets the value of a light asynchronously. - * - * This is done to avoid blocking the looper on devices for which - * setting the backlight brightness is especially slow. - */ -final class PhotonicModulator { - private static final String TAG = "PhotonicModulator"; - private static final boolean DEBUG = false; - - private static final int UNKNOWN_LIGHT_VALUE = -1; - - private final Object mLock = new Object(); - - private final LightsService.Light mLight; - private final Executor mExecutor; - private final SuspendBlocker mSuspendBlocker; - - private boolean mPendingChange; - private int mPendingLightValue; - private int mActualLightValue; - - public PhotonicModulator(Executor executor, LightsService.Light light, - SuspendBlocker suspendBlocker) { - mExecutor = executor; - mLight = light; - mSuspendBlocker = suspendBlocker; - mPendingLightValue = UNKNOWN_LIGHT_VALUE; - mActualLightValue = UNKNOWN_LIGHT_VALUE; - } - - /** - * Sets the backlight brightness, synchronously or asynchronously. - * - * @param lightValue The new light value, from 0 to 255. - * @param sync If true, waits for the brightness change to complete before returning. - */ - public void setBrightness(int lightValue, boolean sync) { - synchronized (mLock) { - if (lightValue != mPendingLightValue) { - mPendingLightValue = lightValue; - if (DEBUG) { - Slog.d(TAG, "Enqueuing request to change brightness to " + lightValue); - } - if (!mPendingChange) { - mPendingChange = true; - mSuspendBlocker.acquire(); - mExecutor.execute(mTask); - } - } - if (sync) { - while (mPendingChange) { - try { - mLock.wait(); - } catch (InterruptedException ex) { - // ignore it - } - } - } - } - } - - private final Runnable mTask = new Runnable() { - @Override - public void run() { - for (;;) { - final int newLightValue; - synchronized (mLock) { - newLightValue = mPendingLightValue; - if (newLightValue == mActualLightValue) { - mSuspendBlocker.release(); - mPendingChange = false; - mLock.notifyAll(); - return; - } - mActualLightValue = newLightValue; - } - if (DEBUG) { - Slog.d(TAG, "Setting brightness to " + newLightValue); - } - mLight.setBrightness(newLightValue); - } - } - }; -} diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index bf81a9003ff6..8650192f1ffb 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -433,7 +433,6 @@ public final class PowerManagerService extends IPowerManager.Stub // own handler thread. mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(), mContext, mNotifier, mLightsService, twilight, - createSuspendBlockerLocked("PowerManagerService.Display"), mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler); mSettingsObserver = new SettingsObserver(mHandler); |