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