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