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