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