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