DisplayPowerController.java revision 9630704ed3b265f008a8f64ec60a33cf9dcd3345
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.power;
18
19import com.android.server.LightsService;
20
21import android.animation.Animator;
22import android.animation.ObjectAnimator;
23import android.content.Context;
24import android.content.res.Resources;
25import android.hardware.Sensor;
26import android.hardware.SensorEvent;
27import android.hardware.SensorEventListener;
28import android.hardware.SensorManager;
29import android.hardware.SystemSensorManager;
30import android.os.AsyncTask;
31import android.os.Handler;
32import android.os.Looper;
33import android.os.Message;
34import android.os.SystemClock;
35import android.util.Slog;
36import android.util.TimeUtils;
37
38import java.io.PrintWriter;
39import java.io.StringWriter;
40import java.util.Arrays;
41import java.util.concurrent.CountDownLatch;
42import java.util.concurrent.Executor;
43
44/**
45 * Controls the power state of the display.
46 *
47 * Handles the proximity sensor, light sensor, and animations between states
48 * including the screen off animation.
49 *
50 * This component acts independently of the rest of the power manager service.
51 * In particular, it does not share any state and it only communicates
52 * via asynchronous callbacks to inform the power manager that something has
53 * changed.
54 *
55 * Everything this class does internally is serialized on its handler although
56 * it may be accessed by other threads from the outside.
57 *
58 * Note that the power manager service guarantees that it will hold a suspend
59 * blocker as long as the display is not ready.  So most of the work done here
60 * does not need to worry about holding a suspend blocker unless it happens
61 * independently of the display ready signal.
62 *
63 * For debugging, you can make the electron beam and brightness animations run
64 * slower by changing the "animator duration scale" option in Development Settings.
65 */
66final class DisplayPowerController {
67    private static final String TAG = "DisplayPowerController";
68
69    private static boolean DEBUG = false;
70    private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
71    private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
72
73    private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 300;
74    private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 600;
75
76    private static final int MSG_UPDATE_POWER_STATE = 1;
77    private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
78    private static final int MSG_LIGHT_SENSOR_DEBOUNCED = 3;
79
80    private static final int PROXIMITY_UNKNOWN = -1;
81    private static final int PROXIMITY_NEGATIVE = 0;
82    private static final int PROXIMITY_POSITIVE = 1;
83
84    // Proximity sensor debounce delay in milliseconds.
85    private static final int PROXIMITY_SENSOR_DEBOUNCE_DELAY = 250;
86
87    // Trigger proximity if distance is less than 5 cm.
88    private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
89
90    // Light sensor event rate in microseconds.
91    private static final int LIGHT_SENSOR_RATE = 1000000;
92
93    // Brightness animation ramp rate in brightness units per second.
94    private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
95    private static final int BRIGHTNESS_RAMP_RATE_SLOW = 50;
96
97    // Filter time constant in milliseconds for computing a moving
98    // average of light samples.  Different constants are used
99    // to adapt to brighter or dimmer environments.
100    private static final long BRIGHTENING_LIGHT_TIME_CONSTANT = 2500; // 2.5 sec
101    private static final long DIMMING_LIGHT_TIME_CONSTANT = 10000; // 10 sec
102
103    private final Object mLock = new Object();
104
105    // Notifier for sending asynchronous notifications.
106    private final Notifier mNotifier;
107
108    // A suspend blocker.
109    private final SuspendBlocker mSuspendBlocker;
110
111    // Our handler.
112    private final DisplayControllerHandler mHandler;
113
114    // Asynchronous callbacks into the power manager service.
115    // Only invoked from the handler thread while no locks are held.
116    private final Callbacks mCallbacks;
117    private Handler mCallbackHandler;
118
119    // The lights service.
120    private final LightsService mLights;
121
122    // The sensor manager.
123    private final SensorManager mSensorManager;
124
125    // The proximity sensor, or null if not available or needed.
126    private Sensor mProximitySensor;
127
128    // The light sensor, or null if not available or needed.
129    private Sensor mLightSensor;
130
131    // The dim screen brightness.
132    private final int mScreenBrightnessDimConfig;
133
134    // Auto-brightness.
135    private boolean mUseSoftwareAutoBrightnessConfig;
136    private int[] mAutoBrightnessLevelsConfig;
137    private int[] mAutoBrightnessLcdBacklightValuesConfig;
138
139    // Amount of time to delay auto-brightness after screen on while waiting for
140    // the light sensor to warm-up in milliseconds.
141    // May be 0 if no warm-up is required.
142    private int mLightSensorWarmUpTimeConfig;
143
144    // The pending power request.
145    // Initially null until the first call to requestPowerState.
146    // Guarded by mLock.
147    private DisplayPowerRequest mPendingRequestLocked;
148
149    // True if a request has been made to wait for the proximity sensor to go negative.
150    // Guarded by mLock.
151    private boolean mPendingWaitForNegativeProximityLocked;
152
153    // True if the pending power request or wait for negative proximity flag
154    // has been changed since the last update occurred.
155    // Guarded by mLock.
156    private boolean mPendingRequestChangedLocked;
157
158    // Set to true when the important parts of the pending power request have been applied.
159    // The important parts are mainly the screen state.  Brightness changes may occur
160    // concurrently.
161    // Guarded by mLock.
162    private boolean mDisplayReadyLocked;
163
164    // Set to true if a power state update is required.
165    // Guarded by mLock.
166    private boolean mPendingUpdatePowerStateLocked;
167
168    /* The following state must only be accessed by the handler thread. */
169
170    // The currently requested power state.
171    // The power controller will progressively update its internal state to match
172    // the requested power state.  Initially null until the first update.
173    private DisplayPowerRequest mPowerRequest;
174
175    // The current power state.
176    // Must only be accessed on the handler thread.
177    private DisplayPowerState mPowerState;
178
179    // True if the device should wait for negative proximity sensor before
180    // waking up the screen.  This is set to false as soon as a negative
181    // proximity sensor measurement is observed or when the device is forced to
182    // go to sleep by the user.  While true, the screen remains off.
183    private boolean mWaitingForNegativeProximity;
184
185    // The actual proximity sensor threshold value.
186    private float mProximityThreshold;
187
188    // Set to true if the proximity sensor listener has been registered
189    // with the sensor manager.
190    private boolean mProximitySensorEnabled;
191
192    // The debounced proximity sensor state.
193    private int mProximity = PROXIMITY_UNKNOWN;
194
195    // The raw non-debounced proximity sensor state.
196    private int mPendingProximity = PROXIMITY_UNKNOWN;
197    private long mPendingProximityDebounceTime;
198
199    // True if the screen was turned off because of the proximity sensor.
200    // When the screen turns on again, we report user activity to the power manager.
201    private boolean mScreenOffBecauseOfProximity;
202
203    // Set to true if the light sensor is enabled.
204    private boolean mLightSensorEnabled;
205
206    // The time when the light sensor was enabled.
207    private long mLightSensorEnableTime;
208
209    // The currently accepted average light sensor value.
210    private float mLightMeasurement;
211
212    // True if the light sensor measurement is valid.
213    private boolean mLightMeasurementValid;
214
215    // The number of light sensor samples that have been collected since the
216    // last time a light sensor reading was accepted.
217    private int mRecentLightSamples;
218
219    // The moving average of recent light sensor values.
220    private float mRecentLightAverage;
221
222    // True if recent light samples are getting brighter than the previous
223    // stable light measurement.
224    private boolean mRecentLightBrightening;
225
226    // The time constant to use for filtering based on whether the
227    // light appears to be brightening or dimming.
228    private long mRecentLightTimeConstant;
229
230    // The most recent light sample.
231    private float mLastLightSample;
232
233    // The time of the most light recent sample.
234    private long mLastLightSampleTime;
235
236    // The upcoming debounce light sensor time.
237    // This is only valid when mLightMeasurementValue && mRecentLightSamples >= 1.
238    private long mPendingLightSensorDebounceTime;
239
240    // The screen brightness level that has been chosen by the auto-brightness
241    // algorithm.  The actual brightness should ramp towards this value.
242    // We preserve this value even when we stop using the light sensor so
243    // that we can quickly revert to the previous auto-brightness level
244    // while the light sensor warms up.
245    // Use -1 if there is no current auto-brightness value available.
246    private int mScreenAutoBrightness = -1;
247
248    // True if the screen auto-brightness value is actually being used to
249    // set the display brightness.
250    private boolean mUsingScreenAutoBrightness;
251
252    // Animators.
253    private ObjectAnimator mElectronBeamOnAnimator;
254    private ObjectAnimator mElectronBeamOffAnimator;
255    private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
256
257    /**
258     * Creates the display power controller.
259     */
260    public DisplayPowerController(Looper looper, Context context, Notifier notifier,
261            LightsService lights, SuspendBlocker suspendBlocker,
262            Callbacks callbacks, Handler callbackHandler) {
263        mHandler = new DisplayControllerHandler(looper);
264        mNotifier = notifier;
265        mSuspendBlocker = suspendBlocker;
266        mCallbacks = callbacks;
267        mCallbackHandler = callbackHandler;
268
269        mLights = lights;
270        mSensorManager = new SystemSensorManager(mHandler.getLooper());
271
272        final Resources resources = context.getResources();
273        mScreenBrightnessDimConfig = resources.getInteger(
274                com.android.internal.R.integer.config_screenBrightnessDim);
275        mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
276                com.android.internal.R.bool.config_automatic_brightness_available);
277        if (mUseSoftwareAutoBrightnessConfig) {
278            mAutoBrightnessLevelsConfig = resources.getIntArray(
279                    com.android.internal.R.array.config_autoBrightnessLevels);
280            mAutoBrightnessLcdBacklightValuesConfig = resources.getIntArray(
281                    com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
282            if (mAutoBrightnessLcdBacklightValuesConfig.length
283                    != mAutoBrightnessLevelsConfig.length + 1) {
284                Slog.e(TAG, "Error in config.xml.  config_autoBrightnessLcdBacklightValues "
285                        + "(size " + mAutoBrightnessLcdBacklightValuesConfig.length + ") "
286                        + "should have exactly one more entry than "
287                        + "config_autoBrightnessLevels (size "
288                        + mAutoBrightnessLevelsConfig.length + ").  "
289                        + "Auto-brightness will be disabled.");
290                mUseSoftwareAutoBrightnessConfig = false;
291            }
292
293            mLightSensorWarmUpTimeConfig = resources.getInteger(
294                    com.android.internal.R.integer.config_lightSensorWarmupTime);
295        }
296
297        if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
298            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
299            if (mProximitySensor != null) {
300                mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
301                        TYPICAL_PROXIMITY_THRESHOLD);
302            }
303        }
304
305        if (mUseSoftwareAutoBrightnessConfig
306                && !DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
307            mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
308        }
309    }
310
311    /**
312     * Returns true if the proximity sensor screen-off function is available.
313     */
314    public boolean isProximitySensorAvailable() {
315        return mProximitySensor != null;
316    }
317
318    /**
319     * Requests a new power state.
320     * The controller makes a copy of the provided object and then
321     * begins adjusting the power state to match what was requested.
322     *
323     * @param request The requested power state.
324     * @param waitForNegativeProximity If true, issues a request to wait for
325     * negative proximity before turning the screen back on, assuming the screen
326     * was turned off by the proximity sensor.
327     * @return True if display is ready, false if there are important changes that must
328     * be made asynchronously (such as turning the screen on), in which case the caller
329     * should grab a wake lock, watch for {@link Callbacks#onStateChanged()} then try
330     * the request again later until the state converges.
331     */
332    public boolean requestPowerState(DisplayPowerRequest request,
333            boolean waitForNegativeProximity) {
334        if (DEBUG) {
335            Slog.d(TAG, "requestPowerState: "
336                    + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
337        }
338
339        synchronized (mLock) {
340            boolean changed = false;
341
342            if (waitForNegativeProximity
343                    && !mPendingWaitForNegativeProximityLocked) {
344                mPendingWaitForNegativeProximityLocked = true;
345                changed = true;
346            }
347
348            if (mPendingRequestLocked == null) {
349                mPendingRequestLocked = new DisplayPowerRequest(request);
350                changed = true;
351            } else if (!mPendingRequestLocked.equals(request)) {
352                mPendingRequestLocked.copyFrom(request);
353                changed = true;
354            }
355
356            if (changed) {
357                mDisplayReadyLocked = false;
358            }
359
360            if (changed && !mPendingRequestChangedLocked) {
361                mPendingRequestChangedLocked = true;
362                sendUpdatePowerStateLocked();
363            }
364
365            return mDisplayReadyLocked;
366        }
367    }
368
369    private void sendUpdatePowerState() {
370        synchronized (mLock) {
371            sendUpdatePowerStateLocked();
372        }
373    }
374
375    private void sendUpdatePowerStateLocked() {
376        if (!mPendingUpdatePowerStateLocked) {
377            mPendingUpdatePowerStateLocked = true;
378            Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
379            msg.setAsynchronous(true);
380            mHandler.sendMessage(msg);
381        }
382    }
383
384    private void initialize() {
385        final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
386        mPowerState = new DisplayPowerState(new ElectronBeam(),
387                new PhotonicModulator(executor,
388                        mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
389                        mSuspendBlocker));
390
391        mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
392                mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
393        mElectronBeamOnAnimator.setDuration(ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS);
394        mElectronBeamOnAnimator.addListener(mAnimatorListener);
395
396        mElectronBeamOffAnimator = ObjectAnimator.ofFloat(
397                mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 1.0f, 0.0f);
398        mElectronBeamOffAnimator.setDuration(ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS);
399        mElectronBeamOffAnimator.addListener(mAnimatorListener);
400
401        mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
402                mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
403    }
404
405    private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
406        @Override
407        public void onAnimationStart(Animator animation) {
408        }
409        @Override
410        public void onAnimationEnd(Animator animation) {
411            sendUpdatePowerState();
412        }
413        @Override
414        public void onAnimationRepeat(Animator animation) {
415        }
416        @Override
417        public void onAnimationCancel(Animator animation) {
418        }
419    };
420
421    private void updatePowerState() {
422        // Update the power state request.
423        final boolean mustNotify;
424        boolean mustInitialize = false;
425        synchronized (mLock) {
426            mPendingUpdatePowerStateLocked = false;
427            if (mPendingRequestLocked == null) {
428                return; // wait until first actual power request
429            }
430
431            if (mPowerRequest == null) {
432                mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
433                mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;
434                mPendingRequestChangedLocked = false;
435                mustInitialize = true;
436            } else if (mPendingRequestChangedLocked) {
437                mPowerRequest.copyFrom(mPendingRequestLocked);
438                mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
439                mPendingRequestChangedLocked = false;
440                mDisplayReadyLocked = false;
441            }
442
443            mustNotify = !mDisplayReadyLocked;
444        }
445
446        // Initialize things the first time the power state is changed.
447        if (mustInitialize) {
448            initialize();
449        }
450
451        // Clear a request to wait for negative proximity if needed.
452        if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_OFF
453                || mProximity == PROXIMITY_NEGATIVE
454                || mProximitySensor == null) {
455            mWaitingForNegativeProximity = false;
456        }
457
458        // Turn on the proximity sensor if needed.
459        if (mProximitySensor != null) {
460            setProximitySensorEnabled(mPowerRequest.useProximitySensor
461                    || mWaitingForNegativeProximity);
462            if (mProximitySensorEnabled && mProximity == PROXIMITY_POSITIVE) {
463                mScreenOffBecauseOfProximity = true;
464                setScreenOn(false);
465            } else if (mScreenOffBecauseOfProximity) {
466                mScreenOffBecauseOfProximity = false;
467                sendOnProximityNegative();
468            }
469        }
470
471        // Turn on the light sensor if needed.
472        if (mLightSensor != null) {
473            setLightSensorEnabled(mPowerRequest.useAutoBrightness
474                    && wantScreenOn(mPowerRequest.screenState));
475        }
476
477        // Set the screen brightness.
478        if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
479            // Screen is dimmed.  Overrides everything else.
480            animateScreenBrightness(mScreenBrightnessDimConfig, BRIGHTNESS_RAMP_RATE_FAST);
481            mUsingScreenAutoBrightness = false;
482        } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) {
483            if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
484                // Use current auto-brightness value.
485                animateScreenBrightness(
486                        Math.max(mScreenAutoBrightness, mScreenBrightnessDimConfig),
487                        mUsingScreenAutoBrightness ? BRIGHTNESS_RAMP_RATE_SLOW :
488                                BRIGHTNESS_RAMP_RATE_FAST);
489                mUsingScreenAutoBrightness = true;
490            } else {
491                // Light sensor is disabled or not ready yet.
492                // Use the current brightness setting from the request, which is expected
493                // provide a nominal default value for the case where auto-brightness
494                // is not ready yet.
495                animateScreenBrightness(
496                        Math.max(mPowerRequest.screenBrightness, mScreenBrightnessDimConfig),
497                        BRIGHTNESS_RAMP_RATE_FAST);
498                mUsingScreenAutoBrightness = false;
499            }
500        } else {
501            // Screen is off.  Don't bother changing the brightness.
502            mUsingScreenAutoBrightness = false;
503        }
504
505        // Animate the screen on or off.
506        if (!mScreenOffBecauseOfProximity) {
507            if (wantScreenOn(mPowerRequest.screenState)) {
508                // Want screen on.
509                // Wait for previous off animation to complete beforehand.
510                // It is relatively short but if we cancel it and switch to the
511                // on animation immediately then the results are pretty ugly.
512                if (!mElectronBeamOffAnimator.isStarted()) {
513                    setScreenOn(true);
514                    if (!mElectronBeamOnAnimator.isStarted()) {
515                        if (mPowerState.getElectronBeamLevel() == 1.0f) {
516                            mPowerState.dismissElectronBeam();
517                        } else if (mPowerState.prepareElectronBeam(true)) {
518                            mElectronBeamOnAnimator.start();
519                        } else {
520                            mElectronBeamOnAnimator.end();
521                        }
522                    }
523                }
524            } else {
525                // Want screen off.
526                // Wait for previous on animation to complete beforehand.
527                if (!mElectronBeamOnAnimator.isStarted()) {
528                    if (!mElectronBeamOffAnimator.isStarted()) {
529                        if (mPowerState.getElectronBeamLevel() == 0.0f) {
530                            setScreenOn(false);
531                        } else if (mPowerState.prepareElectronBeam(false)
532                                && mPowerState.isScreenOn()) {
533                            mElectronBeamOffAnimator.start();
534                        } else {
535                            mElectronBeamOffAnimator.end();
536                        }
537                    }
538                }
539            }
540        }
541
542        // Report whether the display is ready for use.
543        // We mostly care about the screen state here, ignoring brightness changes
544        // which will be handled asynchronously.
545        if (mustNotify
546                && !mElectronBeamOnAnimator.isStarted()
547                && !mElectronBeamOffAnimator.isStarted()
548                && mPowerState.waitUntilClean(mCleanListener)) {
549            synchronized (mLock) {
550                if (!mPendingRequestChangedLocked) {
551                    mDisplayReadyLocked = true;
552                }
553            }
554            sendOnStateChanged();
555        }
556    }
557
558    private void setScreenOn(boolean on) {
559        if (!mPowerState.isScreenOn() == on) {
560            mPowerState.setScreenOn(on);
561            if (on) {
562                mNotifier.onScreenOn();
563            } else {
564                mNotifier.onScreenOff();
565            }
566        }
567    }
568
569    private void animateScreenBrightness(int target, int rate) {
570        if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
571            mNotifier.onScreenBrightness(target);
572        }
573    }
574
575    private final Runnable mCleanListener = new Runnable() {
576        @Override
577        public void run() {
578            sendUpdatePowerState();
579        }
580    };
581
582    private void setProximitySensorEnabled(boolean enable) {
583        if (enable) {
584            if (!mProximitySensorEnabled) {
585                mProximitySensorEnabled = true;
586                mPendingProximity = PROXIMITY_UNKNOWN;
587                mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
588                        SensorManager.SENSOR_DELAY_NORMAL, mHandler);
589            }
590        } else {
591            if (mProximitySensorEnabled) {
592                mProximitySensorEnabled = false;
593                mProximity = PROXIMITY_UNKNOWN;
594                mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
595                mSensorManager.unregisterListener(mProximitySensorListener);
596            }
597        }
598    }
599
600    private void handleProximitySensorEvent(long time, boolean positive) {
601        if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
602            return; // no change
603        }
604        if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
605            return; // no change
606        }
607
608        // Only accept a proximity sensor reading if it remains
609        // stable for the entire debounce delay.
610        mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
611        mPendingProximity = positive ? PROXIMITY_POSITIVE : PROXIMITY_NEGATIVE;
612        mPendingProximityDebounceTime = time + PROXIMITY_SENSOR_DEBOUNCE_DELAY;
613        debounceProximitySensor();
614    }
615
616    private void debounceProximitySensor() {
617        if (mPendingProximity != PROXIMITY_UNKNOWN) {
618            final long now = SystemClock.uptimeMillis();
619            if (mPendingProximityDebounceTime <= now) {
620                mProximity = mPendingProximity;
621                sendUpdatePowerState();
622            } else {
623                Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
624                msg.setAsynchronous(true);
625                mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
626            }
627        }
628    }
629
630    private void setLightSensorEnabled(boolean enable) {
631        if (enable) {
632            if (!mLightSensorEnabled) {
633                mLightSensorEnabled = true;
634                mLightSensorEnableTime = SystemClock.uptimeMillis();
635                mSensorManager.registerListener(mLightSensorListener, mLightSensor,
636                        LIGHT_SENSOR_RATE, mHandler);
637            }
638        } else {
639            if (mLightSensorEnabled) {
640                mLightSensorEnabled = false;
641                mLightMeasurementValid = false;
642                updateAutoBrightness(false);
643                mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
644                mSensorManager.unregisterListener(mLightSensorListener);
645            }
646        }
647    }
648
649    private void handleLightSensorEvent(long time, float lux) {
650        // Take the first few readings during the warm-up period and apply them
651        // immediately without debouncing.
652        if (!mLightMeasurementValid
653                || (time - mLightSensorEnableTime) < mLightSensorWarmUpTimeConfig) {
654            mLightMeasurement = lux;
655            mLightMeasurementValid = true;
656            mRecentLightSamples = 0;
657            updateAutoBrightness(true);
658        }
659
660        // Update our moving average.
661        if (lux != mLightMeasurement && (mRecentLightSamples == 0
662                || (lux < mLightMeasurement && mRecentLightBrightening)
663                || (lux > mLightMeasurement && !mRecentLightBrightening))) {
664            // If the newest light sample doesn't seem to be going in the
665            // same general direction as recent samples, then start over.
666            setRecentLight(time, lux, lux > mLightMeasurement);
667            mPendingLightSensorDebounceTime = time + mRecentLightTimeConstant;
668        } else if (mRecentLightSamples >= 1) {
669            // Add the newest light sample to the moving average.
670            accumulateRecentLight(time, lux);
671        }
672        if (DEBUG) {
673            Slog.d(TAG, "handleLightSensorEvent: lux=" + lux
674                    + ", mLightMeasurementValid=" + mLightMeasurementValid
675                    + ", mLightMeasurement=" + mLightMeasurement
676                    + ", mRecentLightSamples=" + mRecentLightSamples
677                    + ", mRecentLightAverage=" + mRecentLightAverage
678                    + ", mRecentLightBrightening=" + mRecentLightBrightening
679                    + ", mRecentLightTimeConstant=" + mRecentLightTimeConstant
680                    + ", mPendingLightSensorDebounceTime="
681                            + TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
682        }
683
684        // Debounce.
685        mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
686        debounceLightSensor();
687    }
688
689    private void setRecentLight(long time, float lux, boolean brightening) {
690        mRecentLightBrightening = brightening;
691        mRecentLightTimeConstant = brightening ?
692                BRIGHTENING_LIGHT_TIME_CONSTANT : DIMMING_LIGHT_TIME_CONSTANT;
693        mRecentLightSamples = 1;
694        mRecentLightAverage = lux;
695        mLastLightSample = lux;
696        mLastLightSampleTime = time;
697    }
698
699    private void accumulateRecentLight(long time, float lux) {
700        final long timeDelta = time - mLastLightSampleTime;
701        mRecentLightSamples += 1;
702        mRecentLightAverage += (lux - mRecentLightAverage) *
703                timeDelta / (mRecentLightTimeConstant + timeDelta);
704        mLastLightSample = lux;
705        mLastLightSampleTime = time;
706    }
707
708    private void debounceLightSensor() {
709        if (mLightMeasurementValid && mRecentLightSamples >= 1) {
710            final long now = SystemClock.uptimeMillis();
711            if (mPendingLightSensorDebounceTime <= now) {
712                accumulateRecentLight(now, mLastLightSample);
713                mLightMeasurement = mRecentLightAverage;
714
715                if (DEBUG) {
716                    Slog.d(TAG, "debounceLightSensor: Accepted new measurement "
717                            + mLightMeasurement + " after "
718                            + (now - mPendingLightSensorDebounceTime
719                                    + mRecentLightTimeConstant) + " ms based on "
720                            + mRecentLightSamples + " recent samples.");
721                }
722
723                updateAutoBrightness(true);
724
725                // Now that we have debounced the light sensor data, we have the
726                // option of either leaving the sensor in a debounced state or
727                // restarting the debounce cycle by setting mRecentLightSamples to 0.
728                //
729                // If we leave the sensor debounced, then new average light measurements
730                // may be accepted immediately as long as they are trending in the same
731                // direction as they were before.  If the measurements start
732                // jittering or trending in the opposite direction then the debounce
733                // cycle will automatically be restarted.  The benefit is that the
734                // auto-brightness control can be more responsive to changes over a
735                // broad range.
736                //
737                // For now, we choose to be more responsive and leave the following line
738                // commented out.
739                //
740                // mRecentLightSamples = 0;
741            } else {
742                Message msg = mHandler.obtainMessage(MSG_LIGHT_SENSOR_DEBOUNCED);
743                msg.setAsynchronous(true);
744                mHandler.sendMessageAtTime(msg, mPendingLightSensorDebounceTime);
745            }
746        }
747    }
748
749    private void updateAutoBrightness(boolean sendUpdate) {
750        if (!mLightMeasurementValid) {
751            return;
752        }
753
754        final int newScreenAutoBrightness = mapLuxToBrightness(mLightMeasurement,
755                mAutoBrightnessLevelsConfig,
756                mAutoBrightnessLcdBacklightValuesConfig);
757        if (mScreenAutoBrightness != newScreenAutoBrightness) {
758            if (DEBUG) {
759                Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
760                        + mScreenAutoBrightness);
761            }
762
763            mScreenAutoBrightness = newScreenAutoBrightness;
764            if (sendUpdate) {
765                sendUpdatePowerState();
766            }
767        }
768    }
769
770    /**
771     * Maps a light sensor measurement in lux to a brightness value given
772     * a table of lux breakpoint values and a table of brightnesses that
773     * is one element larger.
774     */
775    private static int mapLuxToBrightness(float lux,
776            int[] fromLux, int[] toBrightness) {
777        // TODO implement interpolation and possibly range expansion
778        int level = 0;
779        final int count = fromLux.length;
780        while (level < count && lux >= fromLux[level]) {
781            level += 1;
782        }
783        return toBrightness[level];
784    }
785
786    private void sendOnStateChanged() {
787        mCallbackHandler.post(mOnStateChangedRunnable);
788    }
789
790    private final Runnable mOnStateChangedRunnable = new Runnable() {
791        @Override
792        public void run() {
793            mCallbacks.onStateChanged();
794        }
795    };
796
797    private void sendOnProximityNegative() {
798        mCallbackHandler.post(mOnProximityNegativeRunnable);
799    }
800
801    private final Runnable mOnProximityNegativeRunnable = new Runnable() {
802        @Override
803        public void run() {
804            mCallbacks.onProximityNegative();
805        }
806    };
807
808    public void dump(PrintWriter pw) {
809        synchronized (mLock) {
810            pw.println();
811            pw.println("Display Controller Locked State:");
812            pw.println("  mDisplayReadyLocked=" + mDisplayReadyLocked);
813            pw.println("  mPendingRequestLocked=" + mPendingRequestLocked);
814            pw.println("  mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
815            pw.println("  mPendingWaitForNegativeProximityLocked="
816                    + mPendingWaitForNegativeProximityLocked);
817            pw.println("  mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
818        }
819
820        pw.println();
821        pw.println("Display Controller Configuration:");
822        pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
823        pw.println("  mUseSoftwareAutoBrightnessConfig="
824                + mUseSoftwareAutoBrightnessConfig);
825        pw.println("  mAutoBrightnessLevelsConfig="
826                + Arrays.toString(mAutoBrightnessLevelsConfig));
827        pw.println("  mAutoBrightnessLcdBacklightValuesConfig="
828                + Arrays.toString(mAutoBrightnessLcdBacklightValuesConfig));
829        pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
830
831        if (Looper.myLooper() == mHandler.getLooper()) {
832            dumpLocal(pw);
833        } else {
834            final StringWriter out = new StringWriter();
835            final CountDownLatch latch = new CountDownLatch(1);
836            Message msg = Message.obtain(mHandler,  new Runnable() {
837                @Override
838                public void run() {
839                    PrintWriter localpw = new PrintWriter(out);
840                    try {
841                        dumpLocal(localpw);
842                    } finally {
843                        localpw.flush();
844                        latch.countDown();
845                    }
846                }
847            });
848            msg.setAsynchronous(true);
849            mHandler.sendMessage(msg);
850            try {
851                latch.await();
852                pw.print(out.toString());
853            } catch (InterruptedException ex) {
854                pw.println();
855                pw.println("Failed to dump thread state due to interrupted exception!");
856            }
857        }
858    }
859
860    private void dumpLocal(PrintWriter pw) {
861        pw.println();
862        pw.println("Display Controller Thread State:");
863        pw.println("  mPowerRequest=" + mPowerRequest);
864        pw.println("  mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
865
866        pw.println("  mProximitySensor=" + mProximitySensor);
867        pw.println("  mProximitySensorEnabled=" + mProximitySensorEnabled);
868        pw.println("  mProximityThreshold=" + mProximityThreshold);
869        pw.println("  mProximity=" + proximityToString(mProximity));
870        pw.println("  mPendingProximity=" + proximityToString(mPendingProximity));
871        pw.println("  mPendingProximityDebounceTime="
872                + TimeUtils.formatUptime(mPendingProximityDebounceTime));
873        pw.println("  mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
874
875        pw.println("  mLightSensor=" + mLightSensor);
876        pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
877        pw.println("  mLightSensorEnableTime="
878                + TimeUtils.formatUptime(mLightSensorEnableTime));
879        pw.println("  mLightMeasurement=" + mLightMeasurement);
880        pw.println("  mLightMeasurementValid=" + mLightMeasurementValid);
881        pw.println("  mLastLightSample=" + mLastLightSample);
882        pw.println("  mLastLightSampleTime="
883                + TimeUtils.formatUptime(mLastLightSampleTime));
884        pw.println("  mRecentLightSamples=" + mRecentLightSamples);
885        pw.println("  mRecentLightAverage=" + mRecentLightAverage);
886        pw.println("  mRecentLightBrightening=" + mRecentLightBrightening);
887        pw.println("  mRecentLightTimeConstant=" + mRecentLightTimeConstant);
888        pw.println("  mPendingLightSensorDebounceTime="
889                + TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
890        pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
891        pw.println("  mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
892
893        if (mElectronBeamOnAnimator != null) {
894            pw.println("  mElectronBeamOnAnimator.isStarted()=" +
895                    mElectronBeamOnAnimator.isStarted());
896        }
897        if (mElectronBeamOffAnimator != null) {
898            pw.println("  mElectronBeamOffAnimator.isStarted()=" +
899                    mElectronBeamOffAnimator.isStarted());
900        }
901
902        if (mPowerState != null) {
903            mPowerState.dump(pw);
904        }
905    }
906
907    private static String proximityToString(int state) {
908        switch (state) {
909            case PROXIMITY_UNKNOWN:
910                return "Unknown";
911            case PROXIMITY_NEGATIVE:
912                return "Negative";
913            case PROXIMITY_POSITIVE:
914                return "Positive";
915            default:
916                return Integer.toString(state);
917        }
918    }
919
920    private static boolean wantScreenOn(int state) {
921        switch (state) {
922            case DisplayPowerRequest.SCREEN_STATE_BRIGHT:
923            case DisplayPowerRequest.SCREEN_STATE_DIM:
924                return true;
925        }
926        return false;
927    }
928
929    /**
930     * Asynchronous callbacks from the power controller to the power manager service.
931     */
932    public interface Callbacks {
933        void onStateChanged();
934        void onProximityNegative();
935    }
936
937    private final class DisplayControllerHandler extends Handler {
938        public DisplayControllerHandler(Looper looper) {
939            super(looper);
940        }
941
942        @Override
943        public void handleMessage(Message msg) {
944            switch (msg.what) {
945                case MSG_UPDATE_POWER_STATE:
946                    updatePowerState();
947                    break;
948
949                case MSG_PROXIMITY_SENSOR_DEBOUNCED:
950                    debounceProximitySensor();
951                    break;
952
953                case MSG_LIGHT_SENSOR_DEBOUNCED:
954                    debounceLightSensor();
955                    break;
956            }
957        }
958    }
959
960    private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
961        @Override
962        public void onSensorChanged(SensorEvent event) {
963            if (mProximitySensorEnabled) {
964                final long time = SystemClock.uptimeMillis();
965                final float distance = event.values[0];
966                boolean positive = distance >= 0.0f && distance < mProximityThreshold;
967                handleProximitySensorEvent(time, positive);
968            }
969        }
970
971        @Override
972        public void onAccuracyChanged(Sensor sensor, int accuracy) {
973            // Not used.
974        }
975    };
976
977    private final SensorEventListener mLightSensorListener = new SensorEventListener() {
978        @Override
979        public void onSensorChanged(SensorEvent event) {
980            if (mLightSensorEnabled) {
981                final long time = SystemClock.uptimeMillis();
982                final float lux = event.values[0];
983                handleLightSensorEvent(time, lux);
984            }
985        }
986
987        @Override
988        public void onAccuracyChanged(Sensor sensor, int accuracy) {
989            // Not used.
990        }
991    };
992}
993