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