diff options
Diffstat (limited to 'services/core/java/com/android/server/power/DisplayPowerState.java')
-rw-r--r-- | services/core/java/com/android/server/power/DisplayPowerState.java | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/power/DisplayPowerState.java b/services/core/java/com/android/server/power/DisplayPowerState.java new file mode 100644 index 0000000..42af4b4 --- /dev/null +++ b/services/core/java/com/android/server/power/DisplayPowerState.java @@ -0,0 +1,414 @@ +/* + * 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 com.android.server.lights.Light; + +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.util.FloatProperty; +import android.util.IntProperty; +import android.util.Slog; +import android.view.Choreographer; + +import java.io.PrintWriter; + +/** + * 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 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 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 final Handler mHandler; + private final Choreographer mChoreographer; + private final ElectronBeam mElectronBeam; + private final DisplayBlanker mDisplayBlanker; + private final Light mBacklight; + private final PhotonicModulator mPhotonicModulator; + + private boolean mScreenOn; + 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, + DisplayBlanker displayBlanker, Light backlight) { + mHandler = new Handler(true /*async*/); + mChoreographer = Choreographer.getInstance(); + mElectronBeam = electronBean; + 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, + // so prepare to set it to a known state when the state is next applied. + // Although we set the brightness to full on here, the display power controller + // will reset the brightness to a new level immediately before the changes + // actually have a chance to be applied. + mScreenOn = true; + mScreenBrightness = PowerManager.BRIGHTNESS_ON; + scheduleScreenUpdate(); + + mElectronBeamPrepared = false; + mElectronBeamLevel = 1.0f; + mElectronBeamReady = true; + } + + public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL = + new FloatProperty<DisplayPowerState>("electronBeamLevel") { + @Override + public void setValue(DisplayPowerState object, float value) { + object.setElectronBeamLevel(value); + } + + @Override + public Float get(DisplayPowerState object) { + return object.getElectronBeamLevel(); + } + }; + + public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS = + new IntProperty<DisplayPowerState>("screenBrightness") { + @Override + public void setValue(DisplayPowerState object, int value) { + object.setScreenBrightness(value); + } + + @Override + public Integer get(DisplayPowerState object) { + return object.getScreenBrightness(); + } + }; + + /** + * Sets whether the screen is on or off. + */ + public void setScreenOn(boolean on) { + if (mScreenOn != on) { + if (DEBUG) { + Slog.d(TAG, "setScreenOn: on=" + on); + } + + mScreenOn = on; + mScreenReady = false; + scheduleScreenUpdate(); + } + } + + /** + * Returns true if the screen is on. + */ + public boolean isScreenOn() { + return mScreenOn; + } + + /** + * 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. + * + * @param mode The electron beam animation mode to prepare. + * @return True if the electron beam was prepared. + */ + public boolean prepareElectronBeam(int mode) { + if (!mElectronBeam.prepare(mode)) { + mElectronBeamPrepared = false; + mElectronBeamReady = true; + return false; + } + + mElectronBeamPrepared = true; + mElectronBeamReady = false; + scheduleElectronBeamDraw(); + return true; + } + + /** + * Dismisses the electron beam surface. + */ + public void dismissElectronBeam() { + mElectronBeam.dismiss(); + mElectronBeamPrepared = false; + mElectronBeamReady = true; + } + + /** + * Sets the level of the electron beam steering current. + * + * The display is blanked when the level is 0.0. In normal use, the electron + * beam should have a value of 1.0. The electron beam is unstable in between + * these states and the picture quality may be compromised. For best effect, + * the electron beam should be warmed up or cooled off slowly. + * + * Warning: Electron beam emits harmful radiation. Avoid direct exposure to + * skin or eyes. + * + * @param level The level, ranges from 0.0 (full off) to 1.0 (full on). + */ + public void setElectronBeamLevel(float level) { + if (mElectronBeamLevel != level) { + if (DEBUG) { + Slog.d(TAG, "setElectronBeamLevel: level=" + level); + } + + mElectronBeamLevel = level; + if (mScreenOn) { + mScreenReady = false; + scheduleScreenUpdate(); // update backlight brightness + } + if (mElectronBeamPrepared) { + mElectronBeamReady = false; + scheduleElectronBeamDraw(); + } + } + } + + /** + * Gets the level of the electron beam steering current. + */ + public float getElectronBeamLevel() { + return mElectronBeamLevel; + } + + /** + * 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 (!mScreenReady || !mElectronBeamReady) { + mCleanListener = listener; + return false; + } else { + mCleanListener = null; + return true; + } + } + + public void dump(PrintWriter pw) { + pw.println(); + pw.println("Display Power State:"); + 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 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, + mElectronBeamDrawRunnable, null); + } + } + + private void invokeCleanListenerIfNeeded() { + final Runnable listener = mCleanListener; + if (listener != null && mScreenReady && mElectronBeamReady) { + mCleanListener = null; + listener.run(); + } + } + + private final Runnable mScreenUpdateRunnable = new Runnable() { + @Override + public void run() { + mScreenUpdatePending = false; + + int brightness = mScreenOn && mElectronBeamLevel > 0f ? mScreenBrightness : 0; + if (mPhotonicModulator.setState(mScreenOn, brightness)) { + mScreenReady = true; + invokeCleanListenerIfNeeded(); + } + } + }; + + private final Runnable mElectronBeamDrawRunnable = new Runnable() { + @Override + public void run() { + mElectronBeamDrawPending = false; + + if (mElectronBeamPrepared) { + mElectronBeam.draw(mElectronBeamLevel); + } + + mElectronBeamReady = true; + invokeCleanListenerIfNeeded(); + } + }; + + /** + * 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; + } + } + + 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(); + } + }; + } +} |