AutomaticBrightnessController.java revision 970d4132ea28e748c1010be39450a98bbf7466f3
1639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright/*
2639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * Copyright (C) 2014 The Android Open Source Project
3639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright *
4639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * Licensed under the Apache License, Version 2.0 (the "License");
5639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * you may not use this file except in compliance with the License.
6639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * You may obtain a copy of the License at
7639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright *
8639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright *      http://www.apache.org/licenses/LICENSE-2.0
9639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright *
10639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * Unless required by applicable law or agreed to in writing, software
11639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * distributed under the License is distributed on an "AS IS" BASIS,
12639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * See the License for the specific language governing permissions and
14639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright * limitations under the License.
15639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright */
16639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
17131206b8a9d07400d7c98aea50cc45c38769448fJeff Brownpackage com.android.server.display;
18639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
19131206b8a9d07400d7c98aea50cc45c38769448fJeff Brownimport com.android.server.LocalServices;
20639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport com.android.server.twilight.TwilightListener;
21639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport com.android.server.twilight.TwilightManager;
22639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport com.android.server.twilight.TwilightState;
23639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
24639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.content.res.Resources;
25639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.hardware.Sensor;
26639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.hardware.SensorEvent;
27639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.hardware.SensorEventListener;
28639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.hardware.SensorManager;
29639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.os.Handler;
30639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.os.Looper;
31639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.os.Message;
32639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.os.PowerManager;
33639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.os.SystemClock;
34639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.text.format.DateUtils;
35639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.util.MathUtils;
36639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.util.Spline;
37639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.util.Slog;
38639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport android.util.TimeUtils;
39639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
40639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport java.io.PrintWriter;
41639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightimport java.util.Arrays;
42639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
43639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wrightclass AutomaticBrightnessController {
44639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final String TAG = "AutomaticBrightnessController";
45639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
46639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final boolean DEBUG = false;
47639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
48639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
49639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // If true, enables the use of the screen auto-brightness adjustment setting.
50daf7d410fc97647f2b3ab4254f73c09c923018deAdrian Roos    private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
51639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
52639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The maximum range of gamma adjustment possible using the screen
53639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // auto-brightness adjustment setting.
54639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
55639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
56639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Light sensor event rate in milliseconds.
57639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final int LIGHT_SENSOR_RATE_MILLIS = 1000;
58639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
59639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Period of time in which to consider light samples in milliseconds.
60639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final int AMBIENT_LIGHT_HORIZON = 10000;
61639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
62639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Stability requirements in milliseconds for accepting a new brightness level.  This is used
63639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // for debouncing the light sensor.  Different constants are used to debounce the light sensor
64639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // when adapting to brighter or darker environments.  This parameter controls how quickly
65639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // brightness changes occur in response to an observed change in light level that exceeds the
66639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // hysteresis threshold.
67639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final long BRIGHTENING_LIGHT_DEBOUNCE = 4000;
68639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final long DARKENING_LIGHT_DEBOUNCE = 8000;
69639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
70639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Hysteresis constraints for brightening or darkening.
71639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The recent lux must have changed by at least this fraction relative to the
72639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // current ambient lux before a change will be considered.
73639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
74639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
75639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
76639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The intercept used for the weighting calculation. This is used in order to keep all possible
77639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // weighting values positive.
78639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final int WEIGHTING_INTERCEPT = AMBIENT_LIGHT_HORIZON;
79639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
80639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // How long the current sensor reading is assumed to be valid beyond the current time.
81639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // This provides a bit of prediction, as well as ensures that the weight for the last sample is
82639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // non-zero, which in turn ensures that the total weight is non-zero.
83639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
84639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
85639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // If true, enables the use of the current time as an auto-brightness adjustment.
86639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The basic idea here is to expand the dynamic range of auto-brightness
87639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // when it is especially dark outside.  The light sensor tends to perform
88639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // poorly at low light levels so we compensate for it by making an
89639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // assumption about the environment.
90639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final boolean USE_TWILIGHT_ADJUSTMENT =
91639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            PowerManager.useTwilightAdjustmentFeature();
92639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
93639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Specifies the maximum magnitude of the time of day adjustment.
94639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1.5f;
95639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
96639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The amount of time after or before sunrise over which to start adjusting
97639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // the gamma.  We want the change to happen gradually so that it is below the
98639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // threshold of perceptibility and so that the adjustment has maximum effect
99639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // well after dusk.
100639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2;
101639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
102639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final int MSG_UPDATE_AMBIENT_LUX = 1;
103639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
104639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Callbacks for requesting updates to the the display's power state
105639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final Callbacks mCallbacks;
106639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
107639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The sensor manager.
108639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final SensorManager mSensorManager;
109639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
110639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The light sensor, or null if not available or needed.
111639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final Sensor mLightSensor;
112639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
113639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The twilight service.
114639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final TwilightManager mTwilight;
115639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
116639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The auto-brightness spline adjustment.
117639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The brightness values have been scaled to a range of 0..1.
118639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final Spline mScreenAutoBrightnessSpline;
119639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
120639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The minimum and maximum screen brightnesses.
121639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final int mScreenBrightnessRangeMinimum;
122639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final int mScreenBrightnessRangeMaximum;
123639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
124639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Amount of time to delay auto-brightness after screen on while waiting for
125639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // the light sensor to warm-up in milliseconds.
126639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // May be 0 if no warm-up is required.
127639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private int mLightSensorWarmUpTimeConfig;
128639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
129639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Set to true if the light sensor is enabled.
130639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private boolean mLightSensorEnabled;
131639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
132639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The time when the light sensor was enabled.
133639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private long mLightSensorEnableTime;
134639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
135639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The currently accepted nominal ambient light level.
136639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private float mAmbientLux;
137639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
138639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // True if mAmbientLux holds a valid value.
139639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private boolean mAmbientLuxValid;
140639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
141639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The ambient light level threshold at which to brighten or darken the screen.
142639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private float mBrighteningLuxThreshold;
143639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private float mDarkeningLuxThreshold;
144639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
145639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The most recent light sample.
146639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private float mLastObservedLux;
147639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
148639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The time of the most light recent sample.
149639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private long mLastObservedLuxTime;
150639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
151639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The number of light samples collected since the light sensor was enabled.
152639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private int mRecentLightSamples;
153639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
154639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // A ring buffer containing all of the recent ambient light sensor readings.
155639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private AmbientLightRingBuffer mAmbientLightRingBuffer;
156639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
157639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The handler
158639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private AutomaticBrightnessHandler mHandler;
159639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
160639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The screen brightness level that has been chosen by the auto-brightness
161639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // algorithm.  The actual brightness should ramp towards this value.
162639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // We preserve this value even when we stop using the light sensor so
163639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // that we can quickly revert to the previous auto-brightness level
164639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // while the light sensor warms up.
165639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Use -1 if there is no current auto-brightness value available.
166639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private int mScreenAutoBrightness = -1;
167639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
168639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
169639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private float mScreenAutoBrightnessAdjustment = 0.0f;
170639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
171639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // The last screen auto-brightness gamma.  (For printing in dump() only.)
172639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private float mLastScreenAutoBrightnessGamma = 1.0f;
173639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
174639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
175131206b8a9d07400d7c98aea50cc45c38769448fJeff Brown            SensorManager sensorManager, Spline autoBrightnessSpline,
176639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            int lightSensorWarmUpTime, int brightnessMin, int brightnessMax) {
177639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mCallbacks = callbacks;
178131206b8a9d07400d7c98aea50cc45c38769448fJeff Brown        mTwilight = LocalServices.getService(TwilightManager.class);
179639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mSensorManager = sensorManager;
180639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mScreenAutoBrightnessSpline = autoBrightnessSpline;
181639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mScreenBrightnessRangeMinimum = brightnessMin;
182639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mScreenBrightnessRangeMaximum = brightnessMax;
183639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
184639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
185639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mHandler = new AutomaticBrightnessHandler(looper);
186639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mAmbientLightRingBuffer = new AmbientLightRingBuffer();
187639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
188639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
189639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
190639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
191639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
192639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (USE_TWILIGHT_ADJUSTMENT) {
193639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mTwilight.registerListener(mTwilightListener, mHandler);
194639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
195639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
196639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
197639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    public int getAutomaticScreenBrightness() {
198639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return mScreenAutoBrightness;
199639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
200639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
201970d4132ea28e748c1010be39450a98bbf7466f3Jeff Brown    public void configure(boolean enable, float adjustment) {
202970d4132ea28e748c1010be39450a98bbf7466f3Jeff Brown        boolean changed = setLightSensorEnabled(enable);
203970d4132ea28e748c1010be39450a98bbf7466f3Jeff Brown        changed |= setScreenAutoBrightnessAdjustment(adjustment);
204970d4132ea28e748c1010be39450a98bbf7466f3Jeff Brown        if (changed) {
205639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            updateAutoBrightness(false /*sendUpdate*/);
206639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
207639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
208639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
209639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    public void dump(PrintWriter pw) {
210639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println();
211639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("Automatic Brightness Controller Configuration:");
212639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
213639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
214639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
215639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
216639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
217639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println();
218639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("Automatic Brightness Controller State:");
219639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mLightSensor=" + mLightSensor);
220639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
221639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
222639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
223639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mAmbientLux=" + mAmbientLux);
224639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
225639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
226639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mLastObservedLux=" + mLastObservedLux);
227639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
228639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mRecentLightSamples=" + mRecentLightSamples);
229639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
230639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
231639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
232639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        pw.println("  mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
233639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
234639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
235639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private boolean setLightSensorEnabled(boolean enable) {
236639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (enable) {
237639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (!mLightSensorEnabled) {
238639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mLightSensorEnabled = true;
239639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mLightSensorEnableTime = SystemClock.uptimeMillis();
240639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mSensorManager.registerListener(mLightSensorListener, mLightSensor,
241639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        LIGHT_SENSOR_RATE_MILLIS * 1000, mHandler);
242639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                return true;
243639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
244639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        } else {
245639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mLightSensorEnabled) {
246639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mLightSensorEnabled = false;
247639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mAmbientLuxValid = false;
248639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mRecentLightSamples = 0;
249639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mAmbientLightRingBuffer.clear();
250639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
251639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mSensorManager.unregisterListener(mLightSensorListener);
252639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
253639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
254639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return false;
255639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
256639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
257639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private void handleLightSensorEvent(long time, float lux) {
258639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
259639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
260639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        applyLightSensorMeasurement(time, lux);
261639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        updateAmbientLux(time);
262639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
263639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
264639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private void applyLightSensorMeasurement(long time, float lux) {
265639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mRecentLightSamples++;
266639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
267639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mAmbientLightRingBuffer.push(time, lux);
268639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
269639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // Remember this sample value.
270639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mLastObservedLux = lux;
271639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mLastObservedLuxTime = time;
272639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
273639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
274639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
275639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (adjustment != mScreenAutoBrightnessAdjustment) {
276639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mScreenAutoBrightnessAdjustment = adjustment;
277639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return true;
278639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
279639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return false;
280639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
281639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
282639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private void setAmbientLux(float lux) {
283639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mAmbientLux = lux;
284639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
285639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
286639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
287639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
288639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private float calculateAmbientLux(long now) {
289639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        final int N = mAmbientLightRingBuffer.size();
290639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (N == 0) {
291639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
292639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return -1;
293639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
294639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        float sum = 0;
295639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        float totalWeight = 0;
296639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
297639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        for (int i = N - 1; i >= 0; i--) {
298639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            long startTime = (mAmbientLightRingBuffer.getTime(i) - now);
299639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            float weight = calculateWeight(startTime, endTime);
300639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            float lux = mAmbientLightRingBuffer.getLux(i);
301639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (DEBUG) {
302639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                Slog.d(TAG, "calculateAmbientLux: [" +
303639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        (startTime) + ", " +
304639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        (endTime) + "]: lux=" + lux + ", weight=" + weight);
305639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
306639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            totalWeight += weight;
307639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            sum += mAmbientLightRingBuffer.getLux(i) * weight;
308639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            endTime = startTime;
309639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
310639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (DEBUG) {
311639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
312639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    ", newAmbientLux=" + (sum / totalWeight));
313639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
314639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return sum / totalWeight;
315639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
316639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
317639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static float calculateWeight(long startDelta, long endDelta) {
318639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return weightIntegral(endDelta) - weightIntegral(startDelta);
319639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
320639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
321639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // Evaluates the integral of y = x + WEIGHTING_INTERCEPT. This is always positive for the
322639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    // horizon we're looking at and provides a non-linear weighting for light samples.
323639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static float weightIntegral(long x) {
324639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return x * (x * 0.5f + WEIGHTING_INTERCEPT);
325639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
326639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
327639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private long nextAmbientLightBrighteningTransition(long time) {
328639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        final int N = mAmbientLightRingBuffer.size();
329639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        long earliestValidTime = time;
330639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        for (int i = N - 1; i >= 0; i--) {
331639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
332639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                break;
333639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
334639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            earliestValidTime = mAmbientLightRingBuffer.getTime(i);
335639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
336639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return earliestValidTime + BRIGHTENING_LIGHT_DEBOUNCE;
337639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
338639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
339639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private long nextAmbientLightDarkeningTransition(long time) {
340639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        final int N = mAmbientLightRingBuffer.size();
341639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        long earliestValidTime = time;
342639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        for (int i = N - 1; i >= 0; i--) {
343639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
344639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                break;
345639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
346639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            earliestValidTime = mAmbientLightRingBuffer.getTime(i);
347639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
348639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return earliestValidTime + DARKENING_LIGHT_DEBOUNCE;
349639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
350639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
351639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private void updateAmbientLux() {
352639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        long time = SystemClock.uptimeMillis();
353639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
354639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        updateAmbientLux(time);
355639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
356639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
357639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private void updateAmbientLux(long time) {
358639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // If the light sensor was just turned on then immediately update our initial
359639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // estimate of the current ambient light level.
360639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (!mAmbientLuxValid) {
361639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            final long timeWhenSensorWarmedUp =
362639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
363639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (time < timeWhenSensorWarmedUp) {
364639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                if (DEBUG) {
365639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    Slog.d(TAG, "updateAmbientLux: Sensor not  ready yet: "
366639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                            + "time=" + time
367639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                            + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
368639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                }
369639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
370639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        timeWhenSensorWarmedUp);
371639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                return;
372639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
373639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            setAmbientLux(calculateAmbientLux(time));
374639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mAmbientLuxValid = true;
375639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (DEBUG) {
376639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                Slog.d(TAG, "updateAmbientLux: Initializing: "
377639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
378639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + ", mAmbientLux=" + mAmbientLux);
379639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
380639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            updateAutoBrightness(true);
381639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
382639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
383639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
384639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
385639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        float ambientLux = calculateAmbientLux(time);
386639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
387639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
388639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
389639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            setAmbientLux(ambientLux);
390639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (DEBUG) {
391639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                Slog.d(TAG, "updateAmbientLux: "
392639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
393639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
394639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
395639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + ", mAmbientLux=" + mAmbientLux);
396639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
397639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            updateAutoBrightness(true);
398639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
399639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
400639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
401639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
402639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
403639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // exceed the necessary threshold, then it's possible we'll get a transition time prior to
404639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // now. Rather than continually checking to see whether the weighted lux exceeds the
405639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // threshold, schedule an update for when we'd normally expect another light sample, which
406639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // should be enough time to decide whether we should actually transition to the new
407639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // weighted ambient lux or not.
408639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        nextTransitionTime =
409639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                nextTransitionTime > time ? nextTransitionTime : time + LIGHT_SENSOR_RATE_MILLIS;
410639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (DEBUG) {
411639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
412639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
413639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
414639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
415639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
416639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
417639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private void updateAutoBrightness(boolean sendUpdate) {
418639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (!mAmbientLuxValid) {
419639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return;
420639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
421639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
422639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
423639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        float gamma = 1.0f;
424639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
425639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
426639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                && mScreenAutoBrightnessAdjustment != 0.0f) {
427639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
428639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
429639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            gamma *= adjGamma;
430639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (DEBUG) {
431639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
432639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
433639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
434639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
435639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (USE_TWILIGHT_ADJUSTMENT) {
436639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            TwilightState state = mTwilight.getCurrentState();
437639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (state != null && state.isNight()) {
438639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                final long now = System.currentTimeMillis();
439639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                final float earlyGamma =
440639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
441639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                final float lateGamma =
442639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
443639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                gamma *= earlyGamma * lateGamma;
444639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                if (DEBUG) {
445639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
446639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                            + ", lateGamma=" + lateGamma);
447639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                }
448639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
449639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
450639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
451639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (gamma != 1.0f) {
452639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            final float in = value;
453639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            value = MathUtils.pow(value, gamma);
454639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (DEBUG) {
455639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
456639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + ", in=" + in + ", out=" + value);
457639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
458639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
459639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
460639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        int newScreenAutoBrightness =
461639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
462639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (mScreenAutoBrightness != newScreenAutoBrightness) {
463639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (DEBUG) {
464639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
465639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + mScreenAutoBrightness + ", newScreenAutoBrightness="
466639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                        + newScreenAutoBrightness);
467639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
468639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
469639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mScreenAutoBrightness = newScreenAutoBrightness;
470639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mLastScreenAutoBrightnessGamma = gamma;
471639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (sendUpdate) {
472639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mCallbacks.updateBrightness();
473639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
474639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
475639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
476639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
477639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private int clampScreenBrightness(int value) {
478639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return MathUtils.constrain(value,
479639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
480639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
481639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
482639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) {
483639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (lastSunset < 0 || nextSunrise < 0
484639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                || now < lastSunset || now > nextSunrise) {
485639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return 1.0f;
486639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
487639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
488639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
489639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
490639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
491639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
492639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
493639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
494639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
495639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
496639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
497639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
498639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
499639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
500639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
501639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final class AutomaticBrightnessHandler extends Handler {
502639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public AutomaticBrightnessHandler(Looper looper) {
503639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            super(looper, null, true /*async*/);
504639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
505639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
506639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        @Override
507639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public void handleMessage(Message msg) {
508639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            switch (msg.what) {
509639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                case MSG_UPDATE_AMBIENT_LUX:
510639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    updateAmbientLux();
511639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    break;
512639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
513639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
514639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
515639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
516639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final SensorEventListener mLightSensorListener = new SensorEventListener() {
517639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        @Override
518639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public void onSensorChanged(SensorEvent event) {
519639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mLightSensorEnabled) {
520639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                final long time = SystemClock.uptimeMillis();
521639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                final float lux = event.values[0];
522639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                handleLightSensorEvent(time, lux);
523639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
524639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
525639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
526639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        @Override
527639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public void onAccuracyChanged(Sensor sensor, int accuracy) {
528639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            // Not used.
529639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
530639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    };
531639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
532639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private final TwilightListener mTwilightListener = new TwilightListener() {
533639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        @Override
534639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public void onTwilightStateChanged() {
535639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            updateAutoBrightness(true /*sendUpdate*/);
536639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
537639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    };
538639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
539639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    /** Callbacks to request updates to the display's power state. */
540639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    interface Callbacks {
541639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        void updateBrightness();
542639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
543639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
544639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    private static final class AmbientLightRingBuffer{
545639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // Proportional extra capacity of the buffer beyond the expected number of light samples
546639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // in the horizon
547639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private static final float BUFFER_SLACK = 1.5f;
548639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private static final int DEFAULT_CAPACITY =
549639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            (int) Math.ceil(AMBIENT_LIGHT_HORIZON * BUFFER_SLACK / LIGHT_SENSOR_RATE_MILLIS);
550639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private float[] mRingLux;
551639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private long[] mRingTime;
552639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private int mCapacity;
553639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
554639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // The first valid element and the next open slot.
555639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        // Note that if mCount is zero then there are no valid elements.
556639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private int mStart;
557639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private int mEnd;
558639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private int mCount;
559639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
560639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public AmbientLightRingBuffer() {
561639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            this(DEFAULT_CAPACITY);
562639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
563639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
564639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public AmbientLightRingBuffer(int initialCapacity) {
565639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mCapacity = initialCapacity;
566639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mRingLux = new float[mCapacity];
567639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mRingTime = new long[mCapacity];
568639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
569639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
570639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public float getLux(int index) {
571639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return mRingLux[offsetOf(index)];
572639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
573639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
574639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public long getTime(int index) {
575639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return mRingTime[offsetOf(index)];
576639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
577639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
578639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public void push(long time, float lux) {
579639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            int next = mEnd;
580639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mCount == mCapacity) {
581639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                int newSize = mCapacity * 2;
582639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
583639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                float[] newRingLux = new float[newSize];
584639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                long[] newRingTime = new long[newSize];
585639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                int length = mCapacity - mStart;
586639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
587639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
588639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                if (mStart != 0) {
589639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
590639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
591639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                }
592639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mRingLux = newRingLux;
593639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mRingTime = newRingTime;
594639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
595639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                next = mCapacity;
596639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mCapacity = newSize;
597639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mStart = 0;
598639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
599639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mRingTime[next] = time;
600639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mRingLux[next] = lux;
601639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mEnd = next + 1;
602639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mEnd == mCapacity) {
603639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mEnd = 0;
604639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
605639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mCount++;
606639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
607639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
608639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public void prune(long horizon) {
609639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mCount == 0) {
610639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                return;
611639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
612639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
613639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            while (mCount > 1) {
614639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                int next = mStart + 1;
615639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                if (next >= mCapacity) {
616639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    next -= mCapacity;
617639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                }
618639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                if (mRingTime[next] > horizon) {
619639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    // Some light sensors only produce data upon a change in the ambient light
620639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    // levels, so we need to consider the previous measurement as the ambient light
621639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    // level for all points in time up until we receive a new measurement. Thus, we
622639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    // always want to keep the youngest element that would be removed from the
623639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    // buffer and just set its measurement time to the horizon time since at that
624639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    // point it is the ambient light level, and to remove it would be to drop a
625639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    // valid data point within our horizon.
626639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                    break;
627639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                }
628639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mStart = next;
629639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mCount -= 1;
630639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
631639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
632639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mRingTime[mStart] < horizon) {
633639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                mRingTime[mStart] = horizon;
634639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
635639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
636639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
637639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public int size() {
638639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return mCount;
639639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
640639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
641639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public boolean isEmpty() {
642639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return mCount == 0;
643639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
644639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
645639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public void clear() {
646639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mStart = 0;
647639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mEnd = 0;
648639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            mCount = 0;
649639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
650639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
651639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        @Override
652639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        public String toString() {
653639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            final int length = mCapacity - mStart;
654639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            float[] lux = new float[mCount];
655639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            long[] time = new long[mCount];
656639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
657639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (mCount <= length) {
658639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingLux, mStart, lux, 0, mCount);
659639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingTime, mStart, time, 0, mCount);
660639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            } else {
661639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingLux, mStart, lux, 0, length);
662639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingLux, 0, lux, length, mCount - length);
663639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
664639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingTime, mStart, time, 0, length);
665639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                System.arraycopy(mRingTime, 0, time, length, mCount - length);
666639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
667639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return "AmbientLightRingBuffer{mCapacity=" + mCapacity
668639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                + ", mStart=" + mStart
669639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                + ", mEnd=" + mEnd
670639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                + ", mCount=" + mCount
671639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                + ", mRingLux=" + Arrays.toString(lux)
672639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                + ", mRingTime=" + Arrays.toString(time)
673639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                + "}";
674639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
675639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright
676639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        private int offsetOf(int index) {
677639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (index >= mCount || index < 0) {
678639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                throw new ArrayIndexOutOfBoundsException(index);
679639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
680639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            index += mStart;
681639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            if (index >= mCapacity) {
682639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright                index -= mCapacity;
683639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            }
684639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright            return index;
685639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright        }
686639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright    }
687639c8becc6d0efe9c205f3abf1e9347464a95020Michael Wright}
688