1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display;
18 
19 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
20 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
21 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
22 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
23 
24 import static org.junit.Assert.assertArrayEquals;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.Mockito.any;
29 import static org.mockito.Mockito.anyFloat;
30 import static org.mockito.Mockito.anyInt;
31 import static org.mockito.Mockito.clearInvocations;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.times;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.verifyNoMoreInteractions;
36 import static org.mockito.Mockito.when;
37 
38 import android.content.Context;
39 import android.content.pm.ApplicationInfo;
40 import android.hardware.Sensor;
41 import android.hardware.SensorEventListener;
42 import android.hardware.SensorManager;
43 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
44 import android.os.Handler;
45 import android.os.PowerManager;
46 import android.os.SystemClock;
47 import android.os.test.TestLooper;
48 import android.util.SparseArray;
49 import android.view.Display;
50 
51 import androidx.test.InstrumentationRegistry;
52 import androidx.test.filters.SmallTest;
53 import androidx.test.runner.AndroidJUnit4;
54 
55 import com.android.server.display.config.HysteresisLevels;
56 import com.android.server.display.feature.DisplayManagerFlags;
57 import com.android.server.testutils.OffsettableClock;
58 
59 import org.junit.After;
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.ArgumentCaptor;
64 import org.mockito.Mock;
65 import org.mockito.Mockito;
66 import org.mockito.MockitoAnnotations;
67 
68 @SmallTest
69 @RunWith(AndroidJUnit4.class)
70 public class AutomaticBrightnessControllerTest {
71     private static final int ANDROID_SLEEP_TIME = 1000;
72     private static final int NANO_SECONDS_MULTIPLIER = 1000000;
73     private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
74     private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
75     private static final int LIGHT_SENSOR_RATE = 20;
76     private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
77     private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 2000;
78     private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 4000;
79     private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 1000;
80     private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 2000;
81     private static final float DOZE_SCALE_FACTOR = 0.54f;
82     private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
83     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
84     private static final int AMBIENT_LIGHT_HORIZON_SHORT = 1000;
85     private static final int AMBIENT_LIGHT_HORIZON_LONG = 2000;
86     private static final float EPSILON = 0.001f;
87     private OffsettableClock mClock = new OffsettableClock();
88     private TestLooper mTestLooper;
89     private Context mContext;
90     private AutomaticBrightnessController mController;
91     private Sensor mLightSensor;
92 
93     @Mock SensorManager mSensorManager;
94     @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
95     @Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
96     @Mock BrightnessMappingStrategy mDozeBrightnessMappingStrategy;
97     @Mock HysteresisLevels mAmbientBrightnessThresholds;
98     @Mock HysteresisLevels mScreenBrightnessThresholds;
99     @Mock HysteresisLevels mAmbientBrightnessThresholdsIdle;
100     @Mock HysteresisLevels mScreenBrightnessThresholdsIdle;
101     @Mock Handler mNoOpHandler;
102     @Mock BrightnessRangeController mBrightnessRangeController;
103     @Mock
104     DisplayManagerFlags mDisplayManagerFlags;
105     @Mock BrightnessThrottler mBrightnessThrottler;
106 
107     @Before
setUp()108     public void setUp() throws Exception {
109         // Share classloader to allow package private access.
110         System.setProperty("dexmaker.share_classloader", "true");
111         MockitoAnnotations.initMocks(this);
112 
113         mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
114         mContext = InstrumentationRegistry.getContext();
115         setupController(BrightnessMappingStrategy.INVALID_LUX,
116                 BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ false,
117                 /* useHorizon= */ true);
118     }
119 
120     @After
tearDown()121     public void tearDown() {
122         if (mController != null) {
123             // Stop the update Brightness loop.
124             mController.stop();
125             mController = null;
126         }
127     }
128 
setupController(float userLux, float userNits, boolean applyDebounce, boolean useHorizon)129     private void setupController(float userLux, float userNits, boolean applyDebounce,
130             boolean useHorizon) {
131         mClock = new OffsettableClock.Stopped();
132         mTestLooper = new TestLooper(mClock::now);
133 
134         when(mBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_DEFAULT);
135         when(mIdleBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_IDLE);
136         when(mDozeBrightnessMappingStrategy.getMode()).thenReturn(AUTO_BRIGHTNESS_MODE_DOZE);
137 
138         SparseArray<BrightnessMappingStrategy> brightnessMappingStrategyMap = new SparseArray<>();
139         brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_DEFAULT,
140                 mBrightnessMappingStrategy);
141         brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_IDLE,
142                 mIdleBrightnessMappingStrategy);
143         brightnessMappingStrategyMap.append(AUTO_BRIGHTNESS_MODE_DOZE,
144                 mDozeBrightnessMappingStrategy);
145         mController = new AutomaticBrightnessController(
146                 new AutomaticBrightnessController.Injector() {
147                     @Override
148                     public Handler getBackgroundThreadHandler() {
149                         return mNoOpHandler;
150                     }
151 
152                     @Override
153                     AutomaticBrightnessController.Clock createClock(boolean isEnabled) {
154                         return new AutomaticBrightnessController.Clock() {
155                             @Override
156                             public long uptimeMillis() {
157                                 return mClock.now();
158                             }
159 
160                             @Override
161                             public long getSensorEventScaleTime() {
162                                 return mClock.now() + ANDROID_SLEEP_TIME;
163                             }
164                         };
165                     }
166 
167                 }, // pass in test looper instead, pass in offsettable clock
168                 () -> { }, mTestLooper.getLooper(), mSensorManager, mLightSensor,
169                 brightnessMappingStrategyMap, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
170                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
171                 INITIAL_LIGHT_SENSOR_RATE, applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG : 0,
172                 applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG : 0,
173                 applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
174                 applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
175                 RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
176                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
177                 mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
178                 mContext, mBrightnessRangeController, mBrightnessThrottler,
179                 useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
180                 useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
181                 mDisplayManagerFlags
182         );
183 
184         when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
185                 BRIGHTNESS_MAX_FLOAT);
186         when(mBrightnessRangeController.getCurrentBrightnessMin()).thenReturn(
187                 BRIGHTNESS_MIN_FLOAT);
188         // Disable brightness throttling by default. Individual tests can enable it as needed.
189         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
190         when(mBrightnessThrottler.isThrottled()).thenReturn(false);
191 
192         // Configure the brightness controller and grab an instance of the sensor listener,
193         // through which we can deliver fake (for test) sensor values.
194         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
195                 0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */,
196                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
197                 /* shouldResetShortTermModel= */ true);
198     }
199 
200     @Test
testNoHysteresisAtMinBrightness()201     public void testNoHysteresisAtMinBrightness() throws Exception {
202         ArgumentCaptor<SensorEventListener> listenerCaptor =
203                 ArgumentCaptor.forClass(SensorEventListener.class);
204         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
205                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
206         SensorEventListener listener = listenerCaptor.getValue();
207 
208         // Set up system to return 0.02f as a brightness value
209         float lux1 = 100.0f;
210         // Brightness as float (from 0.0f to 1.0f)
211         float normalizedBrightness1 = 0.02f;
212         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
213                 .thenReturn(lux1);
214         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
215                 .thenReturn(lux1);
216         when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
217                 .thenReturn(normalizedBrightness1);
218 
219         // This is the important bit: When the new brightness is set, make sure the new
220         // brightening threshold is beyond the maximum brightness value...so that we can test that
221         // our threshold clamping works.
222         when(mScreenBrightnessThresholds.getBrighteningThreshold(normalizedBrightness1))
223                 .thenReturn(1.0f);
224 
225         // Send new sensor value and verify
226         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1));
227         assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON);
228 
229         // Set up system to return 0.0f (minimum possible brightness) as a brightness value
230         float lux2 = 10.0f;
231         float normalizedBrightness2 = 0.0f;
232         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
233                 .thenReturn(lux2);
234         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
235                 .thenReturn(lux2);
236         when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
237                 .thenReturn(normalizedBrightness2);
238 
239         // Send new sensor value and verify
240         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2));
241         assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON);
242     }
243 
244     @Test
testNoHysteresisAtMaxBrightness()245     public void testNoHysteresisAtMaxBrightness() throws Exception {
246         ArgumentCaptor<SensorEventListener> listenerCaptor =
247                 ArgumentCaptor.forClass(SensorEventListener.class);
248         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
249                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
250         SensorEventListener listener = listenerCaptor.getValue();
251 
252         // Set up system to return 0.98f as a brightness value
253         float lux1 = 100.0f;
254         float normalizedBrightness1 = 0.98f;
255         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
256                 .thenReturn(lux1);
257         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
258                 .thenReturn(lux1);
259         when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
260                 .thenReturn(normalizedBrightness1);
261 
262         // This is the important bit: When the new brightness is set, make sure the new
263         // brightening threshold is beyond the maximum brightness value...so that we can test that
264         // our threshold clamping works.
265         when(mScreenBrightnessThresholds.getBrighteningThreshold(normalizedBrightness1))
266                 .thenReturn(1.1f);
267 
268         // Send new sensor value and verify
269         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1));
270         assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON);
271 
272 
273         // Set up system to return 1.0f as a brightness value (brightness_max)
274         float lux2 = 110.0f;
275         float normalizedBrightness2 = 1.0f;
276         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
277                 .thenReturn(lux2);
278         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
279                 .thenReturn(lux2);
280         when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
281                 .thenReturn(normalizedBrightness2);
282 
283         // Send new sensor value and verify
284         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2));
285         assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON);
286     }
287 
288     @Test
testUserAddUserDataPoint()289     public void testUserAddUserDataPoint() throws Exception {
290         ArgumentCaptor<SensorEventListener> listenerCaptor =
291                 ArgumentCaptor.forClass(SensorEventListener.class);
292         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
293                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
294         SensorEventListener listener = listenerCaptor.getValue();
295 
296         // Sensor reads 1000 lux,
297         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
298 
299         // User sets brightness to 100
300         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
301                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
302                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
303                 /* shouldResetShortTermModel= */ true);
304 
305         // There should be a user data point added to the mapper.
306         verify(mBrightnessMappingStrategy).addUserDataPoint(/* lux= */ 1000f,
307                 /* brightness= */ 0.5f);
308     }
309 
310     @Test
testRecalculateSplines()311     public void testRecalculateSplines() throws Exception {
312         // Enabling the light sensor, and setting the ambient lux to 1000
313         int currentLux = 1000;
314         ArgumentCaptor<SensorEventListener> listenerCaptor =
315                 ArgumentCaptor.forClass(SensorEventListener.class);
316         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
317                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
318         SensorEventListener listener = listenerCaptor.getValue();
319         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, currentLux));
320 
321         // User sets brightness to 0.5f
322         when(mBrightnessMappingStrategy.getBrightness(currentLux,
323                 null, ApplicationInfo.CATEGORY_UNDEFINED)).thenReturn(0.5f);
324         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
325                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
326                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
327                 /* shouldResetShortTermModel= */ true);
328 
329         //Recalculating the spline with RBC enabled, verifying that the short term model is reset,
330         //and the interaction is learnt in short term model
331         float[] adjustments = new float[]{0.2f, 0.6f};
332         mController.recalculateSplines(true, adjustments);
333         verify(mBrightnessMappingStrategy).clearUserDataPoints();
334         verify(mBrightnessMappingStrategy).recalculateSplines(true, adjustments);
335         verify(mBrightnessMappingStrategy, times(2)).addUserDataPoint(currentLux,
336                 /* brightness= */ 0.5f);
337 
338         clearInvocations(mBrightnessMappingStrategy);
339 
340         // Verify short term model is not learnt when RBC is disabled
341         mController.recalculateSplines(false, adjustments);
342         verify(mBrightnessMappingStrategy).clearUserDataPoints();
343         verify(mBrightnessMappingStrategy).recalculateSplines(false, adjustments);
344         verifyNoMoreInteractions(mBrightnessMappingStrategy);
345     }
346 
347     @Test
testShortTermModelTimesOut()348     public void testShortTermModelTimesOut() throws Exception {
349         ArgumentCaptor<SensorEventListener> listenerCaptor =
350                 ArgumentCaptor.forClass(SensorEventListener.class);
351         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
352                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
353         SensorEventListener listener = listenerCaptor.getValue();
354 
355         // Sensor reads 123 lux,
356         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
357         // User sets brightness to 100
358         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
359                 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
360                 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
361                 /* shouldResetShortTermModel= */ true);
362 
363         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
364 
365         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
366         when(mBrightnessMappingStrategy.shouldResetShortTermModel(
367                 123f, 0.5f)).thenReturn(true);
368 
369         // Sensor reads 1000 lux,
370         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
371         mTestLooper.moveTimeForward(
372                 mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
373         mTestLooper.dispatchAll();
374 
375         mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
376         mTestLooper.moveTimeForward(4000);
377         mTestLooper.dispatchAll();
378 
379         // Verify only happens on the first configure. (i.e. not again when switching back)
380         // Intentionally using any() to ensure it's not called whatsoever.
381         verify(mBrightnessMappingStrategy, times(1))
382                 .addUserDataPoint(/* lux= */ 123.0f, /* brightness= */ 0.5f);
383         verify(mBrightnessMappingStrategy, times(1))
384                 .addUserDataPoint(anyFloat(), anyFloat());
385     }
386 
387     @Test
testShortTermModelDoesntTimeOut()388     public void testShortTermModelDoesntTimeOut() throws Exception {
389         ArgumentCaptor<SensorEventListener> listenerCaptor =
390                 ArgumentCaptor.forClass(SensorEventListener.class);
391         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
392                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
393         SensorEventListener listener = listenerCaptor.getValue();
394 
395         // Sensor reads 123 lux,
396         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
397         // User sets brightness to 100
398         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
399                 0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
400                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
401                 /* shouldResetShortTermModel= */ true);
402 
403         when(mBrightnessMappingStrategy.shouldResetShortTermModel(
404                 anyFloat(), anyFloat())).thenReturn(true);
405         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
406         when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.51f);
407         when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123.0f);
408 
409         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
410 
411         // Time does not move forward, since clock is doesn't increment naturally.
412         mTestLooper.dispatchAll();
413 
414         // Sensor reads 100000 lux,
415         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 678910));
416         mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
417 
418         // Verify short term model is not reset.
419         verify(mBrightnessMappingStrategy, never()).clearUserDataPoints();
420 
421         // Verify that we add the data point once when the user sets it, and again when we return
422         // interactive mode.
423         verify(mBrightnessMappingStrategy, times(2))
424                 .addUserDataPoint(/* lux= */ 123.0f, /* brightness= */ 0.51f);
425     }
426 
427     @Test
testShortTermModelIsRestoredWhenSwitchingWithinTimeout()428     public void testShortTermModelIsRestoredWhenSwitchingWithinTimeout() throws Exception {
429         ArgumentCaptor<SensorEventListener> listenerCaptor =
430                 ArgumentCaptor.forClass(SensorEventListener.class);
431         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
432                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
433         SensorEventListener listener = listenerCaptor.getValue();
434 
435         // Sensor reads 123 lux,
436         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
437         // User sets brightness to 100
438         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
439                 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
440                 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
441                 /* shouldResetShortTermModel= */ true);
442 
443         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
444         when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
445         when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f);
446 
447         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
448         when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(
449                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
450         when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(
451                 BrightnessMappingStrategy.INVALID_LUX);
452         when(mBrightnessMappingStrategy.shouldResetShortTermModel(
453                 123f, 0.5f)).thenReturn(true);
454 
455         // Sensor reads 1000 lux,
456         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
457         mTestLooper.moveTimeForward(
458                 mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000);
459         mTestLooper.dispatchAll();
460 
461         mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
462         mTestLooper.moveTimeForward(4000);
463         mTestLooper.dispatchAll();
464 
465         // Verify only happens on the first configure. (i.e. not again when switching back)
466         // Intentionally using any() to ensure it's not called whatsoever.
467         verify(mBrightnessMappingStrategy, times(1))
468                 .addUserDataPoint(/* lux= */ 123.0f, /* brightness= */ 0.5f);
469         verify(mBrightnessMappingStrategy, times(1))
470                 .addUserDataPoint(anyFloat(), anyFloat());
471     }
472 
473     @Test
testShortTermModelNotRestoredAfterTimeout()474     public void testShortTermModelNotRestoredAfterTimeout() throws Exception {
475         ArgumentCaptor<SensorEventListener> listenerCaptor =
476                 ArgumentCaptor.forClass(SensorEventListener.class);
477         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
478                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
479         SensorEventListener listener = listenerCaptor.getValue();
480 
481         // Sensor reads 123 lux,
482         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
483         // User sets brightness to 100
484         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
485                 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
486                 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
487                 /* shouldResetShortTermModel= */ true);
488 
489         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
490 
491         when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
492         when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f);
493 
494         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
495         when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(
496                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
497         when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(
498                 BrightnessMappingStrategy.INVALID_LUX);
499 
500         when(mBrightnessMappingStrategy.shouldResetShortTermModel(
501                 123f, 0.5f)).thenReturn(true);
502 
503         // Sensor reads 1000 lux,
504         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
505         // Do not fast-forward time.
506         mTestLooper.dispatchAll();
507 
508         mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
509         // Do not fast-forward time
510         mTestLooper.dispatchAll();
511 
512         // Verify this happens on the first configure and again when switching back
513         // Intentionally using any() to ensure it's not called any other times whatsoever.
514         verify(mBrightnessMappingStrategy, times(2))
515                 .addUserDataPoint(/* lux= */ 123.0f, /* brightness= */ 0.5f);
516         verify(mBrightnessMappingStrategy, times(2))
517                 .addUserDataPoint(anyFloat(), anyFloat());
518     }
519 
520     @Test
testSwitchBetweenModesNoUserInteractions()521     public void testSwitchBetweenModesNoUserInteractions() throws Exception {
522         ArgumentCaptor<SensorEventListener> listenerCaptor =
523                 ArgumentCaptor.forClass(SensorEventListener.class);
524         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
525                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
526         SensorEventListener listener = listenerCaptor.getValue();
527 
528         // Sensor reads 123 lux,
529         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123));
530         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
531         when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(
532                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
533         when(mBrightnessMappingStrategy.getUserLux()).thenReturn(
534                 BrightnessMappingStrategy.INVALID_LUX);
535 
536         // No user brightness interaction.
537 
538         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
539         when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(
540                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
541         when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(
542                 BrightnessMappingStrategy.INVALID_LUX);
543 
544         // Sensor reads 1000 lux,
545         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
546         // Do not fast-forward time.
547         mTestLooper.dispatchAll();
548 
549         mController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ true);
550         // Do not fast-forward time
551         mTestLooper.dispatchAll();
552 
553         // Ensure that there are no data points added, since the user has never adjusted the
554         // brightness
555         verify(mBrightnessMappingStrategy, times(0))
556                 .addUserDataPoint(anyFloat(), anyFloat());
557     }
558 
559     @Test
testSwitchToIdleMappingStrategy()560     public void testSwitchToIdleMappingStrategy() throws Exception {
561         ArgumentCaptor<SensorEventListener> listenerCaptor =
562                 ArgumentCaptor.forClass(SensorEventListener.class);
563         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
564                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
565         SensorEventListener listener = listenerCaptor.getValue();
566 
567         // Sensor reads 1000 lux,
568         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000));
569 
570         // User sets brightness to 100
571         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
572                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
573                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
574                 /* shouldResetShortTermModel= */ true);
575 
576         // There should be a user data point added to the mapper.
577         verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
578                 /* brightness= */ 0.5f);
579         verify(mBrightnessMappingStrategy, times(2)).setBrightnessConfiguration(any());
580         verify(mBrightnessMappingStrategy, times(3)).getBrightness(anyFloat(), any(), anyInt());
581 
582         // Now let's do the same for idle mode
583         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
584         // Called once when switching,
585         // setAmbientLux() is called twice and once in updateAutoBrightness(),
586         // nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are
587         // called twice each.
588         verify(mBrightnessMappingStrategy, times(8)).getMode();
589         // Called when switching.
590         verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout();
591         verify(mBrightnessMappingStrategy, times(1)).getUserBrightness();
592         verify(mBrightnessMappingStrategy, times(1)).getUserLux();
593 
594         // Ensure, after switching, original BMS is not used anymore
595         verifyNoMoreInteractions(mBrightnessMappingStrategy);
596 
597         // User sets idle brightness to 0.5
598         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
599                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
600                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
601                 /* shouldResetShortTermModel= */ true);
602 
603         // Ensure we use the correct mapping strategy
604         verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
605                 /* brightness= */ 0.5f);
606     }
607 
608     @Test
testAmbientLightHorizon()609     public void testAmbientLightHorizon() throws Exception {
610         ArgumentCaptor<SensorEventListener> listenerCaptor =
611                 ArgumentCaptor.forClass(SensorEventListener.class);
612         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
613                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
614         SensorEventListener listener = listenerCaptor.getValue();
615 
616         long increment = 500;
617         // set autobrightness to low
618         // t = 0
619         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
620 
621         // t = 500
622         mClock.fastForward(increment);
623         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
624 
625         // t = 1000
626         mClock.fastForward(increment);
627         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
628         assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
629 
630         // t = 1500
631         mClock.fastForward(increment);
632         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
633         assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
634 
635         // t = 2000
636         // ensure that our reading is at 0.
637         mClock.fastForward(increment);
638         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
639         assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
640 
641         // t = 2500
642         // first 10000 lux sensor event reading
643         mClock.fastForward(increment);
644         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
645         assertTrue(mController.getAmbientLux() > 0.0f);
646         assertTrue(mController.getAmbientLux() < 10000.0f);
647 
648         // t = 3000
649         // lux reading should still not yet be 10000.
650         mClock.fastForward(increment);
651         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
652         assertTrue(mController.getAmbientLux() > 0.0f);
653         assertTrue(mController.getAmbientLux() < 10000.0f);
654 
655         // t = 3500
656         mClock.fastForward(increment);
657         // lux has been high (10000) for 1000ms.
658         // lux reading should be 10000
659         // short horizon (ambient lux) is high, long horizon is still not high
660         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
661         assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
662 
663         // t = 4000
664         // stay high
665         mClock.fastForward(increment);
666         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
667         assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
668 
669         // t = 4500
670         Mockito.clearInvocations(mBrightnessMappingStrategy);
671         mClock.fastForward(increment);
672         // short horizon is high, long horizon is high too
673         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000));
674         verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1);
675         assertEquals(10000.0f, mController.getAmbientLux(), EPSILON);
676 
677         // t = 5000
678         mClock.fastForward(increment);
679         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
680         assertTrue(mController.getAmbientLux() > 0.0f);
681         assertTrue(mController.getAmbientLux() < 10000.0f);
682 
683         // t = 5500
684         mClock.fastForward(increment);
685         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
686         assertTrue(mController.getAmbientLux() > 0.0f);
687         assertTrue(mController.getAmbientLux() < 10000.0f);
688 
689         // t = 6000
690         mClock.fastForward(increment);
691         // ambient lux goes to 0
692         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
693         assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
694 
695         // only the values within the horizon should be kept
696         assertArrayEquals(new float[] {10000, 10000, 0, 0, 0}, mController.getLastSensorValues(),
697                 EPSILON);
698         assertArrayEquals(new long[] {4000, 4500, 5000, 5500, 6000},
699                 mController.getLastSensorTimestamps());
700     }
701 
702     @Test
703     public void testHysteresisLevels() {
704         float[] ambientBrighteningThresholds = {50, 100};
705         float[] ambientDarkeningThresholds = {10, 20};
706         float[] ambientThresholdLevels = {0, 500};
707         float ambientDarkeningMinChangeThreshold = 3.0f;
708         float ambientBrighteningMinChangeThreshold = 1.5f;
709         HysteresisLevels hysteresisLevels = new HysteresisLevels(ambientBrighteningThresholds,
710                 ambientDarkeningThresholds, ambientThresholdLevels, ambientThresholdLevels,
711                 ambientDarkeningMinChangeThreshold, ambientBrighteningMinChangeThreshold);
712 
713         // test low, activate minimum change thresholds.
714         assertEquals(1.5f, hysteresisLevels.getBrighteningThreshold(0.0f), EPSILON);
715         assertEquals(0f, hysteresisLevels.getDarkeningThreshold(0.0f), EPSILON);
716         assertEquals(1f, hysteresisLevels.getDarkeningThreshold(4.0f), EPSILON);
717 
718         // test max
719         // epsilon is x2 here, since the next floating point value about 20,000 is 0.0019531 greater
720         assertEquals(20000f, hysteresisLevels.getBrighteningThreshold(10000.0f), EPSILON * 2);
721         assertEquals(8000f, hysteresisLevels.getDarkeningThreshold(10000.0f), EPSILON);
722 
723         // test just below threshold
724         assertEquals(748.5f, hysteresisLevels.getBrighteningThreshold(499f), EPSILON);
725         assertEquals(449.1f, hysteresisLevels.getDarkeningThreshold(499f), EPSILON);
726 
727         // test at (considered above) threshold
728         assertEquals(1000f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON);
729         assertEquals(400f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON);
730     }
731 
732     @Test
733     public void testBrightnessGetsThrottled() throws Exception {
734         ArgumentCaptor<SensorEventListener> listenerCaptor =
735                 ArgumentCaptor.forClass(SensorEventListener.class);
736         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
737                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
738         SensorEventListener listener = listenerCaptor.getValue();
739 
740         // Set up system to return max brightness at 100 lux
741         final float normalizedBrightness = BRIGHTNESS_MAX_FLOAT;
742         final float lux = 100.0f;
743         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux))
744                 .thenReturn(lux);
745         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux))
746                 .thenReturn(lux);
747         when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt()))
748                 .thenReturn(normalizedBrightness);
749 
750         // Sensor reads 100 lux. We should get max brightness.
751         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
752         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
753         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
754 
755         // Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
756         final float throttledBrightness = 0.123f;
757         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(throttledBrightness);
758         when(mBrightnessThrottler.isThrottled()).thenReturn(true);
759         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
760                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
761                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
762                 Display.STATE_ON, /* shouldResetShortTermModel= */ true);
763         assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
764         // The raw brightness value should not have throttling applied
765         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
766 
767         // Remove throttling and notify ABC again
768         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
769         when(mBrightnessThrottler.isThrottled()).thenReturn(false);
770         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
771                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
772                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
773                 Display.STATE_ON, /* shouldResetShortTermModel= */ true);
774         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
775         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
776     }
777 
778     @Test
779     public void testGetSensorReadings() throws Exception {
780         ArgumentCaptor<SensorEventListener> listenerCaptor =
781                 ArgumentCaptor.forClass(SensorEventListener.class);
782         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
783                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
784         SensorEventListener listener = listenerCaptor.getValue();
785 
786         // Choose values such that the ring buffer's capacity is extended and the buffer is pruned
787         int increment = 11;
788         int lux = 5000;
789         for (int i = 0; i < 1000; i++) {
790             lux += increment;
791             mClock.fastForward(increment);
792             listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
793         }
794 
795         int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment + 1);
796         float[] sensorValues = mController.getLastSensorValues();
797         long[] sensorTimestamps = mController.getLastSensorTimestamps();
798 
799         // Only the values within the horizon should be kept
800         assertEquals(valuesCount, sensorValues.length);
801         assertEquals(valuesCount, sensorTimestamps.length);
802 
803         long sensorTimestamp = mClock.now();
804         for (int i = valuesCount - 1; i >= 1; i--) {
805             assertEquals(lux, sensorValues[i], EPSILON);
806             assertEquals(sensorTimestamp, sensorTimestamps[i]);
807             lux -= increment;
808             sensorTimestamp -= increment;
809         }
810         assertEquals(lux, sensorValues[0], EPSILON);
811         assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
812     }
813 
814     @Test
815     public void testAmbientLuxBuffers_prunedBeyondLongHorizonExceptLatestValue() throws Exception {
816         when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
817         ArgumentCaptor<SensorEventListener> listenerCaptor =
818                 ArgumentCaptor.forClass(SensorEventListener.class);
819         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
820                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
821         SensorEventListener listener = listenerCaptor.getValue();
822 
823         // Choose values such that the ring buffer's capacity is extended and the buffer is pruned
824         int increment = 11;
825         int lux = 5000;
826         for (int i = 0; i < 1000; i++) {
827             lux += increment;
828             mClock.fastForward(increment);
829             listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux,
830                     (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
831         }
832         mClock.fastForward(AMBIENT_LIGHT_HORIZON_LONG + 10);
833         int newLux = 2000;
834         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, newLux,
835                 (mClock.now() + ANDROID_SLEEP_TIME) * NANO_SECONDS_MULTIPLIER));
836 
837         float[] sensorValues = mController.getLastSensorValues();
838         long[] sensorTimestamps = mController.getLastSensorTimestamps();
839         // Only the values within the horizon should be kept
840         assertEquals(2, sensorValues.length);
841         assertEquals(2, sensorTimestamps.length);
842 
843         assertEquals(lux, sensorValues[0], EPSILON);
844         assertEquals(newLux, sensorValues[1], EPSILON);
845         assertEquals(mClock.now() + ANDROID_SLEEP_TIME - AMBIENT_LIGHT_HORIZON_LONG,
846                 sensorTimestamps[0]);
847         assertEquals(mClock.now() + ANDROID_SLEEP_TIME,
848                 sensorTimestamps[1]);
849     }
850 
851     @Test
852     public void testGetSensorReadingsFullBuffer() throws Exception {
853         ArgumentCaptor<SensorEventListener> listenerCaptor =
854                 ArgumentCaptor.forClass(SensorEventListener.class);
855         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
856                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
857         SensorEventListener listener = listenerCaptor.getValue();
858         int initialCapacity = 150;
859 
860         // Choose values such that the ring buffer is pruned
861         int increment1 = 200;
862         int lux = 5000;
863         for (int i = 0; i < 20; i++) {
864             lux += increment1;
865             mClock.fastForward(increment1);
866             listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
867         }
868 
869         int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment1 + 1);
870 
871         // Choose values such that the buffer becomes full
872         int increment2 = 1;
873         for (int i = 0; i < initialCapacity - valuesCount; i++) {
874             lux += increment2;
875             mClock.fastForward(increment2);
876             listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
877         }
878 
879         float[] sensorValues = mController.getLastSensorValues();
880         long[] sensorTimestamps = mController.getLastSensorTimestamps();
881 
882         // The buffer should be full
883         assertEquals(initialCapacity, sensorValues.length);
884         assertEquals(initialCapacity, sensorTimestamps.length);
885 
886         long sensorTimestamp = mClock.now();
887         for (int i = initialCapacity - 1; i >= 1; i--) {
888             assertEquals(lux, sensorValues[i], EPSILON);
889             assertEquals(sensorTimestamp, sensorTimestamps[i]);
890 
891             if (i >= valuesCount) {
892                 lux -= increment2;
893                 sensorTimestamp -= increment2;
894             } else {
895                 lux -= increment1;
896                 sensorTimestamp -= increment1;
897             }
898         }
899         assertEquals(lux, sensorValues[0], EPSILON);
900         assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
901     }
902 
903     @Test
904     public void testResetShortTermModelWhenConfigChanges() {
905         when(mBrightnessMappingStrategy.setBrightnessConfiguration(any())).thenReturn(true);
906 
907         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
908                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
909                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
910                 Display.STATE_ON, /* shouldResetShortTermModel= */ false);
911         verify(mBrightnessMappingStrategy, never()).clearUserDataPoints();
912 
913         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
914                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
915                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
916                 Display.STATE_ON, /* shouldResetShortTermModel= */ true);
917         verify(mBrightnessMappingStrategy).clearUserDataPoints();
918     }
919 
920     @Test
921     public void testUseProvidedShortTermModel() {
922         verify(mBrightnessMappingStrategy, never()).addUserDataPoint(anyFloat(), anyFloat());
923 
924         float userLux = 1000;
925         float userNits = 500;
926         float userBrightness = 0.3f;
927         when(mBrightnessMappingStrategy.getBrightnessFromNits(userNits)).thenReturn(userBrightness);
928         setupController(userLux, userNits, /* applyDebounce= */ true,
929                 /* useHorizon= */ false);
930         verify(mBrightnessMappingStrategy).addUserDataPoint(userLux, userBrightness);
931     }
932 
933     @Test
934     public void testBrighteningLightDebounce() throws Exception {
935         clearInvocations(mSensorManager);
936         setupController(BrightnessMappingStrategy.INVALID_LUX,
937                 BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
938                 /* useHorizon= */ false);
939 
940         ArgumentCaptor<SensorEventListener> listenerCaptor =
941                 ArgumentCaptor.forClass(SensorEventListener.class);
942         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
943                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
944         SensorEventListener listener = listenerCaptor.getValue();
945 
946         // t = 0
947         // Initial lux
948         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
949         assertEquals(500, mController.getAmbientLux(), EPSILON);
950 
951         // t = 1000
952         // Lux isn't steady yet
953         mClock.fastForward(1000);
954         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
955         assertEquals(500, mController.getAmbientLux(), EPSILON);
956 
957         // t = 1500
958         // Lux isn't steady yet
959         mClock.fastForward(500);
960         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
961         assertEquals(500, mController.getAmbientLux(), EPSILON);
962 
963         // t = 2500
964         // Lux is steady now
965         mClock.fastForward(1000);
966         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
967         assertEquals(1200, mController.getAmbientLux(), EPSILON);
968     }
969 
970     @Test
971     public void testDarkeningLightDebounce() throws Exception {
972         clearInvocations(mSensorManager);
973         when(mAmbientBrightnessThresholds.getBrighteningThreshold(anyFloat()))
974                 .thenReturn(10000f);
975         when(mAmbientBrightnessThresholds.getDarkeningThreshold(anyFloat()))
976                 .thenReturn(10000f);
977         setupController(BrightnessMappingStrategy.INVALID_LUX,
978                 BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
979                 /* useHorizon= */ false);
980 
981         ArgumentCaptor<SensorEventListener> listenerCaptor =
982                 ArgumentCaptor.forClass(SensorEventListener.class);
983         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
984                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
985         SensorEventListener listener = listenerCaptor.getValue();
986 
987         // t = 0
988         // Initial lux
989         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
990         assertEquals(1200, mController.getAmbientLux(), EPSILON);
991 
992         // t = 2000
993         // Lux isn't steady yet
994         mClock.fastForward(2000);
995         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
996         assertEquals(1200, mController.getAmbientLux(), EPSILON);
997 
998         // t = 2500
999         // Lux isn't steady yet
1000         mClock.fastForward(500);
1001         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
1002         assertEquals(1200, mController.getAmbientLux(), EPSILON);
1003 
1004         // t = 4500
1005         // Lux is steady now
1006         mClock.fastForward(2000);
1007         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
1008         assertEquals(500, mController.getAmbientLux(), EPSILON);
1009     }
1010 
1011     @Test
1012     public void testBrighteningLightDebounceIdle() throws Exception {
1013         clearInvocations(mSensorManager);
1014         setupController(BrightnessMappingStrategy.INVALID_LUX,
1015                 BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
1016                 /* useHorizon= */ false);
1017 
1018         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
1019 
1020         ArgumentCaptor<SensorEventListener> listenerCaptor =
1021                 ArgumentCaptor.forClass(SensorEventListener.class);
1022         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
1023                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
1024         SensorEventListener listener = listenerCaptor.getValue();
1025 
1026         // t = 0
1027         // Initial lux
1028         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
1029         assertEquals(500, mController.getAmbientLux(), EPSILON);
1030 
1031         // t = 500
1032         // Lux isn't steady yet
1033         mClock.fastForward(500);
1034         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
1035         assertEquals(500, mController.getAmbientLux(), EPSILON);
1036 
1037         // t = 1500
1038         // Lux is steady now
1039         mClock.fastForward(1000);
1040         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
1041         assertEquals(1200, mController.getAmbientLux(), EPSILON);
1042     }
1043 
1044     @Test
1045     public void testDarkeningLightDebounceIdle() throws Exception {
1046         clearInvocations(mSensorManager);
1047         when(mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(anyFloat()))
1048                 .thenReturn(10000f);
1049         when(mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(anyFloat()))
1050                 .thenReturn(10000f);
1051         setupController(BrightnessMappingStrategy.INVALID_LUX,
1052                 BrightnessMappingStrategy.INVALID_NITS, /* applyDebounce= */ true,
1053                 /* useHorizon= */ false);
1054 
1055         mController.switchMode(AUTO_BRIGHTNESS_MODE_IDLE, /* sendUpdate= */ true);
1056 
1057         ArgumentCaptor<SensorEventListener> listenerCaptor =
1058                 ArgumentCaptor.forClass(SensorEventListener.class);
1059         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
1060                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
1061         SensorEventListener listener = listenerCaptor.getValue();
1062 
1063         // t = 0
1064         // Initial lux
1065         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
1066         assertEquals(1200, mController.getAmbientLux(), EPSILON);
1067 
1068         // t = 1000
1069         // Lux isn't steady yet
1070         mClock.fastForward(1000);
1071         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
1072         assertEquals(1200, mController.getAmbientLux(), EPSILON);
1073 
1074         // t = 2500
1075         // Lux is steady now
1076         mClock.fastForward(1500);
1077         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
1078         assertEquals(500, mController.getAmbientLux(), EPSILON);
1079     }
1080 
1081     @Test
1082     public void testAutoBrightnessInDoze() throws Exception {
1083         ArgumentCaptor<SensorEventListener> listenerCaptor =
1084                 ArgumentCaptor.forClass(SensorEventListener.class);
1085         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
1086                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
1087         SensorEventListener listener = listenerCaptor.getValue();
1088 
1089         // Set up system to return 0.3f as a brightness value
1090         float lux = 100.0f;
1091         // Brightness as float (from 0.0f to 1.0f)
1092         float normalizedBrightness = 0.3f;
1093         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
1094         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
1095         when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
1096                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
1097         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
1098 
1099         // Set policy to DOZE
1100         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
1101                 /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
1102                 /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_DOZE,
1103                 /* shouldResetShortTermModel= */ true);
1104 
1105         // Send a new sensor value
1106         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
1107 
1108         // The brightness should be scaled by the doze factor
1109         assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
1110                 mController.getAutomaticScreenBrightness(
1111                         /* brightnessEvent= */ null), EPSILON);
1112     }
1113 
1114     @Test
1115     public void testAutoBrightnessInDoze_ShouldNotScaleIfUsingDozeCurve() throws Exception {
1116         ArgumentCaptor<SensorEventListener> listenerCaptor =
1117                 ArgumentCaptor.forClass(SensorEventListener.class);
1118         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
1119                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
1120         SensorEventListener listener = listenerCaptor.getValue();
1121 
1122         // Set up system to return 0.3f as a brightness value
1123         float lux = 100.0f;
1124         // Brightness as float (from 0.0f to 1.0f)
1125         float normalizedBrightness = 0.3f;
1126         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
1127         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
1128         when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
1129                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
1130         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
1131 
1132         // Switch mode to DOZE
1133         mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ false);
1134 
1135         // Set policy to DOZE
1136         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
1137                 /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
1138                 /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_DOZE,
1139                 /* shouldResetShortTermModel= */ true);
1140 
1141         // Send a new sensor value
1142         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
1143 
1144         // The brightness should not be scaled by the doze factor
1145         assertEquals(normalizedBrightness,
1146                 mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
1147     }
1148 
1149     @Test
1150     public void testAutoBrightnessInDoze_ShouldNotScaleIfScreenOn() throws Exception {
1151         ArgumentCaptor<SensorEventListener> listenerCaptor =
1152                 ArgumentCaptor.forClass(SensorEventListener.class);
1153         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
1154                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
1155         SensorEventListener listener = listenerCaptor.getValue();
1156 
1157         // Set up system to return 0.3f as a brightness value
1158         float lux = 100.0f;
1159         // Brightness as float (from 0.0f to 1.0f)
1160         float normalizedBrightness = 0.3f;
1161         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
1162         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
1163         when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
1164                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
1165         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
1166 
1167         // Set policy to DOZE
1168         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
1169                 /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
1170                 /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_ON,
1171                 /* shouldResetShortTermModel= */ true);
1172 
1173         // Send a new sensor value
1174         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
1175 
1176         // The brightness should not be scaled by the doze factor
1177         assertEquals(normalizedBrightness,
1178                 mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
1179     }
1180 
1181     @Test
1182     public void testSwitchMode_UpdateBrightnessImmediately() throws Exception {
1183         ArgumentCaptor<SensorEventListener> listenerCaptor =
1184                 ArgumentCaptor.forClass(SensorEventListener.class);
1185         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
1186                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
1187         SensorEventListener listener = listenerCaptor.getValue();
1188 
1189         // Set up system to return 0.3f as a brightness value
1190         float lux = 100.0f;
1191         // Brightness as float (from 0.0f to 1.0f)
1192         float normalizedBrightness = 0.3f;
1193         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
1194         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
1195         when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
1196                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
1197         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
1198 
1199         // Send a new sensor value
1200         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
1201 
1202         // Switch mode to DOZE
1203         mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ false);
1204 
1205         assertEquals(normalizedBrightness,
1206                 mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
1207     }
1208 
1209     @Test
1210     public void testSwitchMode_UpdateBrightnessInBackground() throws Exception {
1211         ArgumentCaptor<SensorEventListener> listenerCaptor =
1212                 ArgumentCaptor.forClass(SensorEventListener.class);
1213         verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
1214                 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
1215         SensorEventListener listener = listenerCaptor.getValue();
1216 
1217         // Set up system to return 0.3f as a brightness value
1218         float lux = 100.0f;
1219         // Brightness as float (from 0.0f to 1.0f)
1220         float normalizedBrightness = 0.3f;
1221         when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
1222         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
1223         when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
1224                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
1225         when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
1226 
1227         // Send a new sensor value
1228         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
1229 
1230         // Switch mode to DOZE
1231         mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ true);
1232         mClock.fastForward(SystemClock.uptimeMillis());
1233         mTestLooper.dispatchAll();
1234 
1235         assertEquals(normalizedBrightness,
1236                 mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
1237     }
1238 }
1239