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.power; 18 19 import com.android.server.LightsService; 20 21 import android.os.AsyncTask; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.PowerManager; 25 import android.util.FloatProperty; 26 import android.util.IntProperty; 27 import android.util.Slog; 28 import android.view.Choreographer; 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 View} except that it describes 36 * 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 * {@link PowerManagerService} does that for us whenever there is a change 47 * in progress. 48 * </p> 49 */ 50 final 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 ElectronBeam mElectronBeam; 58 private final DisplayBlanker mDisplayBlanker; 59 private final LightsService.Light mBacklight; 60 private final PhotonicModulator mPhotonicModulator; 61 62 private boolean mScreenOn; 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(ElectronBeam electronBean, 75 DisplayBlanker displayBlanker, LightsService.Light backlight) { 76 mHandler = new Handler(true /*async*/); 77 mChoreographer = Choreographer.getInstance(); 78 mElectronBeam = electronBean; 79 mDisplayBlanker = displayBlanker; 80 mBacklight = backlight; 81 mPhotonicModulator = new PhotonicModulator(); 82 83 // At boot time, we know that the screen is on and the electron beam 84 // animation is not playing. We don't know the screen's brightness though, 85 // so prepare to set it to a known state when the state is next applied. 86 // Although we set the brightness to full on here, the display power controller 87 // will reset the brightness to a new level immediately before the changes 88 // actually have a chance to be applied. 89 mScreenOn = true; 90 mScreenBrightness = PowerManager.BRIGHTNESS_ON; 91 scheduleScreenUpdate(); 92 93 mElectronBeamPrepared = false; 94 mElectronBeamLevel = 1.0f; 95 mElectronBeamReady = true; 96 } 97 98 public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL = 99 new FloatProperty<DisplayPowerState>("electronBeamLevel") { 100 @Override 101 public void setValue(DisplayPowerState object, float value) { 102 object.setElectronBeamLevel(value); 103 } 104 105 @Override 106 public Float get(DisplayPowerState object) { 107 return object.getElectronBeamLevel(); 108 } 109 }; 110 111 public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS = 112 new IntProperty<DisplayPowerState>("screenBrightness") { 113 @Override 114 public void setValue(DisplayPowerState object, int value) { 115 object.setScreenBrightness(value); 116 } 117 118 @Override 119 public Integer get(DisplayPowerState object) { 120 return object.getScreenBrightness(); 121 } 122 }; 123 124 /** 125 * Sets whether the screen is on or off. 126 */ 127 public void setScreenOn(boolean on) { 128 if (mScreenOn != on) { 129 if (DEBUG) { 130 Slog.d(TAG, "setScreenOn: on=" + on); 131 } 132 133 mScreenOn = on; 134 mScreenReady = false; 135 scheduleScreenUpdate(); 136 } 137 } 138 139 /** 140 * Returns true if the screen is on. 141 */ 142 public boolean isScreenOn() { 143 return mScreenOn; 144 } 145 146 /** 147 * Sets the display brightness. 148 * 149 * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). 150 */ 151 public void setScreenBrightness(int brightness) { 152 if (mScreenBrightness != brightness) { 153 if (DEBUG) { 154 Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); 155 } 156 157 mScreenBrightness = brightness; 158 if (mScreenOn) { 159 mScreenReady = false; 160 scheduleScreenUpdate(); 161 } 162 } 163 } 164 165 /** 166 * Gets the screen brightness. 167 */ 168 public int getScreenBrightness() { 169 return mScreenBrightness; 170 } 171 172 /** 173 * Prepares the electron beam to turn on or off. 174 * This method should be called before starting an animation because it 175 * can take a fair amount of time to prepare the electron beam surface. 176 * 177 * @param mode The electron beam animation mode to prepare. 178 * @return True if the electron beam was prepared. 179 */ 180 public boolean prepareElectronBeam(int mode) { 181 if (!mElectronBeam.prepare(mode)) { 182 mElectronBeamPrepared = false; 183 mElectronBeamReady = true; 184 return false; 185 } 186 187 mElectronBeamPrepared = true; 188 mElectronBeamReady = false; 189 scheduleElectronBeamDraw(); 190 return true; 191 } 192 193 /** 194 * Dismisses the electron beam surface. 195 */ 196 public void dismissElectronBeam() { 197 mElectronBeam.dismiss(); 198 mElectronBeamPrepared = false; 199 mElectronBeamReady = true; 200 } 201 202 /** 203 * Sets the level of the electron beam steering current. 204 * 205 * The display is blanked when the level is 0.0. In normal use, the electron 206 * beam should have a value of 1.0. The electron beam is unstable in between 207 * these states and the picture quality may be compromised. For best effect, 208 * the electron beam should be warmed up or cooled off slowly. 209 * 210 * Warning: Electron beam emits harmful radiation. Avoid direct exposure to 211 * skin or eyes. 212 * 213 * @param level The level, ranges from 0.0 (full off) to 1.0 (full on). 214 */ 215 public void setElectronBeamLevel(float level) { 216 if (mElectronBeamLevel != level) { 217 if (DEBUG) { 218 Slog.d(TAG, "setElectronBeamLevel: level=" + level); 219 } 220 221 mElectronBeamLevel = level; 222 if (mScreenOn) { 223 mScreenReady = false; 224 scheduleScreenUpdate(); // update backlight brightness 225 } 226 if (mElectronBeamPrepared) { 227 mElectronBeamReady = false; 228 scheduleElectronBeamDraw(); 229 } 230 } 231 } 232 233 /** 234 * Gets the level of the electron beam steering current. 235 */ 236 public float getElectronBeamLevel() { 237 return mElectronBeamLevel; 238 } 239 240 /** 241 * Returns true if no properties have been invalidated. 242 * Otherwise, returns false and promises to invoke the specified listener 243 * when the properties have all been applied. 244 * The listener always overrides any previously set listener. 245 */ 246 public boolean waitUntilClean(Runnable listener) { 247 if (!mScreenReady || !mElectronBeamReady) { 248 mCleanListener = listener; 249 return false; 250 } else { 251 mCleanListener = null; 252 return true; 253 } 254 } 255 256 public void dump(PrintWriter pw) { 257 pw.println(); 258 pw.println("Display Power State:"); 259 pw.println(" mScreenOn=" + mScreenOn); 260 pw.println(" mScreenBrightness=" + mScreenBrightness); 261 pw.println(" mScreenReady=" + mScreenReady); 262 pw.println(" mScreenUpdatePending=" + mScreenUpdatePending); 263 pw.println(" mElectronBeamPrepared=" + mElectronBeamPrepared); 264 pw.println(" mElectronBeamLevel=" + mElectronBeamLevel); 265 pw.println(" mElectronBeamReady=" + mElectronBeamReady); 266 pw.println(" mElectronBeamDrawPending=" + mElectronBeamDrawPending); 267 268 mPhotonicModulator.dump(pw); 269 mElectronBeam.dump(pw); 270 } 271 272 private void scheduleScreenUpdate() { 273 if (!mScreenUpdatePending) { 274 mScreenUpdatePending = true; 275 postScreenUpdateThreadSafe(); 276 } 277 } 278 279 private void postScreenUpdateThreadSafe() { 280 mHandler.removeCallbacks(mScreenUpdateRunnable); 281 mHandler.post(mScreenUpdateRunnable); 282 } 283 284 private void scheduleElectronBeamDraw() { 285 if (!mElectronBeamDrawPending) { 286 mElectronBeamDrawPending = true; 287 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, 288 mElectronBeamDrawRunnable, null); 289 } 290 } 291 292 private void invokeCleanListenerIfNeeded() { 293 final Runnable listener = mCleanListener; 294 if (listener != null && mScreenReady && mElectronBeamReady) { 295 mCleanListener = null; 296 listener.run(); 297 } 298 } 299 300 private final Runnable mScreenUpdateRunnable = new Runnable() { 301 @Override 302 public void run() { 303 mScreenUpdatePending = false; 304 305 int brightness = mScreenOn && mElectronBeamLevel > 0f ? mScreenBrightness : 0; 306 if (mPhotonicModulator.setState(mScreenOn, 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 boolean INITIAL_SCREEN_ON = false; // unknown, assume off 339 private static final int INITIAL_BACKLIGHT = -1; // unknown 340 341 private final Object mLock = new Object(); 342 343 private boolean mPendingOn = INITIAL_SCREEN_ON; 344 private int mPendingBacklight = INITIAL_BACKLIGHT; 345 private boolean mActualOn = INITIAL_SCREEN_ON; 346 private int mActualBacklight = INITIAL_BACKLIGHT; 347 private boolean mChangeInProgress; 348 349 public boolean setState(boolean on, int backlight) { 350 synchronized (mLock) { 351 if (on != mPendingOn || backlight != mPendingBacklight) { 352 if (DEBUG) { 353 Slog.d(TAG, "Requesting new screen state: on=" + on 354 + ", backlight=" + backlight); 355 } 356 357 mPendingOn = on; 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(" mPendingOn=" + mPendingOn); 373 pw.println(" mPendingBacklight=" + mPendingBacklight); 374 pw.println(" mActualOn=" + mActualOn); 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 boolean on; 385 final boolean onChanged; 386 final int backlight; 387 final boolean backlightChanged; 388 synchronized (mLock) { 389 on = mPendingOn; 390 onChanged = (on != mActualOn); 391 backlight = mPendingBacklight; 392 backlightChanged = (backlight != mActualBacklight); 393 if (!onChanged && !backlightChanged) { 394 mChangeInProgress = false; 395 break; 396 } 397 mActualOn = on; 398 mActualBacklight = backlight; 399 } 400 401 if (DEBUG) { 402 Slog.d(TAG, "Updating screen state: on=" + on 403 + ", backlight=" + backlight); 404 } 405 if (onChanged && on) { 406 mDisplayBlanker.unblankAllDisplays(); 407 } 408 if (backlightChanged) { 409 mBacklight.setBrightness(backlight); 410 } 411 if (onChanged && !on) { 412 mDisplayBlanker.blankAllDisplays(); 413 } 414 } 415 416 // Let the outer class know that all changes have been applied. 417 postScreenUpdateThreadSafe(); 418 } 419 }; 420 } 421 } 422