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