19630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown/* 29630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Copyright (C) 2012 The Android Open Source Project 39630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * 49630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 59630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * you may not use this file except in compliance with the License. 69630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * You may obtain a copy of the License at 79630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * 89630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 99630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * 109630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Unless required by applicable law or agreed to in writing, software 119630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 129630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * See the License for the specific language governing permissions and 149630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * limitations under the License. 159630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 169630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 17ad9ef191f50767d8d5b6f0fbd4b59bb1400dcd25Jeff Brownpackage com.android.server.display; 189630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 19182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskiimport com.android.server.lights.Light; 2032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 210839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentineimport android.content.Context; 2232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brownimport android.os.AsyncTask; 2332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brownimport android.os.Handler; 249630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.os.Looper; 259630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.os.PowerManager; 263edf5272fb2185403dfe64b9722b9fc9b9de80f8Jeff Brownimport android.os.Trace; 279630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.util.FloatProperty; 289630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.util.IntProperty; 299630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.util.Slog; 309630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.view.Choreographer; 31037c33eae74bee2774897d969d48947f9abe254fJeff Brownimport android.view.Display; 329630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 339630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport java.io.PrintWriter; 349630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 359630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown/** 3632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * Controls the display power state. 3732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * <p> 38ad9ef191f50767d8d5b6f0fbd4b59bb1400dcd25Jeff Brown * This component is similar in nature to a {@link android.view.View} except that it 39ad9ef191f50767d8d5b6f0fbd4b59bb1400dcd25Jeff Brown * describes the properties of a display. When properties are changed, the component 4032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * invalidates itself and posts a callback to apply the changes in a consistent order. 4132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * This mechanism enables multiple properties of the display power state to be animated 4232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * together smoothly by the animation framework. Some of the work to blank or unblank 4332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * the display is done on a separate thread to avoid blocking the looper. 4432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * </p><p> 459630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * This component must only be created or accessed by the {@link Looper} thread 469630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * that belongs to the {@link DisplayPowerController}. 4732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * </p><p> 489630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * We don't need to worry about holding a suspend blocker here because the 49ad9ef191f50767d8d5b6f0fbd4b59bb1400dcd25Jeff Brown * power manager does that for us whenever there is a change in progress. 5032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * </p> 519630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 529630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownfinal class DisplayPowerState { 539630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown private static final String TAG = "DisplayPowerState"; 549630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 559630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown private static boolean DEBUG = false; 569630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 5732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private final Handler mHandler; 589630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown private final Choreographer mChoreographer; 59037c33eae74bee2774897d969d48947f9abe254fJeff Brown private final DisplayBlanker mBlanker; 60182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski private final Light mBacklight; 610839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine private final ColorFade mColorFade; 6232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private final PhotonicModulator mPhotonicModulator; 639630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 64037c33eae74bee2774897d969d48947f9abe254fJeff Brown private int mScreenState; 659630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown private int mScreenBrightness; 6632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private boolean mScreenReady; 6732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private boolean mScreenUpdatePending; 6832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 690839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine private boolean mColorFadePrepared; 700839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine private float mColorFadeLevel; 710839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine private boolean mColorFadeReady; 720839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine private boolean mColorFadeDrawPending; 739630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 749630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown private Runnable mCleanListener; 759630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 760839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine public DisplayPowerState(DisplayBlanker blanker, Light backlight, ColorFade electronBeam) { 7732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mHandler = new Handler(true /*async*/); 789630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown mChoreographer = Choreographer.getInstance(); 79037c33eae74bee2774897d969d48947f9abe254fJeff Brown mBlanker = blanker; 8032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mBacklight = backlight; 810839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFade = electronBeam; 8232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mPhotonicModulator = new PhotonicModulator(); 830a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mPhotonicModulator.start(); 849630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 85f75724b3d36d84c881d4052cfd4be766d454c98fJeff Brown // At boot time, we know that the screen is on and the electron beam 86f75724b3d36d84c881d4052cfd4be766d454c98fJeff Brown // animation is not playing. We don't know the screen's brightness though, 87f75724b3d36d84c881d4052cfd4be766d454c98fJeff Brown // so prepare to set it to a known state when the state is next applied. 88f75724b3d36d84c881d4052cfd4be766d454c98fJeff Brown // Although we set the brightness to full on here, the display power controller 89f75724b3d36d84c881d4052cfd4be766d454c98fJeff Brown // will reset the brightness to a new level immediately before the changes 90f75724b3d36d84c881d4052cfd4be766d454c98fJeff Brown // actually have a chance to be applied. 91037c33eae74bee2774897d969d48947f9abe254fJeff Brown mScreenState = Display.STATE_ON; 929630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown mScreenBrightness = PowerManager.BRIGHTNESS_ON; 9332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown scheduleScreenUpdate(); 9432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 950839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadePrepared = false; 960839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeLevel = 1.0f; 970839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeReady = true; 989630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 999630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1000839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine public static final FloatProperty<DisplayPowerState> COLOR_FADE_LEVEL = 1019630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown new FloatProperty<DisplayPowerState>("electronBeamLevel") { 1029630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown @Override 1039630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public void setValue(DisplayPowerState object, float value) { 1040839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine object.setColorFadeLevel(value); 1059630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1069630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1079630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown @Override 1089630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public Float get(DisplayPowerState object) { 1090839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine return object.getColorFadeLevel(); 1109630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1119630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown }; 1129630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1139630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS = 1149630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown new IntProperty<DisplayPowerState>("screenBrightness") { 1159630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown @Override 1169630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public void setValue(DisplayPowerState object, int value) { 1179630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown object.setScreenBrightness(value); 1189630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1199630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1209630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown @Override 1219630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public Integer get(DisplayPowerState object) { 1229630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown return object.getScreenBrightness(); 1239630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1249630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown }; 1259630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1269630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown /** 127037c33eae74bee2774897d969d48947f9abe254fJeff Brown * Sets whether the screen is on, off, or dozing. 1289630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 129037c33eae74bee2774897d969d48947f9abe254fJeff Brown public void setScreenState(int state) { 130037c33eae74bee2774897d969d48947f9abe254fJeff Brown if (mScreenState != state) { 1319630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown if (DEBUG) { 132037c33eae74bee2774897d969d48947f9abe254fJeff Brown Slog.d(TAG, "setScreenState: state=" + state); 1339630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1349630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 135037c33eae74bee2774897d969d48947f9abe254fJeff Brown mScreenState = state; 13632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mScreenReady = false; 13732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown scheduleScreenUpdate(); 1389630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1399630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1409630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1419630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown /** 142037c33eae74bee2774897d969d48947f9abe254fJeff Brown * Gets the desired screen state. 1439630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 144037c33eae74bee2774897d969d48947f9abe254fJeff Brown public int getScreenState() { 145037c33eae74bee2774897d969d48947f9abe254fJeff Brown return mScreenState; 1469630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1479630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1489630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown /** 14932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * Sets the display brightness. 15032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * 15132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest). 15232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown */ 15332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown public void setScreenBrightness(int brightness) { 15432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown if (mScreenBrightness != brightness) { 15532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown if (DEBUG) { 15632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown Slog.d(TAG, "setScreenBrightness: brightness=" + brightness); 15732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 15832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 15932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mScreenBrightness = brightness; 160037c33eae74bee2774897d969d48947f9abe254fJeff Brown if (mScreenState != Display.STATE_OFF) { 16132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mScreenReady = false; 16232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown scheduleScreenUpdate(); 16332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 16432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 16532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 16632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 16732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown /** 16832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * Gets the screen brightness. 16932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown */ 17032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown public int getScreenBrightness() { 17132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown return mScreenBrightness; 17232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 17332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 17432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown /** 1759630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Prepares the electron beam to turn on or off. 1769630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * This method should be called before starting an animation because it 1779630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * can take a fair amount of time to prepare the electron beam surface. 1789630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * 1798b9cf1c8000eb581457713a5c0ce41c59f90c353Jeff Brown * @param mode The electron beam animation mode to prepare. 1809630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * @return True if the electron beam was prepared. 1819630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 1820839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine public boolean prepareColorFade(Context context, int mode) { 1830839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine if (!mColorFade.prepare(context, mode)) { 1840839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadePrepared = false; 1850839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeReady = true; 18632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown return false; 18732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 18832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 1890839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadePrepared = true; 1900839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeReady = false; 1910839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine scheduleColorFadeDraw(); 19232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown return true; 1939630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 1949630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 1959630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown /** 1969630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Dismisses the electron beam surface. 1979630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 1980839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine public void dismissColorFade() { 1990839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFade.dismiss(); 2000839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadePrepared = false; 2010839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeReady = true; 2029630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2039630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 2049630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown /** 2059630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Sets the level of the electron beam steering current. 2069630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * 2079630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * The display is blanked when the level is 0.0. In normal use, the electron 2089630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * beam should have a value of 1.0. The electron beam is unstable in between 2099630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * these states and the picture quality may be compromised. For best effect, 2109630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * the electron beam should be warmed up or cooled off slowly. 2119630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * 2129630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Warning: Electron beam emits harmful radiation. Avoid direct exposure to 2139630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * skin or eyes. 2149630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * 2159630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * @param level The level, ranges from 0.0 (full off) to 1.0 (full on). 2169630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 2170839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine public void setColorFadeLevel(float level) { 2180839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine if (mColorFadeLevel != level) { 2199630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown if (DEBUG) { 2200839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine Slog.d(TAG, "setColorFadeLevel: level=" + level); 2219630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2229630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 2230839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeLevel = level; 224037c33eae74bee2774897d969d48947f9abe254fJeff Brown if (mScreenState != Display.STATE_OFF) { 22532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mScreenReady = false; 22632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown scheduleScreenUpdate(); // update backlight brightness 22732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 2280839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine if (mColorFadePrepared) { 2290839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeReady = false; 2300839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine scheduleColorFadeDraw(); 23132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 2329630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2339630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2349630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 2359630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown /** 2369630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Gets the level of the electron beam steering current. 2379630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 2380839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine public float getColorFadeLevel() { 2390839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine return mColorFadeLevel; 2409630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2419630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 2429630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown /** 2439630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Returns true if no properties have been invalidated. 2449630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Otherwise, returns false and promises to invoke the specified listener 2459630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * when the properties have all been applied. 2469630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * The listener always overrides any previously set listener. 2479630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */ 2489630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public boolean waitUntilClean(Runnable listener) { 2490839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine if (!mScreenReady || !mColorFadeReady) { 2509630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown mCleanListener = listener; 2519630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown return false; 2529630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } else { 2539630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown mCleanListener = null; 2549630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown return true; 2559630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2569630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2579630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 2589630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown public void dump(PrintWriter pw) { 2599630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown pw.println(); 2609630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown pw.println("Display Power State:"); 261037c33eae74bee2774897d969d48947f9abe254fJeff Brown pw.println(" mScreenState=" + Display.stateToString(mScreenState)); 2629630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown pw.println(" mScreenBrightness=" + mScreenBrightness); 26332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown pw.println(" mScreenReady=" + mScreenReady); 26432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown pw.println(" mScreenUpdatePending=" + mScreenUpdatePending); 2650839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine pw.println(" mColorFadePrepared=" + mColorFadePrepared); 2660839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine pw.println(" mColorFadeLevel=" + mColorFadeLevel); 2670839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine pw.println(" mColorFadeReady=" + mColorFadeReady); 2680839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine pw.println(" mColorFadeDrawPending=" + mColorFadeDrawPending); 2699630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 27032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mPhotonicModulator.dump(pw); 2710839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFade.dump(pw); 2729630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 2739630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 27432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private void scheduleScreenUpdate() { 27532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown if (!mScreenUpdatePending) { 27632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mScreenUpdatePending = true; 27732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown postScreenUpdateThreadSafe(); 27832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 27932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 28032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 28132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private void postScreenUpdateThreadSafe() { 28232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mHandler.removeCallbacks(mScreenUpdateRunnable); 28332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mHandler.post(mScreenUpdateRunnable); 28432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 28532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 2860839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine private void scheduleColorFadeDraw() { 2870839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine if (!mColorFadeDrawPending) { 2880839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeDrawPending = true; 2899630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, 2900839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeDrawRunnable, null); 2919630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 29232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 2939630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 29432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private void invokeCleanListenerIfNeeded() { 29532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown final Runnable listener = mCleanListener; 2960839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine if (listener != null && mScreenReady && mColorFadeReady) { 29732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mCleanListener = null; 29832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown listener.run(); 29932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 3009630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 3019630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 30232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private final Runnable mScreenUpdateRunnable = new Runnable() { 30332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown @Override 30432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown public void run() { 30532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mScreenUpdatePending = false; 3069630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 307037c33eae74bee2774897d969d48947f9abe254fJeff Brown int brightness = mScreenState != Display.STATE_OFF 3080839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine && mColorFadeLevel > 0f ? mScreenBrightness : 0; 309037c33eae74bee2774897d969d48947f9abe254fJeff Brown if (mPhotonicModulator.setState(mScreenState, brightness)) { 3102d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown if (DEBUG) { 3112d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown Slog.d(TAG, "Screen ready"); 3122d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown } 31332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mScreenReady = true; 31432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown invokeCleanListenerIfNeeded(); 3152d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown } else { 3162d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown if (DEBUG) { 3172d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown Slog.d(TAG, "Screen not ready"); 3182d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown } 3199630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 32032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 32132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown }; 3229630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 3230839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine private final Runnable mColorFadeDrawRunnable = new Runnable() { 32432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown @Override 32532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown public void run() { 3260839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeDrawPending = false; 3279630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 3280839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine if (mColorFadePrepared) { 3290839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFade.draw(mColorFadeLevel); 330735f740fe81b7172d0b208d584eecf632533ec4aJeff Brown } 331735f740fe81b7172d0b208d584eecf632533ec4aJeff Brown 3320839adb25c6d1a9f4e06fc5a098ffd03c67dbe99Michael Lentine mColorFadeReady = true; 33332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown invokeCleanListenerIfNeeded(); 33432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 33532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown }; 3369630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 33732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown /** 33832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown * Updates the state of the screen and backlight asynchronously on a separate thread. 33932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown */ 3400a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown private final class PhotonicModulator extends Thread { 341037c33eae74bee2774897d969d48947f9abe254fJeff Brown private static final int INITIAL_SCREEN_STATE = Display.STATE_OFF; // unknown, assume off 34232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private static final int INITIAL_BACKLIGHT = -1; // unknown 34332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 34432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private final Object mLock = new Object(); 34532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 346037c33eae74bee2774897d969d48947f9abe254fJeff Brown private int mPendingState = INITIAL_SCREEN_STATE; 34732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private int mPendingBacklight = INITIAL_BACKLIGHT; 348037c33eae74bee2774897d969d48947f9abe254fJeff Brown private int mActualState = INITIAL_SCREEN_STATE; 34932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private int mActualBacklight = INITIAL_BACKLIGHT; 35032dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown private boolean mChangeInProgress; 35132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 352037c33eae74bee2774897d969d48947f9abe254fJeff Brown public boolean setState(int state, int backlight) { 35332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown synchronized (mLock) { 354037c33eae74bee2774897d969d48947f9abe254fJeff Brown if (state != mPendingState || backlight != mPendingBacklight) { 35532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown if (DEBUG) { 356037c33eae74bee2774897d969d48947f9abe254fJeff Brown Slog.d(TAG, "Requesting new screen state: state=" 357037c33eae74bee2774897d969d48947f9abe254fJeff Brown + Display.stateToString(state) + ", backlight=" + backlight); 35832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 35932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 360037c33eae74bee2774897d969d48947f9abe254fJeff Brown mPendingState = state; 36132dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mPendingBacklight = backlight; 36232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 36332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown if (!mChangeInProgress) { 36432dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown mChangeInProgress = true; 3650a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mLock.notifyAll(); 36632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 36732dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 3682d8a3908d2b0a74ccdecd97e86e7bfda1caa218eJeff Brown return !mChangeInProgress; 3699630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 3709630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 3719630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown 37232dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown public void dump(PrintWriter pw) { 3730a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown synchronized (mLock) { 3740a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown pw.println(); 3750a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown pw.println("Photonic Modulator State:"); 3760a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown pw.println(" mPendingState=" + Display.stateToString(mPendingState)); 3770a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown pw.println(" mPendingBacklight=" + mPendingBacklight); 3780a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown pw.println(" mActualState=" + Display.stateToString(mActualState)); 3790a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown pw.println(" mActualBacklight=" + mActualBacklight); 3800a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown pw.println(" mChangeInProgress=" + mChangeInProgress); 3810a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 3829630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown } 38332dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 3840a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown @Override 3850a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown public void run() { 3860a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown for (;;) { 3870a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown // Get pending change. 3880a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown final int state; 3890a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown final boolean stateChanged; 3900a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown final int backlight; 3910a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown final boolean backlightChanged; 3920a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown synchronized (mLock) { 3930a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown state = mPendingState; 3940a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown stateChanged = (state != mActualState); 3950a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown backlight = mPendingBacklight; 3960a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown backlightChanged = (backlight != mActualBacklight); 3970a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown if (!stateChanged && !backlightChanged) { 3980a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown // All changed applied, notify outer class and wait for more. 3990a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mChangeInProgress = false; 4000a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown postScreenUpdateThreadSafe(); 4010a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown try { 4020a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mLock.wait(); 4030a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } catch (InterruptedException ex) { } 4040a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown continue; 40532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 4060a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mActualState = state; 4070a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mActualBacklight = backlight; 40832dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 40932dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown 4100a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown // Apply pending change. 4110a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown if (DEBUG) { 4120a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown Slog.d(TAG, "Updating screen state: state=" 4130a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown + Display.stateToString(state) + ", backlight=" + backlight); 4140a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 4150a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown boolean suspending = Display.isSuspendedState(state); 4160a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown if (stateChanged && !suspending) { 4170a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown requestDisplayState(state); 4180a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 4190a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown if (backlightChanged) { 4200a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown setBrightness(backlight); 4210a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 4220a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown if (stateChanged && suspending) { 4230a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown requestDisplayState(state); 4240a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 42532dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 4260a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 4273edf5272fb2185403dfe64b9722b9fc9b9de80f8Jeff Brown 4280a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown private void requestDisplayState(int state) { 4290a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestDisplayState(" 4300a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown + Display.stateToString(state) + ")"); 4310a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown try { 4320a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mBlanker.requestDisplayState(state); 4330a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } finally { 4340a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown Trace.traceEnd(Trace.TRACE_TAG_POWER); 4353edf5272fb2185403dfe64b9722b9fc9b9de80f8Jeff Brown } 4360a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 4373edf5272fb2185403dfe64b9722b9fc9b9de80f8Jeff Brown 4380a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown private void setBrightness(int backlight) { 4390a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown Trace.traceBegin(Trace.TRACE_TAG_POWER, "setBrightness(" + backlight + ")"); 4400a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown try { 4410a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown mBacklight.setBrightness(backlight); 4420a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } finally { 4430a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown Trace.traceEnd(Trace.TRACE_TAG_POWER); 4443edf5272fb2185403dfe64b9722b9fc9b9de80f8Jeff Brown } 4450a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown } 44632dafe25ac2e06f127f48d6a5826537e11575f52Jeff Brown } 4479630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown} 448