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