/* * 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; 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 ELECTRON_BEAM_LEVEL = new FloatProperty("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 SCREEN_BRIGHTNESS = new IntProperty("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) { boolean success = mElectronBeam.prepare(warmUp); invalidate(DIRTY_ELECTRON_BEAM); return success; } /** * Dismisses the electron beam surface. */ public void dismissElectronBeam() { 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); 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.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(); } } }; }