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();
348         pw.println("Display Power State:");
349         pw.println("  mStopped=" + mStopped);
350         pw.println("  mScreenState=" + Display.stateToString(mScreenState));
351         pw.println("  mScreenBrightness=" + mScreenBrightness);
352         pw.println("  mSdrScreenBrightness=" + mSdrScreenBrightness);
353         pw.println("  mScreenReady=" + mScreenReady);
354         pw.println("  mScreenUpdatePending=" + mScreenUpdatePending);
355         pw.println("  mColorFadePrepared=" + mColorFadePrepared);
356         pw.println("  mColorFadeLevel=" + mColorFadeLevel);
357         pw.println("  mColorFadeReady=" + mColorFadeReady);
358         pw.println("  mColorFadeDrawPending=" + mColorFadeDrawPending);
359 
360         mPhotonicModulator.dump(pw);
361         if (mColorFade != null) mColorFade.dump(pw);
362     }
363 
364     /**
365      * Resets the screen state to unknown. Useful when the underlying display-device changes for the
366      * LogicalDisplay and we do not know the last state that was sent to it.
367      */
resetScreenState()368     void resetScreenState() {
369         mScreenState = Display.STATE_UNKNOWN;
370         mScreenReady = false;
371     }
372 
scheduleScreenUpdate()373     private void scheduleScreenUpdate() {
374         if (!mScreenUpdatePending) {
375             mScreenUpdatePending = true;
376             postScreenUpdateThreadSafe();
377         }
378     }
379 
postScreenUpdateThreadSafe()380     private void postScreenUpdateThreadSafe() {
381         mHandler.removeCallbacks(mScreenUpdateRunnable);
382         mHandler.post(mScreenUpdateRunnable);
383     }
384 
scheduleColorFadeDraw()385     private void scheduleColorFadeDraw() {
386         if (!mColorFadeDrawPending) {
387             mColorFadeDrawPending = true;
388             mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
389                     mColorFadeDrawRunnable, null);
390         }
391     }
392 
invokeCleanListenerIfNeeded()393     private void invokeCleanListenerIfNeeded() {
394         final Runnable listener = mCleanListener;
395         if (listener != null && mScreenReady && mColorFadeReady) {
396             mCleanListener = null;
397             listener.run();
398         }
399     }
400 
401     private final Runnable mScreenUpdateRunnable = new Runnable() {
402         @Override
403         public void run() {
404             mScreenUpdatePending = false;
405 
406             float brightnessState = mScreenState != Display.STATE_OFF
407                     && mColorFadeLevel > 0f ? mScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
408             float sdrBrightnessState = mScreenState != Display.STATE_OFF
409                     && mColorFadeLevel > 0f
410                             ? mSdrScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
411             if (mPhotonicModulator.setState(mScreenState, brightnessState, sdrBrightnessState)) {
412                 if (DEBUG) {
413                     Slog.d(TAG, "Screen ready");
414                 }
415                 mScreenReady = true;
416                 invokeCleanListenerIfNeeded();
417             } else {
418                 if (DEBUG) {
419                     Slog.d(TAG, "Screen not ready");
420                 }
421             }
422         }
423     };
424 
425     @VisibleForTesting
426     final Runnable mColorFadeDrawRunnable = new Runnable() {
427         @Override
428         public void run() {
429             mColorFadeDrawPending = false;
430 
431             if (mColorFadePrepared) {
432                 mColorFade.draw(mColorFadeLevel);
433                 Trace.traceCounter(Trace.TRACE_TAG_POWER,
434                         COUNTER_COLOR_FADE, Math.round(mColorFadeLevel * 100));
435             }
436 
437             mColorFadeReady = true;
438             invokeCleanListenerIfNeeded();
439         }
440     };
441 
442     /**
443      * Updates the state of the screen and backlight asynchronously on a separate thread.
444      */
445     private final class PhotonicModulator extends Thread {
446         private static final int INITIAL_SCREEN_STATE = Display.STATE_UNKNOWN;
447         private static final float INITIAL_BACKLIGHT_FLOAT = PowerManager.BRIGHTNESS_INVALID_FLOAT;
448 
449         private final Object mLock = new Object();
450 
451         private int mPendingState = INITIAL_SCREEN_STATE;
452         private float mPendingBacklight = INITIAL_BACKLIGHT_FLOAT;
453         private float mPendingSdrBacklight = INITIAL_BACKLIGHT_FLOAT;
454         private int mActualState = INITIAL_SCREEN_STATE;
455         private float mActualBacklight = INITIAL_BACKLIGHT_FLOAT;
456         private float mActualSdrBacklight = INITIAL_BACKLIGHT_FLOAT;
457         private boolean mStateChangeInProgress;
458         private boolean mBacklightChangeInProgress;
459 
PhotonicModulator()460         public PhotonicModulator() {
461             super("PhotonicModulator");
462         }
463 
setState(int state, float brightnessState, float sdrBrightnessState)464         public boolean setState(int state, float brightnessState, float sdrBrightnessState) {
465             synchronized (mLock) {
466                 boolean stateChanged = state != mPendingState;
467                 boolean backlightChanged = brightnessState != mPendingBacklight
468                         || sdrBrightnessState != mPendingSdrBacklight;
469                 if (stateChanged || backlightChanged) {
470                     if (DEBUG) {
471                         Slog.d(TAG, "Requesting new screen state: state="
472                                 + Display.stateToString(state) + ", backlight=" + brightnessState);
473                     }
474 
475                     mPendingState = state;
476                     mPendingBacklight = brightnessState;
477                     mPendingSdrBacklight = sdrBrightnessState;
478                     boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
479                     mStateChangeInProgress = stateChanged || mStateChangeInProgress;
480                     mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress;
481 
482                     if (!changeInProgress) {
483                         mLock.notifyAll();
484                     }
485                 }
486                 return !mStateChangeInProgress;
487             }
488         }
489 
dump(PrintWriter pw)490         public void dump(PrintWriter pw) {
491             synchronized (mLock) {
492                 pw.println();
493                 pw.println("Photonic Modulator State:");
494                 pw.println("  mPendingState=" + Display.stateToString(mPendingState));
495                 pw.println("  mPendingBacklight=" + mPendingBacklight);
496                 pw.println("  mPendingSdrBacklight=" + mPendingSdrBacklight);
497                 pw.println("  mActualState=" + Display.stateToString(mActualState));
498                 pw.println("  mActualBacklight=" + mActualBacklight);
499                 pw.println("  mActualSdrBacklight=" + mActualSdrBacklight);
500                 pw.println("  mStateChangeInProgress=" + mStateChangeInProgress);
501                 pw.println("  mBacklightChangeInProgress=" + mBacklightChangeInProgress);
502             }
503         }
504 
505         @Override
run()506         public void run() {
507             for (;;) {
508                 // Get pending change.
509                 final int state;
510                 final boolean stateChanged;
511                 final float brightnessState;
512                 final float sdrBrightnessState;
513                 final boolean backlightChanged;
514                 synchronized (mLock) {
515                     state = mPendingState;
516                     stateChanged = (state != mActualState);
517                     brightnessState = mPendingBacklight;
518                     sdrBrightnessState = mPendingSdrBacklight;
519                     backlightChanged = brightnessState != mActualBacklight
520                             || sdrBrightnessState != mActualSdrBacklight;
521                     if (!stateChanged) {
522                         // State changed applied, notify outer class.
523                         postScreenUpdateThreadSafe();
524                         mStateChangeInProgress = false;
525                     }
526                     if (!backlightChanged) {
527                         mBacklightChangeInProgress = false;
528                     }
529                     boolean valid = state != Display.STATE_UNKNOWN && !Float.isNaN(brightnessState);
530                     boolean changed = stateChanged || backlightChanged;
531                     if (!valid || !changed) {
532                         mStateChangeInProgress = false;
533                         mBacklightChangeInProgress = false;
534                         try {
535                             mLock.wait();
536                         } catch (InterruptedException ex) {
537                             if (mStopped) {
538                                 return;
539                             }
540                         }
541                         continue;
542                     }
543                     mActualState = state;
544                     mActualBacklight = brightnessState;
545                     mActualSdrBacklight = sdrBrightnessState;
546                 }
547 
548                 // Apply pending change.
549                 if (DEBUG) {
550                     Slog.d(TAG, "Updating screen state: id=" + mDisplayId +  ", state="
551                             + Display.stateToString(state) + ", backlight=" + brightnessState
552                             + ", sdrBacklight=" + sdrBrightnessState);
553                 }
554                 mBlanker.requestDisplayState(mDisplayId, state, brightnessState,
555                         sdrBrightnessState);
556             }
557         }
558     }
559 }
560