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 
17 package com.android.server.display;
18 
19 import android.content.Context;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.PowerManager;
23 import android.os.Trace;
24 import android.util.FloatProperty;
25 import android.util.Slog;
26 import android.view.Choreographer;
27 import android.view.Display;
28 
29 import java.io.PrintWriter;
30 
31 /**
32  * Controls the display power state.
33  * <p>
34  * This component is similar in nature to a {@link android.view.View} except that it
35  * describes the properties of a display.  When properties are changed, the component
36  * invalidates itself and posts a callback to apply the changes in a consistent order.
37  * This mechanism enables multiple properties of the display power state to be animated
38  * together smoothly by the animation framework.  Some of the work to blank or unblank
39  * the display is done on a separate thread to avoid blocking the looper.
40  * </p><p>
41  * This component must only be created or accessed by the {@link Looper} thread
42  * that belongs to the {@link DisplayPowerController}.
43  * </p><p>
44  * We don't need to worry about holding a suspend blocker here because the
45  * power manager does that for us whenever there is a change in progress.
46  * </p>
47  */
48 final class DisplayPowerState {
49     private static final String TAG = "DisplayPowerState";
50 
51     private static final boolean DEBUG = false;
52     private static String COUNTER_COLOR_FADE = "ColorFadeLevel";
53 
54     private final Handler mHandler;
55     private final Choreographer mChoreographer;
56     private final DisplayBlanker mBlanker;
57     private final ColorFade mColorFade;
58     private final PhotonicModulator mPhotonicModulator;
59     private final int mDisplayId;
60 
61     private int mScreenState;
62     private float mScreenBrightness;
63     private float mSdrScreenBrightness;
64     private boolean mScreenReady;
65     private boolean mScreenUpdatePending;
66 
67     private boolean mColorFadePrepared;
68     private float mColorFadeLevel;
69     private boolean mColorFadeReady;
70     private boolean mColorFadeDrawPending;
71 
72     private Runnable mCleanListener;
73 
74     private volatile boolean mStopped;
75 
DisplayPowerState( DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState)76     DisplayPowerState(
77             DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
78         mHandler = new Handler(true /*async*/);
79         mChoreographer = Choreographer.getInstance();
80         mBlanker = blanker;
81         mColorFade = colorFade;
82         mPhotonicModulator = new PhotonicModulator();
83         mPhotonicModulator.start();
84         mDisplayId = displayId;
85 
86         // At boot time, we don't know the screen's brightness,
87         // so prepare to set it to a known state when the state is next applied.
88         // Although we set the brightness here, the display power controller
89         // will reset the brightness to a new level immediately before the changes
90         // actually have a chance to be applied.
91         mScreenState = displayState;
92         mScreenBrightness = (displayState != Display.STATE_OFF) ? PowerManager.BRIGHTNESS_MAX
93                 : PowerManager.BRIGHTNESS_OFF_FLOAT;
94         mSdrScreenBrightness = mScreenBrightness;
95         scheduleScreenUpdate();
96 
97         mColorFadePrepared = false;
98         mColorFadeLevel = 1.0f;
99         mColorFadeReady = true;
100     }
101 
102     public static final FloatProperty<DisplayPowerState> COLOR_FADE_LEVEL =
103             new FloatProperty<DisplayPowerState>("electronBeamLevel") {
104         @Override
105         public void setValue(DisplayPowerState object, float value) {
106             object.setColorFadeLevel(value);
107         }
108 
109         @Override
110         public Float get(DisplayPowerState object) {
111             return object.getColorFadeLevel();
112         }
113     };
114 
115 
116     public static final FloatProperty<DisplayPowerState> SCREEN_BRIGHTNESS_FLOAT =
117             new FloatProperty<DisplayPowerState>("screenBrightnessFloat") {
118                 @Override
119                 public void setValue(DisplayPowerState object, float value) {
120                     object.setScreenBrightness(value);
121                 }
122 
123                 @Override
124                 public Float get(DisplayPowerState object) {
125                     return object.getScreenBrightness();
126                 }
127             };
128 
129     public static final FloatProperty<DisplayPowerState> SCREEN_SDR_BRIGHTNESS_FLOAT =
130             new FloatProperty<DisplayPowerState>("sdrScreenBrightnessFloat") {
131                 @Override
132                 public void setValue(DisplayPowerState object, float value) {
133                     object.setSdrScreenBrightness(value);
134                 }
135 
136                 @Override
137                 public Float get(DisplayPowerState object) {
138                     return object.getSdrScreenBrightness();
139                 }
140             };
141 
142     /**
143      * Sets whether the screen is on, off, or dozing.
144      */
setScreenState(int state)145     public void setScreenState(int state) {
146         if (mScreenState != state) {
147             if (DEBUG) {
148                 Slog.w(TAG, "setScreenState: state=" + Display.stateToString(state));
149             }
150 
151             mScreenState = state;
152             mScreenReady = false;
153             scheduleScreenUpdate();
154         }
155     }
156 
157     /**
158      * Gets the desired screen state.
159      */
getScreenState()160     public int getScreenState() {
161         return mScreenState;
162     }
163 
164     /**
165      * Sets the display's SDR brightness.
166      *
167      * @param brightness The brightness, ranges from 0.0f (minimum) to 1.0f (brightest), or is -1f
168      *                   (off).
169      */
setSdrScreenBrightness(float brightness)170     public void setSdrScreenBrightness(float brightness) {
171         if (mSdrScreenBrightness != brightness) {
172             if (DEBUG) {
173                 Slog.d(TAG, "setSdrScreenBrightness: brightness=" + brightness);
174             }
175 
176             mSdrScreenBrightness = brightness;
177             if (mScreenState != Display.STATE_OFF) {
178                 mScreenReady = false;
179                 scheduleScreenUpdate();
180             }
181         }
182     }
183 
184     /**
185      * Gets the screen SDR brightness.
186      */
getSdrScreenBrightness()187     public float getSdrScreenBrightness() {
188         return mSdrScreenBrightness;
189     }
190 
191     /**
192      * Sets the display brightness.
193      *
194      * @param brightness The brightness, ranges from 0.0f (minimum) to 1.0f (brightest), or is -1f
195      *                   (off).
196      */
setScreenBrightness(float brightness)197     public void setScreenBrightness(float brightness) {
198         if (mScreenBrightness != brightness) {
199             if (DEBUG) {
200                 Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
201             }
202 
203             mScreenBrightness = brightness;
204             if (mScreenState != Display.STATE_OFF) {
205                 mScreenReady = false;
206                 scheduleScreenUpdate();
207             }
208         }
209     }
210 
211     /**
212      * Gets the screen brightness.
213      */
getScreenBrightness()214     public float getScreenBrightness() {
215         return mScreenBrightness;
216     }
217 
218     /**
219      * Prepares the electron beam to turn on or off.
220      * This method should be called before starting an animation because it
221      * can take a fair amount of time to prepare the electron beam surface.
222      *
223      * @param mode The electron beam animation mode to prepare.
224      * @return True if the electron beam was prepared.
225      */
prepareColorFade(Context context, int mode)226     public boolean prepareColorFade(Context context, int mode) {
227         if (mColorFade == null || !mColorFade.prepare(context, mode)) {
228             mColorFadePrepared = false;
229             mColorFadeReady = true;
230             return false;
231         }
232 
233         mColorFadePrepared = true;
234         mColorFadeReady = false;
235         scheduleColorFadeDraw();
236         return true;
237     }
238 
239     /**
240      * Dismisses the color fade surface.
241      */
dismissColorFade()242     public void dismissColorFade() {
243         Trace.traceCounter(Trace.TRACE_TAG_POWER, COUNTER_COLOR_FADE, 100);
244         if (mColorFade != null) mColorFade.dismiss();
245         mColorFadePrepared = false;
246         mColorFadeReady = true;
247     }
248 
249    /**
250      * Dismisses the color fade resources.
251      */
dismissColorFadeResources()252     public void dismissColorFadeResources() {
253         if (mColorFade != null) mColorFade.dismissResources();
254     }
255 
256     /**
257      * Sets the level of the electron beam steering current.
258      *
259      * The display is blanked when the level is 0.0.  In normal use, the electron
260      * beam should have a value of 1.0.  The electron beam is unstable in between
261      * these states and the picture quality may be compromised.  For best effect,
262      * the electron beam should be warmed up or cooled off slowly.
263      *
264      * Warning: Electron beam emits harmful radiation.  Avoid direct exposure to
265      * skin or eyes.
266      *
267      * @param level The level, ranges from 0.0 (full off) to 1.0 (full on).
268      */
setColorFadeLevel(float level)269     public void setColorFadeLevel(float level) {
270         if (mColorFadeLevel != level) {
271             if (DEBUG) {
272                 Slog.d(TAG, "setColorFadeLevel: level=" + level);
273             }
274 
275             mColorFadeLevel = level;
276             if (mScreenState != Display.STATE_OFF) {
277                 mScreenReady = false;
278                 scheduleScreenUpdate(); // update backlight brightness
279             }
280             if (mColorFadePrepared) {
281                 mColorFadeReady = false;
282                 scheduleColorFadeDraw();
283             }
284         }
285     }
286 
287     /**
288      * Gets the level of the electron beam steering current.
289      */
getColorFadeLevel()290     public float getColorFadeLevel() {
291         return mColorFadeLevel;
292     }
293 
294     /**
295      * Returns true if no properties have been invalidated.
296      * Otherwise, returns false and promises to invoke the specified listener
297      * when the properties have all been applied.
298      * The listener always overrides any previously set listener.
299      */
waitUntilClean(Runnable listener)300     public boolean waitUntilClean(Runnable listener) {
301         if (!mScreenReady || !mColorFadeReady) {
302             mCleanListener = listener;
303             return false;
304         } else {
305             mCleanListener = null;
306             return true;
307         }
308     }
309 
310     /**
311      * Interrupts all running threads; halting future work.
312      *
313      * This method should be called when the DisplayPowerState is no longer in use; i.e. when
314      * the {@link #mDisplayId display} has been removed.
315      */
stop()316     public void stop() {
317         mStopped = true;
318         mPhotonicModulator.interrupt();
319         dismissColorFade();
320         mCleanListener = null;
321         mHandler.removeCallbacksAndMessages(null);
322     }
323 
dump(PrintWriter pw)324     public void dump(PrintWriter pw) {
325         pw.println();
326         pw.println("Display Power State:");
327         pw.println("  mStopped=" + mStopped);
328         pw.println("  mScreenState=" + Display.stateToString(mScreenState));
329         pw.println("  mScreenBrightness=" + mScreenBrightness);
330         pw.println("  mSdrScreenBrightness=" + mSdrScreenBrightness);
331         pw.println("  mScreenReady=" + mScreenReady);
332         pw.println("  mScreenUpdatePending=" + mScreenUpdatePending);
333         pw.println("  mColorFadePrepared=" + mColorFadePrepared);
334         pw.println("  mColorFadeLevel=" + mColorFadeLevel);
335         pw.println("  mColorFadeReady=" + mColorFadeReady);
336         pw.println("  mColorFadeDrawPending=" + mColorFadeDrawPending);
337 
338         mPhotonicModulator.dump(pw);
339         if (mColorFade != null) mColorFade.dump(pw);
340     }
341 
342     /**
343      * Resets the screen state to unknown. Useful when the underlying display-device changes for the
344      * LogicalDisplay and we do not know the last state that was sent to it.
345      */
resetScreenState()346     void resetScreenState() {
347         mScreenState = Display.STATE_UNKNOWN;
348         mScreenReady = false;
349     }
350 
scheduleScreenUpdate()351     private void scheduleScreenUpdate() {
352         if (!mScreenUpdatePending) {
353             mScreenUpdatePending = true;
354             postScreenUpdateThreadSafe();
355         }
356     }
357 
postScreenUpdateThreadSafe()358     private void postScreenUpdateThreadSafe() {
359         mHandler.removeCallbacks(mScreenUpdateRunnable);
360         mHandler.post(mScreenUpdateRunnable);
361     }
362 
scheduleColorFadeDraw()363     private void scheduleColorFadeDraw() {
364         if (!mColorFadeDrawPending) {
365             mColorFadeDrawPending = true;
366             mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
367                     mColorFadeDrawRunnable, null);
368         }
369     }
370 
invokeCleanListenerIfNeeded()371     private void invokeCleanListenerIfNeeded() {
372         final Runnable listener = mCleanListener;
373         if (listener != null && mScreenReady && mColorFadeReady) {
374             mCleanListener = null;
375             listener.run();
376         }
377     }
378 
379     private final Runnable mScreenUpdateRunnable = new Runnable() {
380         @Override
381         public void run() {
382             mScreenUpdatePending = false;
383 
384             float brightnessState = mScreenState != Display.STATE_OFF
385                     && mColorFadeLevel > 0f ? mScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
386             float sdrBrightnessState = mScreenState != Display.STATE_OFF
387                     && mColorFadeLevel > 0f
388                             ? mSdrScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
389             if (mPhotonicModulator.setState(mScreenState, brightnessState, sdrBrightnessState)) {
390                 if (DEBUG) {
391                     Slog.d(TAG, "Screen ready");
392                 }
393                 mScreenReady = true;
394                 invokeCleanListenerIfNeeded();
395             } else {
396                 if (DEBUG) {
397                     Slog.d(TAG, "Screen not ready");
398                 }
399             }
400         }
401     };
402 
403     private final Runnable mColorFadeDrawRunnable = new Runnable() {
404         @Override
405         public void run() {
406             mColorFadeDrawPending = false;
407 
408             if (mColorFadePrepared) {
409                 mColorFade.draw(mColorFadeLevel);
410                 Trace.traceCounter(Trace.TRACE_TAG_POWER,
411                         COUNTER_COLOR_FADE, Math.round(mColorFadeLevel * 100));
412             }
413 
414             mColorFadeReady = true;
415             invokeCleanListenerIfNeeded();
416         }
417     };
418 
419     /**
420      * Updates the state of the screen and backlight asynchronously on a separate thread.
421      */
422     private final class PhotonicModulator extends Thread {
423         private static final int INITIAL_SCREEN_STATE = Display.STATE_UNKNOWN;
424         private static final float INITIAL_BACKLIGHT_FLOAT = PowerManager.BRIGHTNESS_INVALID_FLOAT;
425 
426         private final Object mLock = new Object();
427 
428         private int mPendingState = INITIAL_SCREEN_STATE;
429         private float mPendingBacklight = INITIAL_BACKLIGHT_FLOAT;
430         private float mPendingSdrBacklight = INITIAL_BACKLIGHT_FLOAT;
431         private int mActualState = INITIAL_SCREEN_STATE;
432         private float mActualBacklight = INITIAL_BACKLIGHT_FLOAT;
433         private float mActualSdrBacklight = INITIAL_BACKLIGHT_FLOAT;
434         private boolean mStateChangeInProgress;
435         private boolean mBacklightChangeInProgress;
436 
PhotonicModulator()437         public PhotonicModulator() {
438             super("PhotonicModulator");
439         }
440 
setState(int state, float brightnessState, float sdrBrightnessState)441         public boolean setState(int state, float brightnessState, float sdrBrightnessState) {
442             synchronized (mLock) {
443                 boolean stateChanged = state != mPendingState;
444                 boolean backlightChanged = brightnessState != mPendingBacklight
445                         || sdrBrightnessState != mPendingSdrBacklight;
446                 if (stateChanged || backlightChanged) {
447                     if (DEBUG) {
448                         Slog.d(TAG, "Requesting new screen state: state="
449                                 + Display.stateToString(state) + ", backlight=" + brightnessState);
450                     }
451 
452                     mPendingState = state;
453                     mPendingBacklight = brightnessState;
454                     mPendingSdrBacklight = sdrBrightnessState;
455                     boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
456                     mStateChangeInProgress = stateChanged || mStateChangeInProgress;
457                     mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress;
458 
459                     if (!changeInProgress) {
460                         mLock.notifyAll();
461                     }
462                 }
463                 return !mStateChangeInProgress;
464             }
465         }
466 
dump(PrintWriter pw)467         public void dump(PrintWriter pw) {
468             synchronized (mLock) {
469                 pw.println();
470                 pw.println("Photonic Modulator State:");
471                 pw.println("  mPendingState=" + Display.stateToString(mPendingState));
472                 pw.println("  mPendingBacklight=" + mPendingBacklight);
473                 pw.println("  mPendingSdrBacklight=" + mPendingSdrBacklight);
474                 pw.println("  mActualState=" + Display.stateToString(mActualState));
475                 pw.println("  mActualBacklight=" + mActualBacklight);
476                 pw.println("  mActualSdrBacklight=" + mActualSdrBacklight);
477                 pw.println("  mStateChangeInProgress=" + mStateChangeInProgress);
478                 pw.println("  mBacklightChangeInProgress=" + mBacklightChangeInProgress);
479             }
480         }
481 
482         @Override
run()483         public void run() {
484             for (;;) {
485                 // Get pending change.
486                 final int state;
487                 final boolean stateChanged;
488                 final float brightnessState;
489                 final float sdrBrightnessState;
490                 final boolean backlightChanged;
491                 synchronized (mLock) {
492                     state = mPendingState;
493                     stateChanged = (state != mActualState);
494                     brightnessState = mPendingBacklight;
495                     sdrBrightnessState = mPendingSdrBacklight;
496                     backlightChanged = brightnessState != mActualBacklight
497                             || sdrBrightnessState != mActualSdrBacklight;
498                     if (!stateChanged) {
499                         // State changed applied, notify outer class.
500                         postScreenUpdateThreadSafe();
501                         mStateChangeInProgress = false;
502                     }
503                     if (!backlightChanged) {
504                         mBacklightChangeInProgress = false;
505                     }
506                     boolean valid = state != Display.STATE_UNKNOWN && !Float.isNaN(brightnessState);
507                     boolean changed = stateChanged || backlightChanged;
508                     if (!valid || !changed) {
509                         mStateChangeInProgress = false;
510                         mBacklightChangeInProgress = false;
511                         try {
512                             mLock.wait();
513                         } catch (InterruptedException ex) {
514                             if (mStopped) {
515                                 return;
516                             }
517                         }
518                         continue;
519                     }
520                     mActualState = state;
521                     mActualBacklight = brightnessState;
522                     mActualSdrBacklight = sdrBrightnessState;
523                 }
524 
525                 // Apply pending change.
526                 if (DEBUG) {
527                     Slog.d(TAG, "Updating screen state: id=" + mDisplayId +  ", state="
528                             + Display.stateToString(state) + ", backlight=" + brightnessState
529                             + ", sdrBacklight=" + sdrBrightnessState);
530                 }
531                 mBlanker.requestDisplayState(mDisplayId, state, brightnessState,
532                         sdrBrightnessState);
533             }
534         }
535     }
536 }
537