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