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 mScreenReady = true; 308 invokeCleanListenerIfNeeded(); 309 } 310 } 311 }; 312 313 private final Runnable mElectronBeamDrawRunnable = new Runnable() { 314 @Override 315 public void run() { 316 mElectronBeamDrawPending = false; 317 318 if (mElectronBeamPrepared) { 319 mElectronBeam.draw(mElectronBeamLevel); 320 } 321 322 mElectronBeamReady = true; 323 invokeCleanListenerIfNeeded(); 324 } 325 }; 326 327 /** 328 * Updates the state of the screen and backlight asynchronously on a separate thread. 329 */ 330 private final class PhotonicModulator { 331 private static final boolean INITIAL_SCREEN_ON = false; // unknown, assume off 332 private static final int INITIAL_BACKLIGHT = -1; // unknown 333 334 private final Object mLock = new Object(); 335 336 private boolean mPendingOn = INITIAL_SCREEN_ON; 337 private int mPendingBacklight = INITIAL_BACKLIGHT; 338 private boolean mActualOn = INITIAL_SCREEN_ON; 339 private int mActualBacklight = INITIAL_BACKLIGHT; 340 private boolean mChangeInProgress; 341 342 public boolean setState(boolean on, int backlight) { 343 synchronized (mLock) { 344 if (on != mPendingOn || backlight != mPendingBacklight) { 345 if (DEBUG) { 346 Slog.d(TAG, "Requesting new screen state: on=" + on 347 + ", backlight=" + backlight); 348 } 349 350 mPendingOn = on; 351 mPendingBacklight = backlight; 352 353 if (!mChangeInProgress) { 354 mChangeInProgress = true; 355 AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask); 356 } 357 } 358 return mChangeInProgress; 359 } 360 } 361 362 public void dump(PrintWriter pw) { 363 pw.println(); 364 pw.println("Photonic Modulator State:"); 365 pw.println(" mPendingOn=" + mPendingOn); 366 pw.println(" mPendingBacklight=" + mPendingBacklight); 367 pw.println(" mActualOn=" + mActualOn); 368 pw.println(" mActualBacklight=" + mActualBacklight); 369 pw.println(" mChangeInProgress=" + mChangeInProgress); 370 } 371 372 private final Runnable mTask = new Runnable() { 373 @Override 374 public void run() { 375 // Apply pending changes until done. 376 for (;;) { 377 final boolean on; 378 final boolean onChanged; 379 final int backlight; 380 final boolean backlightChanged; 381 synchronized (mLock) { 382 on = mPendingOn; 383 onChanged = (on != mActualOn); 384 backlight = mPendingBacklight; 385 backlightChanged = (backlight != mActualBacklight); 386 if (!onChanged && !backlightChanged) { 387 mChangeInProgress = false; 388 break; 389 } 390 mActualOn = on; 391 mActualBacklight = backlight; 392 } 393 394 if (DEBUG) { 395 Slog.d(TAG, "Updating screen state: on=" + on 396 + ", backlight=" + backlight); 397 } 398 if (onChanged && on) { 399 mDisplayBlanker.unblankAllDisplays(); 400 } 401 if (backlightChanged) { 402 mBacklight.setBrightness(backlight); 403 } 404 if (onChanged && !on) { 405 mDisplayBlanker.blankAllDisplays(); 406 } 407 } 408 409 // Let the outer class know that all changes have been applied. 410 postScreenUpdateThreadSafe(); 411 } 412 }; 413 } 414 } 415