| /* |
| * 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.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; |
| |
| /** |
| * Represents the current display power state and realizes it. |
| * |
| * 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. |
| * |
| * This component must only be created or accessed by the {@link Looper} thread |
| * that belongs to the {@link DisplayPowerController}. |
| * |
| * 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. |
| */ |
| 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 Choreographer mChoreographer; |
| private final ElectronBeam mElectronBeam; // may be null if only animating backlights |
| private final PhotonicModulator mScreenBrightnessModulator; |
| |
| private int mDirty; |
| private boolean mScreenOn; |
| private float mElectronBeamLevel; |
| private int mScreenBrightness; |
| |
| private Runnable mCleanListener; |
| |
| public DisplayPowerState(ElectronBeam electronBean, |
| PhotonicModulator screenBrightnessModulator) { |
| mChoreographer = Choreographer.getInstance(); |
| mElectronBeam = electronBean; |
| mScreenBrightnessModulator = screenBrightnessModulator; |
| |
| // 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; |
| mElectronBeamLevel = 1.0f; |
| mScreenBrightness = PowerManager.BRIGHTNESS_ON; |
| invalidate(DIRTY_BRIGHTNESS); |
| } |
| |
| 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; |
| invalidate(DIRTY_SCREEN_ON); |
| } |
| } |
| |
| /** |
| * Returns true if the screen is on. |
| */ |
| public boolean isScreenOn() { |
| return mScreenOn; |
| } |
| |
| /** |
| * 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 warmUp True if the electron beam should start warming up. |
| * @return True if the electron beam was prepared. |
| */ |
| public boolean prepareElectronBeam(boolean warmUp) { |
| if (mElectronBeam != null) { |
| boolean success = mElectronBeam.prepare(warmUp); |
| invalidate(DIRTY_ELECTRON_BEAM); |
| return success; |
| } else { |
| return true; |
| } |
| } |
| |
| /** |
| * Dismisses the electron beam surface. |
| */ |
| public void dismissElectronBeam() { |
| if (mElectronBeam != null) { |
| mElectronBeam.dismiss(); |
| } |
| } |
| |
| /** |
| * 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; |
| invalidate(DIRTY_ELECTRON_BEAM); |
| } |
| } |
| |
| /** |
| * Gets the level of the electron beam steering current. |
| */ |
| public float getElectronBeamLevel() { |
| return mElectronBeamLevel; |
| } |
| |
| /** |
| * 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) { |
| mCleanListener = listener; |
| return false; |
| } else { |
| mCleanListener = null; |
| return true; |
| } |
| } |
| |
| 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(" mElectronBeamLevel=" + mElectronBeamLevel); |
| |
| if (mElectronBeam != null) { |
| mElectronBeam.dump(pw); |
| } |
| } |
| |
| private void invalidate(int dirty) { |
| if (mDirty == 0) { |
| mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, |
| mTraversalRunnable, null); |
| } |
| |
| mDirty |= dirty; |
| } |
| |
| private void apply() { |
| if (mDirty != 0) { |
| if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) { |
| mScreenBrightnessModulator.setBrightness(0, true /*sync*/); |
| PowerManagerService.nativeSetScreenState(false); |
| } |
| |
| if ((mDirty & DIRTY_ELECTRON_BEAM) != 0 && mElectronBeam != null) { |
| mElectronBeam.draw(mElectronBeamLevel); |
| } |
| |
| if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) { |
| PowerManagerService.nativeSetScreenState(true); |
| } |
| |
| if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0 |
| && mScreenOn) { |
| mScreenBrightnessModulator.setBrightness( |
| (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/); |
| } |
| |
| mDirty = 0; |
| |
| if (mCleanListener != null) { |
| mCleanListener.run(); |
| } |
| } |
| } |
| |
| private final Runnable mTraversalRunnable = new Runnable() { |
| @Override |
| public void run() { |
| if (mDirty != 0) { |
| apply(); |
| } |
| } |
| }; |
| } |