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.IntProperty; 26 import android.util.Slog; 27 import android.view.Choreographer; 28 import android.view.Display; 29 30 import java.io.PrintWriter; 31 32 /** 33 * Controls the display power state. 34 * <p> 35 * This component is similar in nature to a {@link android.view.View} except that it 36 * describes the properties of a display. When properties are changed, the component 37 * invalidates itself and posts a callback to apply the changes in a consistent order. 38 * This mechanism enables multiple properties of the display power state to be animated 39 * together smoothly by the animation framework. Some of the work to blank or unblank 40 * the display is done on a separate thread to avoid blocking the looper. 41 * </p><p> 42 * This component must only be created or accessed by the {@link Looper} thread 43 * that belongs to the {@link DisplayPowerController}. 44 * </p><p> 45 * We don't need to worry about holding a suspend blocker here because the 46 * power manager does that for us whenever there is a change in progress. 47 * </p> 48 */ 49 final class DisplayPowerState { 50 private static final String TAG = "DisplayPowerState"; 51 52 private static boolean DEBUG = false; 53 private static String COUNTER_COLOR_FADE = "ColorFadeLevel"; 54 55 private final Handler mHandler; 56 private final Choreographer mChoreographer; 57 private final DisplayBlanker mBlanker; 58 private final ColorFade mColorFade; 59 private final PhotonicModulator mPhotonicModulator; 60 61 private int mScreenState; 62 private int mScreenBrightness; 63 private boolean mScreenReady; 64 private boolean mScreenUpdatePending; 65 66 private boolean mColorFadePrepared; 67 private float mColorFadeLevel; 68 private boolean mColorFadeReady; 69 private boolean mColorFadeDrawPending; 70 71 private Runnable mCleanListener; 72 73 public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade) { 74 mHandler = new Handler(true /*async*/); 75 mChoreographer = Choreographer.getInstance(); 76 mBlanker = blanker; 77 mColorFade = colorFade; 78 mPhotonicModulator = new PhotonicModulator(); 79 mPhotonicModulator.start(); 80 81 // At boot time, we know that the screen is on and the electron beam 82 // animation is not playing. We don't know the screen's brightness though, 83 // so prepare to set it to a known state when the state is next applied. 84 // Although we set the brightness to full on here, the display power controller 85 // will reset the brightness to a new level immediately before the changes 86 // actually have a chance to be applied. 87 mScreenState = Display.STATE_ON; 88 mScreenBrightness = PowerManager.BRIGHTNESS_ON; 89 scheduleScreenUpdate(); 90 91 mColorFadePrepared = false; 92 mColorFadeLevel = 1.0f; 93 mColorFadeReady = true; 94 } 95 96 public static final FloatProperty<DisplayPowerState> COLOR_FADE_LEVEL = 97 new FloatProperty<DisplayPowerState>("electronBeamLevel") { 98 @Override 99 public void setValue(DisplayPowerState object, float value) { 100 object.setColorFadeLevel(value); 101 } 102 103 @Override 104 public Float get(DisplayPowerState object) { 105 return object.getColorFadeLevel(); 106 } 107 }; 108 109 public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS = 110 new IntProperty<DisplayPowerState>("screenBrightness") { 111 @Override 112 public void setValue(DisplayPowerState object, int value) { 113 object.setScreenBrightness(value); 114 } 115 116 @Override 117 public Integer get(DisplayPowerState object) { 118 return object.getScreenBrightness(); 119 } 120 }; 121 122 /** 123 * Sets whether the screen is on, off, or dozing. 124 */ 125 public void setScreenState(int state) { 126 if (mScreenState != state) { 127 if (DEBUG) { 128 Slog.d(TAG, "setScreenState: state=" + state); 129 } 130 131 mScreenState = state; 132 mScreenReady = false; 133 scheduleScreenUpdate(); 134 } 135 } 136 137 /** 138 * Gets the desired screen state. 139 */ 140 public int getScreenState() { 141 return mScreenState; 142 } 143 144 /** 145 * Sets the display brightness. 146 * 147 * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). 148 */ 149 public void setScreenBrightness(int brightness) { 150 if (mScreenBrightness != brightness) { 151 if (DEBUG) { 152 Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); 153 } 154 155 mScreenBrightness = brightness; 156 if (mScreenState != Display.STATE_OFF) { 157 mScreenReady = false; 158 scheduleScreenUpdate(); 159 } 160 } 161 } 162 163 /** 164 * Gets the screen brightness. 165 */ 166 public int getScreenBrightness() { 167 return mScreenBrightness; 168 } 169 170 /** 171 * Prepares the electron beam to turn on or off. 172 * This method should be called before starting an animation because it 173 * can take a fair amount of time to prepare the electron beam surface. 174 * 175 * @param mode The electron beam animation mode to prepare. 176 * @return True if the electron beam was prepared. 177 */ 178 public boolean prepareColorFade(Context context, int mode) { 179 if (mColorFade == null || !mColorFade.prepare(context, mode)) { 180 mColorFadePrepared = false; 181 mColorFadeReady = true; 182 return false; 183 } 184 185 mColorFadePrepared = true; 186 mColorFadeReady = false; 187 scheduleColorFadeDraw(); 188 return true; 189 } 190 191 /** 192 * Dismisses the color fade surface. 193 */ 194 public void dismissColorFade() { 195 Trace.traceCounter(Trace.TRACE_TAG_POWER, COUNTER_COLOR_FADE, 100); 196 if (mColorFade != null) mColorFade.dismiss(); 197 mColorFadePrepared = false; 198 mColorFadeReady = true; 199 } 200 201 /** 202 * Dismisses the color fade resources. 203 */ 204 public void dismissColorFadeResources() { 205 if (mColorFade != null) mColorFade.dismissResources(); 206 } 207 208 /** 209 * Sets the level of the electron beam steering current. 210 * 211 * The display is blanked when the level is 0.0. In normal use, the electron 212 * beam should have a value of 1.0. The electron beam is unstable in between 213 * these states and the picture quality may be compromised. For best effect, 214 * the electron beam should be warmed up or cooled off slowly. 215 * 216 * Warning: Electron beam emits harmful radiation. Avoid direct exposure to 217 * skin or eyes. 218 * 219 * @param level The level, ranges from 0.0 (full off) to 1.0 (full on). 220 */ 221 public void setColorFadeLevel(float level) { 222 if (mColorFadeLevel != level) { 223 if (DEBUG) { 224 Slog.d(TAG, "setColorFadeLevel: level=" + level); 225 } 226 227 mColorFadeLevel = level; 228 if (mScreenState != Display.STATE_OFF) { 229 mScreenReady = false; 230 scheduleScreenUpdate(); // update backlight brightness 231 } 232 if (mColorFadePrepared) { 233 mColorFadeReady = false; 234 scheduleColorFadeDraw(); 235 } 236 } 237 } 238 239 /** 240 * Gets the level of the electron beam steering current. 241 */ 242 public float getColorFadeLevel() { 243 return mColorFadeLevel; 244 } 245 246 /** 247 * Returns true if no properties have been invalidated. 248 * Otherwise, returns false and promises to invoke the specified listener 249 * when the properties have all been applied. 250 * The listener always overrides any previously set listener. 251 */ 252 public boolean waitUntilClean(Runnable listener) { 253 if (!mScreenReady || !mColorFadeReady) { 254 mCleanListener = listener; 255 return false; 256 } else { 257 mCleanListener = null; 258 return true; 259 } 260 } 261 262 public void dump(PrintWriter pw) { 263 pw.println(); 264 pw.println("Display Power State:"); 265 pw.println(" mScreenState=" + Display.stateToString(mScreenState)); 266 pw.println(" mScreenBrightness=" + mScreenBrightness); 267 pw.println(" mScreenReady=" + mScreenReady); 268 pw.println(" mScreenUpdatePending=" + mScreenUpdatePending); 269 pw.println(" mColorFadePrepared=" + mColorFadePrepared); 270 pw.println(" mColorFadeLevel=" + mColorFadeLevel); 271 pw.println(" mColorFadeReady=" + mColorFadeReady); 272 pw.println(" mColorFadeDrawPending=" + mColorFadeDrawPending); 273 274 mPhotonicModulator.dump(pw); 275 if (mColorFade != null) mColorFade.dump(pw); 276 } 277 278 private void scheduleScreenUpdate() { 279 if (!mScreenUpdatePending) { 280 mScreenUpdatePending = true; 281 postScreenUpdateThreadSafe(); 282 } 283 } 284 285 private void postScreenUpdateThreadSafe() { 286 mHandler.removeCallbacks(mScreenUpdateRunnable); 287 mHandler.post(mScreenUpdateRunnable); 288 } 289 290 private void scheduleColorFadeDraw() { 291 if (!mColorFadeDrawPending) { 292 mColorFadeDrawPending = true; 293 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, 294 mColorFadeDrawRunnable, null); 295 } 296 } 297 298 private void invokeCleanListenerIfNeeded() { 299 final Runnable listener = mCleanListener; 300 if (listener != null && mScreenReady && mColorFadeReady) { 301 mCleanListener = null; 302 listener.run(); 303 } 304 } 305 306 private final Runnable mScreenUpdateRunnable = new Runnable() { 307 @Override 308 public void run() { 309 mScreenUpdatePending = false; 310 311 int brightness = mScreenState != Display.STATE_OFF 312 && mColorFadeLevel > 0f ? mScreenBrightness : 0; 313 if (mPhotonicModulator.setState(mScreenState, brightness)) { 314 if (DEBUG) { 315 Slog.d(TAG, "Screen ready"); 316 } 317 mScreenReady = true; 318 invokeCleanListenerIfNeeded(); 319 } else { 320 if (DEBUG) { 321 Slog.d(TAG, "Screen not ready"); 322 } 323 } 324 } 325 }; 326 327 private final Runnable mColorFadeDrawRunnable = new Runnable() { 328 @Override 329 public void run() { 330 mColorFadeDrawPending = false; 331 332 if (mColorFadePrepared) { 333 mColorFade.draw(mColorFadeLevel); 334 Trace.traceCounter(Trace.TRACE_TAG_POWER, 335 COUNTER_COLOR_FADE, Math.round(mColorFadeLevel * 100)); 336 } 337 338 mColorFadeReady = true; 339 invokeCleanListenerIfNeeded(); 340 } 341 }; 342 343 /** 344 * Updates the state of the screen and backlight asynchronously on a separate thread. 345 */ 346 private final class PhotonicModulator extends Thread { 347 private static final int INITIAL_SCREEN_STATE = Display.STATE_OFF; // unknown, assume off 348 private static final int INITIAL_BACKLIGHT = -1; // unknown 349 350 private final Object mLock = new Object(); 351 352 private int mPendingState = INITIAL_SCREEN_STATE; 353 private int mPendingBacklight = INITIAL_BACKLIGHT; 354 private int mActualState = INITIAL_SCREEN_STATE; 355 private int mActualBacklight = INITIAL_BACKLIGHT; 356 private boolean mStateChangeInProgress; 357 private boolean mBacklightChangeInProgress; 358 359 public PhotonicModulator() { 360 super("PhotonicModulator"); 361 } 362 363 public boolean setState(int state, int backlight) { 364 synchronized (mLock) { 365 boolean stateChanged = state != mPendingState; 366 boolean backlightChanged = backlight != mPendingBacklight; 367 if (stateChanged || backlightChanged) { 368 if (DEBUG) { 369 Slog.d(TAG, "Requesting new screen state: state=" 370 + Display.stateToString(state) + ", backlight=" + backlight); 371 } 372 373 mPendingState = state; 374 mPendingBacklight = backlight; 375 376 boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress; 377 mStateChangeInProgress = stateChanged || mStateChangeInProgress; 378 mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress; 379 380 if (!changeInProgress) { 381 mLock.notifyAll(); 382 } 383 } 384 return !mStateChangeInProgress; 385 } 386 } 387 388 public void dump(PrintWriter pw) { 389 synchronized (mLock) { 390 pw.println(); 391 pw.println("Photonic Modulator State:"); 392 pw.println(" mPendingState=" + Display.stateToString(mPendingState)); 393 pw.println(" mPendingBacklight=" + mPendingBacklight); 394 pw.println(" mActualState=" + Display.stateToString(mActualState)); 395 pw.println(" mActualBacklight=" + mActualBacklight); 396 pw.println(" mStateChangeInProgress=" + mStateChangeInProgress); 397 pw.println(" mBacklightChangeInProgress=" + mBacklightChangeInProgress); 398 } 399 } 400 401 @Override 402 public void run() { 403 for (;;) { 404 // Get pending change. 405 final int state; 406 final boolean stateChanged; 407 final int backlight; 408 final boolean backlightChanged; 409 synchronized (mLock) { 410 state = mPendingState; 411 stateChanged = (state != mActualState); 412 backlight = mPendingBacklight; 413 backlightChanged = (backlight != mActualBacklight); 414 if (!stateChanged) { 415 // State changed applied, notify outer class. 416 postScreenUpdateThreadSafe(); 417 mStateChangeInProgress = false; 418 } 419 if (!backlightChanged) { 420 mBacklightChangeInProgress = false; 421 } 422 if (!stateChanged && !backlightChanged) { 423 try { 424 mLock.wait(); 425 } catch (InterruptedException ex) { } 426 continue; 427 } 428 mActualState = state; 429 mActualBacklight = backlight; 430 } 431 432 // Apply pending change. 433 if (DEBUG) { 434 Slog.d(TAG, "Updating screen state: state=" 435 + Display.stateToString(state) + ", backlight=" + backlight); 436 } 437 mBlanker.requestDisplayState(state, backlight); 438 } 439 } 440 } 441 } 442