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