DisplayPowerState.java revision 735f740fe81b7172d0b208d584eecf632533ec4a
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 final Choreographer mChoreographer;
53    private final ElectronBeam mElectronBeam;
54    private final PhotonicModulator mScreenBrightnessModulator;
55
56    private int mDirty;
57    private boolean mScreenOn;
58    private float mElectronBeamLevel;
59    private int mScreenBrightness;
60
61    private Runnable mCleanListener;
62
63    public DisplayPowerState(ElectronBeam electronBean,
64            PhotonicModulator screenBrightnessModulator) {
65        mChoreographer = Choreographer.getInstance();
66        mElectronBeam = electronBean;
67        mScreenBrightnessModulator = screenBrightnessModulator;
68
69        // At boot time, we know that the screen is on and the electron beam
70        // animation is not playing.  We don't know the screen's brightness though,
71        // so prepare to set it to a known state when the state is next applied.
72        // Although we set the brightness to full on here, the display power controller
73        // will reset the brightness to a new level immediately before the changes
74        // actually have a chance to be applied.
75        mScreenOn = true;
76        mElectronBeamLevel = 1.0f;
77        mScreenBrightness = PowerManager.BRIGHTNESS_ON;
78        invalidate(DIRTY_BRIGHTNESS);
79    }
80
81    public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
82            new FloatProperty<DisplayPowerState>("electronBeamLevel") {
83        @Override
84        public void setValue(DisplayPowerState object, float value) {
85            object.setElectronBeamLevel(value);
86        }
87
88        @Override
89        public Float get(DisplayPowerState object) {
90            return object.getElectronBeamLevel();
91        }
92    };
93
94    public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
95            new IntProperty<DisplayPowerState>("screenBrightness") {
96        @Override
97        public void setValue(DisplayPowerState object, int value) {
98            object.setScreenBrightness(value);
99        }
100
101        @Override
102        public Integer get(DisplayPowerState object) {
103            return object.getScreenBrightness();
104        }
105    };
106
107    /**
108     * Sets whether the screen is on or off.
109     */
110    public void setScreenOn(boolean on) {
111        if (mScreenOn != on) {
112            if (DEBUG) {
113                Slog.d(TAG, "setScreenOn: on=" + on);
114            }
115
116            mScreenOn = on;
117            invalidate(DIRTY_SCREEN_ON);
118        }
119    }
120
121    /**
122     * Returns true if the screen is on.
123     */
124    public boolean isScreenOn() {
125        return mScreenOn;
126    }
127
128    /**
129     * Prepares the electron beam to turn on or off.
130     * This method should be called before starting an animation because it
131     * can take a fair amount of time to prepare the electron beam surface.
132     *
133     * @param warmUp True if the electron beam should start warming up.
134     * @return True if the electron beam was prepared.
135     */
136    public boolean prepareElectronBeam(boolean warmUp) {
137        boolean success = mElectronBeam.prepare(warmUp);
138        invalidate(DIRTY_ELECTRON_BEAM);
139        return success;
140    }
141
142    /**
143     * Dismisses the electron beam surface.
144     */
145    public void dismissElectronBeam() {
146        mElectronBeam.dismiss();
147    }
148
149    /**
150     * Sets the level of the electron beam steering current.
151     *
152     * The display is blanked when the level is 0.0.  In normal use, the electron
153     * beam should have a value of 1.0.  The electron beam is unstable in between
154     * these states and the picture quality may be compromised.  For best effect,
155     * the electron beam should be warmed up or cooled off slowly.
156     *
157     * Warning: Electron beam emits harmful radiation.  Avoid direct exposure to
158     * skin or eyes.
159     *
160     * @param level The level, ranges from 0.0 (full off) to 1.0 (full on).
161     */
162    public void setElectronBeamLevel(float level) {
163        if (mElectronBeamLevel != level) {
164            if (DEBUG) {
165                Slog.d(TAG, "setElectronBeamLevel: level=" + level);
166            }
167
168            mElectronBeamLevel = level;
169            invalidate(DIRTY_ELECTRON_BEAM);
170        }
171    }
172
173    /**
174     * Gets the level of the electron beam steering current.
175     */
176    public float getElectronBeamLevel() {
177        return mElectronBeamLevel;
178    }
179
180    /**
181     * Sets the display brightness.
182     *
183     * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
184     */
185    public void setScreenBrightness(int brightness) {
186        if (mScreenBrightness != brightness) {
187            if (DEBUG) {
188                Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
189            }
190
191            mScreenBrightness = brightness;
192            invalidate(DIRTY_BRIGHTNESS);
193        }
194    }
195
196    /**
197     * Gets the screen brightness.
198     */
199    public int getScreenBrightness() {
200        return mScreenBrightness;
201    }
202
203    /**
204     * Returns true if no properties have been invalidated.
205     * Otherwise, returns false and promises to invoke the specified listener
206     * when the properties have all been applied.
207     * The listener always overrides any previously set listener.
208     */
209    public boolean waitUntilClean(Runnable listener) {
210        if (mDirty != 0) {
211            mCleanListener = listener;
212            return false;
213        } else {
214            mCleanListener = null;
215            return true;
216        }
217    }
218
219    public void dump(PrintWriter pw) {
220        pw.println();
221        pw.println("Display Power State:");
222        pw.println("  mDirty=" + Integer.toHexString(mDirty));
223        pw.println("  mScreenOn=" + mScreenOn);
224        pw.println("  mScreenBrightness=" + mScreenBrightness);
225        pw.println("  mElectronBeamLevel=" + mElectronBeamLevel);
226
227        mElectronBeam.dump(pw);
228    }
229
230    private void invalidate(int dirty) {
231        if (mDirty == 0) {
232            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
233                    mTraversalRunnable, null);
234        }
235
236        mDirty |= dirty;
237    }
238
239    private void apply() {
240        if (mDirty != 0) {
241            if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) {
242                mScreenBrightnessModulator.setBrightness(0, true /*sync*/);
243                PowerManagerService.nativeSetScreenState(false);
244            }
245
246            if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
247                mElectronBeam.draw(mElectronBeamLevel);
248            }
249
250            if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
251                PowerManagerService.nativeSetScreenState(true);
252            }
253
254            if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0
255                    && mScreenOn) {
256                mScreenBrightnessModulator.setBrightness(
257                        (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/);
258            }
259
260            mDirty = 0;
261
262            if (mCleanListener != null) {
263                mCleanListener.run();
264            }
265        }
266    }
267
268    private final Runnable mTraversalRunnable = new Runnable() {
269        @Override
270        public void run() {
271            if (mDirty != 0) {
272                apply();
273            }
274        }
275    };
276}
277