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