DisplayPowerState.java revision 13c589b66c47aa4d988eecce9a12c39d580939c9
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.power;
18
19import android.os.Looper;
20import android.os.PowerManager;
21import android.util.FloatProperty;
22import android.util.IntProperty;
23import android.util.Slog;
24import android.view.Choreographer;
25
26import java.io.PrintWriter;
27
28/**
29 * Represents the current display power state and realizes it.
30 *
31 * This component is similar in nature to a {@link View} except that it describes
32 * the properties of a display.  When properties are changed, the component
33 * invalidates itself and posts a callback to the {@link Choreographer} to
34 * apply the changes.  This mechanism enables the display power state to be
35 * animated smoothly by the animation framework.
36 *
37 * This component must only be created or accessed by the {@link Looper} thread
38 * that belongs to the {@link DisplayPowerController}.
39 *
40 * We don't need to worry about holding a suspend blocker here because the
41 * {@link DisplayPowerController} does that for us whenever there is a pending invalidate.
42 */
43final class DisplayPowerState {
44    private static final String TAG = "DisplayPowerState";
45
46    private static boolean DEBUG = false;
47
48    private static final int DIRTY_SCREEN_ON = 1 << 0;
49    private static final int DIRTY_ELECTRON_BEAM = 1 << 1;
50    private static final int DIRTY_BRIGHTNESS = 1 << 2;
51
52    private static final int DIRTY_ALL = 0xffffffff;
53
54    private final Choreographer mChoreographer;
55    private final ElectronBeam mElectronBeam;
56    private final PhotonicModulator mScreenBrightnessModulator;
57
58    private int mDirty;
59    private boolean mScreenOn;
60    private float mElectronBeamLevel;
61    private int mScreenBrightness;
62
63    private Runnable mCleanListener;
64
65    public DisplayPowerState(ElectronBeam electronBean,
66            PhotonicModulator screenBrightnessModulator) {
67        mChoreographer = Choreographer.getInstance();
68        mElectronBeam = electronBean;
69        mScreenBrightnessModulator = screenBrightnessModulator;
70
71        mScreenOn = true;
72        mElectronBeamLevel = 1.0f;
73        mScreenBrightness = PowerManager.BRIGHTNESS_ON;
74        invalidate(DIRTY_ALL);
75    }
76
77    public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
78            new FloatProperty<DisplayPowerState>("electronBeamLevel") {
79        @Override
80        public void setValue(DisplayPowerState object, float value) {
81            object.setElectronBeamLevel(value);
82        }
83
84        @Override
85        public Float get(DisplayPowerState object) {
86            return object.getElectronBeamLevel();
87        }
88    };
89
90    public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
91            new IntProperty<DisplayPowerState>("screenBrightness") {
92        @Override
93        public void setValue(DisplayPowerState object, int value) {
94            object.setScreenBrightness(value);
95        }
96
97        @Override
98        public Integer get(DisplayPowerState object) {
99            return object.getScreenBrightness();
100        }
101    };
102
103    /**
104     * Sets whether the screen is on or off.
105     */
106    public void setScreenOn(boolean on) {
107        if (mScreenOn != on) {
108            if (DEBUG) {
109                Slog.d(TAG, "setScreenOn: on=" + on);
110            }
111
112            mScreenOn = on;
113            invalidate(DIRTY_SCREEN_ON);
114        }
115    }
116
117    /**
118     * Returns true if the screen is on.
119     */
120    public boolean isScreenOn() {
121        return mScreenOn;
122    }
123
124    /**
125     * Prepares the electron beam to turn on or off.
126     * This method should be called before starting an animation because it
127     * can take a fair amount of time to prepare the electron beam surface.
128     *
129     * @param warmUp True if the electron beam should start warming up.
130     * @return True if the electron beam was prepared.
131     */
132    public boolean prepareElectronBeam(boolean warmUp) {
133        boolean success = mElectronBeam.prepare(warmUp);
134        invalidate(DIRTY_ELECTRON_BEAM);
135        return success;
136    }
137
138    /**
139     * Dismisses the electron beam surface.
140     */
141    public void dismissElectronBeam() {
142        mElectronBeam.dismiss();
143    }
144
145    /**
146     * Sets the level of the electron beam steering current.
147     *
148     * The display is blanked when the level is 0.0.  In normal use, the electron
149     * beam should have a value of 1.0.  The electron beam is unstable in between
150     * these states and the picture quality may be compromised.  For best effect,
151     * the electron beam should be warmed up or cooled off slowly.
152     *
153     * Warning: Electron beam emits harmful radiation.  Avoid direct exposure to
154     * skin or eyes.
155     *
156     * @param level The level, ranges from 0.0 (full off) to 1.0 (full on).
157     */
158    public void setElectronBeamLevel(float level) {
159        if (mElectronBeamLevel != level) {
160            if (DEBUG) {
161                Slog.d(TAG, "setElectronBeamLevel: level=" + level);
162            }
163
164            mElectronBeamLevel = level;
165            invalidate(DIRTY_ELECTRON_BEAM);
166        }
167    }
168
169    /**
170     * Gets the level of the electron beam steering current.
171     */
172    public float getElectronBeamLevel() {
173        return mElectronBeamLevel;
174    }
175
176    /**
177     * Sets the display brightness.
178     *
179     * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
180     */
181    public void setScreenBrightness(int brightness) {
182        if (mScreenBrightness != brightness) {
183            if (DEBUG) {
184                Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
185            }
186
187            mScreenBrightness = brightness;
188            invalidate(DIRTY_BRIGHTNESS);
189        }
190    }
191
192    /**
193     * Gets the screen brightness.
194     */
195    public int getScreenBrightness() {
196        return mScreenBrightness;
197    }
198
199    /**
200     * Returns true if no properties have been invalidated.
201     * Otherwise, returns false and promises to invoke the specified listener
202     * when the properties have all been applied.
203     * The listener always overrides any previously set listener.
204     */
205    public boolean waitUntilClean(Runnable listener) {
206        if (mDirty != 0) {
207            mCleanListener = listener;
208            return false;
209        } else {
210            mCleanListener = null;
211            return true;
212        }
213    }
214
215    public void dump(PrintWriter pw) {
216        pw.println();
217        pw.println("Display Power State:");
218        pw.println("  mDirty=" + Integer.toHexString(mDirty));
219        pw.println("  mScreenOn=" + mScreenOn);
220        pw.println("  mScreenBrightness=" + mScreenBrightness);
221        pw.println("  mElectronBeamLevel=" + mElectronBeamLevel);
222
223        mElectronBeam.dump(pw);
224    }
225
226    private void invalidate(int dirty) {
227        if (mDirty == 0) {
228            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
229                    mTraversalRunnable, null);
230        }
231
232        mDirty |= dirty;
233    }
234
235    private void apply() {
236        if (mDirty != 0) {
237            if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) {
238                PowerManagerService.nativeSetScreenState(false);
239            }
240
241            if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
242                mElectronBeam.draw(mElectronBeamLevel);
243            }
244
245            if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0) {
246                mScreenBrightnessModulator.setBrightness(mScreenOn ?
247                        (int)(mScreenBrightness * mElectronBeamLevel) : 0);
248            }
249
250            if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
251                PowerManagerService.nativeSetScreenState(true);
252            }
253
254            mDirty = 0;
255
256            if (mCleanListener != null) {
257                mCleanListener.run();
258            }
259        }
260    }
261
262    private final Runnable mTraversalRunnable = new Runnable() {
263        @Override
264        public void run() {
265            if (mDirty != 0) {
266                apply();
267            }
268        }
269    };
270}
271