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 21 import static org.junit.Assert.assertArrayEquals; 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.Mockito.any; 26 import static org.mockito.Mockito.anyFloat; 27 import static org.mockito.Mockito.anyInt; 28 import static org.mockito.Mockito.clearInvocations; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.verifyNoMoreInteractions; 33 import static org.mockito.Mockito.when; 34 35 import android.content.Context; 36 import android.content.pm.ApplicationInfo; 37 import android.hardware.Sensor; 38 import android.hardware.SensorEventListener; 39 import android.hardware.SensorManager; 40 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; 41 import android.os.Handler; 42 import android.os.test.TestLooper; 43 44 import androidx.test.InstrumentationRegistry; 45 import androidx.test.filters.SmallTest; 46 import androidx.test.runner.AndroidJUnit4; 47 48 import com.android.server.testutils.OffsettableClock; 49 50 import org.junit.After; 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.ArgumentCaptor; 55 import org.mockito.Mock; 56 import org.mockito.Mockito; 57 import org.mockito.MockitoAnnotations; 58 59 @SmallTest 60 @RunWith(AndroidJUnit4.class) 61 public class AutomaticBrightnessControllerTest { 62 private static final float BRIGHTNESS_MIN_FLOAT = 0.0f; 63 private static final float BRIGHTNESS_MAX_FLOAT = 1.0f; 64 private static final int LIGHT_SENSOR_RATE = 20; 65 private static final int INITIAL_LIGHT_SENSOR_RATE = 20; 66 private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0; 67 private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0; 68 private static final float DOZE_SCALE_FACTOR = 0.0f; 69 private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false; 70 private static final int LIGHT_SENSOR_WARMUP_TIME = 0; 71 private static final int AMBIENT_LIGHT_HORIZON_SHORT = 1000; 72 private static final int AMBIENT_LIGHT_HORIZON_LONG = 2000; 73 private static final float EPSILON = 0.001f; 74 private OffsettableClock mClock = new OffsettableClock(); 75 private TestLooper mTestLooper; 76 private Context mContext; 77 private AutomaticBrightnessController mController; 78 private Sensor mLightSensor; 79 80 @Mock SensorManager mSensorManager; 81 @Mock BrightnessMappingStrategy mBrightnessMappingStrategy; 82 @Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy; 83 @Mock HysteresisLevels mAmbientBrightnessThresholds; 84 @Mock HysteresisLevels mScreenBrightnessThresholds; 85 @Mock HysteresisLevels mAmbientBrightnessThresholdsIdle; 86 @Mock HysteresisLevels mScreenBrightnessThresholdsIdle; 87 @Mock Handler mNoOpHandler; 88 @Mock BrightnessRangeController mBrightnessRangeController; 89 @Mock BrightnessThrottler mBrightnessThrottler; 90 91 @Before setUp()92 public void setUp() throws Exception { 93 // Share classloader to allow package private access. 94 System.setProperty("dexmaker.share_classloader", "true"); 95 MockitoAnnotations.initMocks(this); 96 97 mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); 98 mContext = InstrumentationRegistry.getContext(); 99 mController = setupController(mLightSensor, BrightnessMappingStrategy.NO_USER_LUX, 100 BrightnessMappingStrategy.NO_USER_BRIGHTNESS); 101 } 102 103 @After tearDown()104 public void tearDown() { 105 if (mController != null) { 106 // Stop the update Brightness loop. 107 mController.stop(); 108 mController = null; 109 } 110 } 111 setupController(Sensor lightSensor, float userLux, float userBrightness)112 private AutomaticBrightnessController setupController(Sensor lightSensor, float userLux, 113 float userBrightness) { 114 mClock = new OffsettableClock.Stopped(); 115 mTestLooper = new TestLooper(mClock::now); 116 117 AutomaticBrightnessController controller = new AutomaticBrightnessController( 118 new AutomaticBrightnessController.Injector() { 119 @Override 120 public Handler getBackgroundThreadHandler() { 121 return mNoOpHandler; 122 } 123 124 @Override 125 AutomaticBrightnessController.Clock createClock() { 126 return mClock::now; 127 } 128 129 }, // pass in test looper instead, pass in offsettable clock 130 () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor, 131 mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT, 132 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, 133 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, 134 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, 135 mAmbientBrightnessThresholds, mScreenBrightnessThresholds, 136 mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle, 137 mContext, mBrightnessRangeController, mBrightnessThrottler, 138 mIdleBrightnessMappingStrategy, AMBIENT_LIGHT_HORIZON_SHORT, 139 AMBIENT_LIGHT_HORIZON_LONG, userLux, userBrightness 140 ); 141 142 when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn( 143 BRIGHTNESS_MAX_FLOAT); 144 when(mBrightnessRangeController.getCurrentBrightnessMin()).thenReturn( 145 BRIGHTNESS_MIN_FLOAT); 146 // Disable brightness throttling by default. Individual tests can enable it as needed. 147 when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT); 148 when(mBrightnessThrottler.isThrottled()).thenReturn(false); 149 150 // Configure the brightness controller and grab an instance of the sensor listener, 151 // through which we can deliver fake (for test) sensor values. 152 controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 153 0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */, 154 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 155 /* shouldResetShortTermModel= */ true); 156 157 return controller; 158 } 159 160 @Test testNoHysteresisAtMinBrightness()161 public void testNoHysteresisAtMinBrightness() throws Exception { 162 ArgumentCaptor<SensorEventListener> listenerCaptor = 163 ArgumentCaptor.forClass(SensorEventListener.class); 164 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 165 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 166 SensorEventListener listener = listenerCaptor.getValue(); 167 168 // Set up system to return 0.02f as a brightness value 169 float lux1 = 100.0f; 170 // Brightness as float (from 0.0f to 1.0f) 171 float normalizedBrightness1 = 0.02f; 172 when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) 173 .thenReturn(lux1); 174 when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) 175 .thenReturn(lux1); 176 when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt())) 177 .thenReturn(normalizedBrightness1); 178 179 // This is the important bit: When the new brightness is set, make sure the new 180 // brightening threshold is beyond the maximum brightness value...so that we can test that 181 // our threshold clamping works. 182 when(mScreenBrightnessThresholds.getBrighteningThreshold(normalizedBrightness1)) 183 .thenReturn(1.0f); 184 185 // Send new sensor value and verify 186 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1)); 187 assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON); 188 189 // Set up system to return 0.0f (minimum possible brightness) as a brightness value 190 float lux2 = 10.0f; 191 float normalizedBrightness2 = 0.0f; 192 when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2)) 193 .thenReturn(lux2); 194 when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2)) 195 .thenReturn(lux2); 196 when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt())) 197 .thenReturn(normalizedBrightness2); 198 199 // Send new sensor value and verify 200 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2)); 201 assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON); 202 } 203 204 @Test testNoHysteresisAtMaxBrightness()205 public void testNoHysteresisAtMaxBrightness() throws Exception { 206 ArgumentCaptor<SensorEventListener> listenerCaptor = 207 ArgumentCaptor.forClass(SensorEventListener.class); 208 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 209 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 210 SensorEventListener listener = listenerCaptor.getValue(); 211 212 // Set up system to return 0.98f as a brightness value 213 float lux1 = 100.0f; 214 float normalizedBrightness1 = 0.98f; 215 when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) 216 .thenReturn(lux1); 217 when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) 218 .thenReturn(lux1); 219 when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt())) 220 .thenReturn(normalizedBrightness1); 221 222 // This is the important bit: When the new brightness is set, make sure the new 223 // brightening threshold is beyond the maximum brightness value...so that we can test that 224 // our threshold clamping works. 225 when(mScreenBrightnessThresholds.getBrighteningThreshold(normalizedBrightness1)) 226 .thenReturn(1.1f); 227 228 // Send new sensor value and verify 229 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1)); 230 assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON); 231 232 233 // Set up system to return 1.0f as a brightness value (brightness_max) 234 float lux2 = 110.0f; 235 float normalizedBrightness2 = 1.0f; 236 when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2)) 237 .thenReturn(lux2); 238 when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2)) 239 .thenReturn(lux2); 240 when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt())) 241 .thenReturn(normalizedBrightness2); 242 243 // Send new sensor value and verify 244 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2)); 245 assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON); 246 } 247 248 @Test testUserAddUserDataPoint()249 public void testUserAddUserDataPoint() throws Exception { 250 ArgumentCaptor<SensorEventListener> listenerCaptor = 251 ArgumentCaptor.forClass(SensorEventListener.class); 252 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 253 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 254 SensorEventListener listener = listenerCaptor.getValue(); 255 256 // Sensor reads 1000 lux, 257 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); 258 259 // User sets brightness to 100 260 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 261 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */, 262 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 263 /* shouldResetShortTermModel= */ true); 264 265 // There should be a user data point added to the mapper. 266 verify(mBrightnessMappingStrategy).addUserDataPoint(1000f, 0.5f); 267 } 268 269 @Test testRecalculateSplines()270 public void testRecalculateSplines() throws Exception { 271 // Enabling the light sensor, and setting the ambient lux to 1000 272 int currentLux = 1000; 273 ArgumentCaptor<SensorEventListener> listenerCaptor = 274 ArgumentCaptor.forClass(SensorEventListener.class); 275 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 276 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 277 SensorEventListener listener = listenerCaptor.getValue(); 278 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, currentLux)); 279 280 // User sets brightness to 0.5f 281 when(mBrightnessMappingStrategy.getBrightness(currentLux, 282 null, ApplicationInfo.CATEGORY_UNDEFINED)).thenReturn(0.5f); 283 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 284 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */, 285 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 286 /* shouldResetShortTermModel= */ true); 287 288 //Recalculating the spline with RBC enabled, verifying that the short term model is reset, 289 //and the interaction is learnt in short term model 290 float[] adjustments = new float[]{0.2f, 0.6f}; 291 mController.recalculateSplines(true, adjustments); 292 verify(mBrightnessMappingStrategy).clearUserDataPoints(); 293 verify(mBrightnessMappingStrategy).recalculateSplines(true, adjustments); 294 verify(mBrightnessMappingStrategy, times(2)).addUserDataPoint(currentLux, 0.5f); 295 296 clearInvocations(mBrightnessMappingStrategy); 297 298 // Verify short term model is not learnt when RBC is disabled 299 mController.recalculateSplines(false, adjustments); 300 verify(mBrightnessMappingStrategy).clearUserDataPoints(); 301 verify(mBrightnessMappingStrategy).recalculateSplines(false, adjustments); 302 verifyNoMoreInteractions(mBrightnessMappingStrategy); 303 } 304 305 @Test testShortTermModelTimesOut()306 public void testShortTermModelTimesOut() throws Exception { 307 ArgumentCaptor<SensorEventListener> listenerCaptor = 308 ArgumentCaptor.forClass(SensorEventListener.class); 309 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 310 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 311 SensorEventListener listener = listenerCaptor.getValue(); 312 313 // Sensor reads 123 lux, 314 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); 315 // User sets brightness to 100 316 mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null, 317 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0, 318 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, 319 /* shouldResetShortTermModel= */ true); 320 321 when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); 322 323 mController.switchToIdleMode(); 324 when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); 325 when(mBrightnessMappingStrategy.shouldResetShortTermModel( 326 123f, 0.5f)).thenReturn(true); 327 328 // Sensor reads 1000 lux, 329 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); 330 mTestLooper.moveTimeForward( 331 mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000); 332 mTestLooper.dispatchAll(); 333 334 mController.switchToInteractiveScreenBrightnessMode(); 335 mTestLooper.moveTimeForward(4000); 336 mTestLooper.dispatchAll(); 337 338 // Verify only happens on the first configure. (i.e. not again when switching back) 339 // Intentionally using any() to ensure it's not called whatsoever. 340 verify(mBrightnessMappingStrategy, times(1)) 341 .addUserDataPoint(123.0f, 0.5f); 342 verify(mBrightnessMappingStrategy, times(1)) 343 .addUserDataPoint(anyFloat(), anyFloat()); 344 } 345 346 @Test testShortTermModelDoesntTimeOut()347 public void testShortTermModelDoesntTimeOut() throws Exception { 348 ArgumentCaptor<SensorEventListener> listenerCaptor = 349 ArgumentCaptor.forClass(SensorEventListener.class); 350 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 351 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 352 SensorEventListener listener = listenerCaptor.getValue(); 353 354 // Sensor reads 123 lux, 355 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); 356 // User sets brightness to 100 357 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 358 0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */, 359 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 360 /* shouldResetShortTermModel= */ true); 361 362 when(mBrightnessMappingStrategy.shouldResetShortTermModel( 363 anyFloat(), anyFloat())).thenReturn(true); 364 when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); 365 when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.51f); 366 when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123.0f); 367 368 mController.switchToIdleMode(); 369 when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); 370 371 // Time does not move forward, since clock is doesn't increment naturally. 372 mTestLooper.dispatchAll(); 373 374 // Sensor reads 100000 lux, 375 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 678910)); 376 mController.switchToInteractiveScreenBrightnessMode(); 377 378 // Verify short term model is not reset. 379 verify(mBrightnessMappingStrategy, never()).clearUserDataPoints(); 380 381 // Verify that we add the data point once when the user sets it, and again when we return 382 // interactive mode. 383 verify(mBrightnessMappingStrategy, times(2)) 384 .addUserDataPoint(123.0f, 0.51f); 385 } 386 387 @Test testShortTermModelIsRestoredWhenSwitchingWithinTimeout()388 public void testShortTermModelIsRestoredWhenSwitchingWithinTimeout() 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, /* configuration= */ null, 399 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0, 400 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, 401 /* shouldResetShortTermModel= */ true); 402 403 when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); 404 when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f); 405 when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f); 406 407 mController.switchToIdleMode(); 408 when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); 409 when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f); 410 when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f); 411 when(mBrightnessMappingStrategy.shouldResetShortTermModel( 412 123f, 0.5f)).thenReturn(true); 413 414 // Sensor reads 1000 lux, 415 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); 416 mTestLooper.moveTimeForward( 417 mBrightnessMappingStrategy.getShortTermModelTimeout() + 1000); 418 mTestLooper.dispatchAll(); 419 420 mController.switchToInteractiveScreenBrightnessMode(); 421 mTestLooper.moveTimeForward(4000); 422 mTestLooper.dispatchAll(); 423 424 // Verify only happens on the first configure. (i.e. not again when switching back) 425 // Intentionally using any() to ensure it's not called whatsoever. 426 verify(mBrightnessMappingStrategy, times(1)) 427 .addUserDataPoint(123.0f, 0.5f); 428 verify(mBrightnessMappingStrategy, times(1)) 429 .addUserDataPoint(anyFloat(), anyFloat()); 430 } 431 432 @Test testShortTermModelNotRestoredAfterTimeout()433 public void testShortTermModelNotRestoredAfterTimeout() throws Exception { 434 ArgumentCaptor<SensorEventListener> listenerCaptor = 435 ArgumentCaptor.forClass(SensorEventListener.class); 436 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 437 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 438 SensorEventListener listener = listenerCaptor.getValue(); 439 440 // Sensor reads 123 lux, 441 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); 442 // User sets brightness to 100 443 mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null, 444 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0, 445 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, 446 /* shouldResetShortTermModel= */ true); 447 448 when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); 449 450 when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f); 451 when(mBrightnessMappingStrategy.getUserLux()).thenReturn(123f); 452 453 mController.switchToIdleMode(); 454 when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); 455 when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1f); 456 when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1f); 457 458 when(mBrightnessMappingStrategy.shouldResetShortTermModel( 459 123f, 0.5f)).thenReturn(true); 460 461 // Sensor reads 1000 lux, 462 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); 463 // Do not fast-forward time. 464 mTestLooper.dispatchAll(); 465 466 mController.switchToInteractiveScreenBrightnessMode(); 467 // Do not fast-forward time 468 mTestLooper.dispatchAll(); 469 470 // Verify this happens on the first configure and again when switching back 471 // Intentionally using any() to ensure it's not called any other times whatsoever. 472 verify(mBrightnessMappingStrategy, times(2)) 473 .addUserDataPoint(123.0f, 0.5f); 474 verify(mBrightnessMappingStrategy, times(2)) 475 .addUserDataPoint(anyFloat(), anyFloat()); 476 } 477 478 @Test testSwitchBetweenModesNoUserInteractions()479 public void testSwitchBetweenModesNoUserInteractions() throws Exception { 480 ArgumentCaptor<SensorEventListener> listenerCaptor = 481 ArgumentCaptor.forClass(SensorEventListener.class); 482 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 483 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 484 SensorEventListener listener = listenerCaptor.getValue(); 485 486 // Sensor reads 123 lux, 487 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 123)); 488 when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L); 489 when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1.0f); 490 when(mBrightnessMappingStrategy.getUserLux()).thenReturn(-1.0f); 491 492 // No user brightness interaction. 493 494 mController.switchToIdleMode(); 495 when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true); 496 when(mIdleBrightnessMappingStrategy.getUserBrightness()).thenReturn(-1.0f); 497 when(mIdleBrightnessMappingStrategy.getUserLux()).thenReturn(-1.0f); 498 499 // Sensor reads 1000 lux, 500 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); 501 // Do not fast-forward time. 502 mTestLooper.dispatchAll(); 503 504 mController.switchToInteractiveScreenBrightnessMode(); 505 // Do not fast-forward time 506 mTestLooper.dispatchAll(); 507 508 // Ensure that there are no data points added, since the user has never adjusted the 509 // brightness 510 verify(mBrightnessMappingStrategy, times(0)) 511 .addUserDataPoint(anyFloat(), anyFloat()); 512 } 513 514 @Test testSwitchToIdleMappingStrategy()515 public void testSwitchToIdleMappingStrategy() throws Exception { 516 ArgumentCaptor<SensorEventListener> listenerCaptor = 517 ArgumentCaptor.forClass(SensorEventListener.class); 518 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 519 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 520 SensorEventListener listener = listenerCaptor.getValue(); 521 522 // Sensor reads 1000 lux, 523 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1000)); 524 525 // User sets brightness to 100 526 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 527 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */, 528 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 529 /* shouldResetShortTermModel= */ true); 530 531 // There should be a user data point added to the mapper. 532 verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f); 533 verify(mBrightnessMappingStrategy, times(2)).setBrightnessConfiguration(any()); 534 verify(mBrightnessMappingStrategy, times(3)).getBrightness(anyFloat(), any(), anyInt()); 535 536 // Now let's do the same for idle mode 537 mController.switchToIdleMode(); 538 // Called once for init, and once when switching, 539 // setAmbientLux() is called twice and once in updateAutoBrightness() 540 verify(mBrightnessMappingStrategy, times(5)).isForIdleMode(); 541 // Called when switching. 542 verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout(); 543 verify(mBrightnessMappingStrategy, times(1)).getUserBrightness(); 544 verify(mBrightnessMappingStrategy, times(1)).getUserLux(); 545 546 // Ensure, after switching, original BMS is not used anymore 547 verifyNoMoreInteractions(mBrightnessMappingStrategy); 548 549 // User sets idle brightness to 0.5 550 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 551 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */, 552 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 553 /* shouldResetShortTermModel= */ true); 554 555 // Ensure we use the correct mapping strategy 556 verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(1000f, 0.5f); 557 } 558 559 @Test testAmbientLightHorizon()560 public void testAmbientLightHorizon() 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 long increment = 500; 568 // set autobrightness to low 569 // t = 0 570 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 571 572 // t = 500 573 mClock.fastForward(increment); 574 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 575 576 // t = 1000 577 mClock.fastForward(increment); 578 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 579 assertEquals(0.0f, mController.getAmbientLux(), EPSILON); 580 581 // t = 1500 582 mClock.fastForward(increment); 583 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 584 assertEquals(0.0f, mController.getAmbientLux(), EPSILON); 585 586 // t = 2000 587 // ensure that our reading is at 0. 588 mClock.fastForward(increment); 589 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 590 assertEquals(0.0f, mController.getAmbientLux(), EPSILON); 591 592 // t = 2500 593 // first 10000 lux sensor event reading 594 mClock.fastForward(increment); 595 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); 596 assertTrue(mController.getAmbientLux() > 0.0f); 597 assertTrue(mController.getAmbientLux() < 10000.0f); 598 599 // t = 3000 600 // lux reading should still not yet be 10000. 601 mClock.fastForward(increment); 602 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); 603 assertTrue(mController.getAmbientLux() > 0.0f); 604 assertTrue(mController.getAmbientLux() < 10000.0f); 605 606 // t = 3500 607 mClock.fastForward(increment); 608 // lux has been high (10000) for 1000ms. 609 // lux reading should be 10000 610 // short horizon (ambient lux) is high, long horizon is still not high 611 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); 612 assertEquals(10000.0f, mController.getAmbientLux(), EPSILON); 613 614 // t = 4000 615 // stay high 616 mClock.fastForward(increment); 617 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); 618 assertEquals(10000.0f, mController.getAmbientLux(), EPSILON); 619 620 // t = 4500 621 Mockito.clearInvocations(mBrightnessMappingStrategy); 622 mClock.fastForward(increment); 623 // short horizon is high, long horizon is high too 624 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 10000)); 625 verify(mBrightnessMappingStrategy, times(1)).getBrightness(10000, null, -1); 626 assertEquals(10000.0f, mController.getAmbientLux(), EPSILON); 627 628 // t = 5000 629 mClock.fastForward(increment); 630 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 631 assertTrue(mController.getAmbientLux() > 0.0f); 632 assertTrue(mController.getAmbientLux() < 10000.0f); 633 634 // t = 5500 635 mClock.fastForward(increment); 636 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 637 assertTrue(mController.getAmbientLux() > 0.0f); 638 assertTrue(mController.getAmbientLux() < 10000.0f); 639 640 // t = 6000 641 mClock.fastForward(increment); 642 // ambient lux goes to 0 643 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0)); 644 assertEquals(0.0f, mController.getAmbientLux(), EPSILON); 645 646 // only the values within the horizon should be kept 647 assertArrayEquals(new float[] {10000, 10000, 0, 0, 0}, mController.getLastSensorValues(), 648 EPSILON); 649 assertArrayEquals(new long[] {4000, 4500, 5000, 5500, 6000}, 650 mController.getLastSensorTimestamps()); 651 } 652 653 @Test 654 public void testHysteresisLevels() { 655 float[] ambientBrighteningThresholds = {50, 100}; 656 float[] ambientDarkeningThresholds = {10, 20}; 657 float[] ambientThresholdLevels = {0, 500}; 658 float ambientDarkeningMinChangeThreshold = 3.0f; 659 float ambientBrighteningMinChangeThreshold = 1.5f; 660 HysteresisLevels hysteresisLevels = new HysteresisLevels(ambientBrighteningThresholds, 661 ambientDarkeningThresholds, ambientThresholdLevels, ambientThresholdLevels, 662 ambientDarkeningMinChangeThreshold, ambientBrighteningMinChangeThreshold); 663 664 // test low, activate minimum change thresholds. 665 assertEquals(1.5f, hysteresisLevels.getBrighteningThreshold(0.0f), EPSILON); 666 assertEquals(0f, hysteresisLevels.getDarkeningThreshold(0.0f), EPSILON); 667 assertEquals(1f, hysteresisLevels.getDarkeningThreshold(4.0f), EPSILON); 668 669 // test max 670 // epsilon is x2 here, since the next floating point value about 20,000 is 0.0019531 greater 671 assertEquals(20000f, hysteresisLevels.getBrighteningThreshold(10000.0f), EPSILON * 2); 672 assertEquals(8000f, hysteresisLevels.getDarkeningThreshold(10000.0f), EPSILON); 673 674 // test just below threshold 675 assertEquals(748.5f, hysteresisLevels.getBrighteningThreshold(499f), EPSILON); 676 assertEquals(449.1f, hysteresisLevels.getDarkeningThreshold(499f), EPSILON); 677 678 // test at (considered above) threshold 679 assertEquals(1000f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON); 680 assertEquals(400f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON); 681 } 682 683 @Test 684 public void testBrightnessGetsThrottled() throws Exception { 685 ArgumentCaptor<SensorEventListener> listenerCaptor = 686 ArgumentCaptor.forClass(SensorEventListener.class); 687 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 688 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 689 SensorEventListener listener = listenerCaptor.getValue(); 690 691 // Set up system to return max brightness at 100 lux 692 final float normalizedBrightness = BRIGHTNESS_MAX_FLOAT; 693 final float lux = 100.0f; 694 when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)) 695 .thenReturn(lux); 696 when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)) 697 .thenReturn(lux); 698 when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt())) 699 .thenReturn(normalizedBrightness); 700 701 // Sensor reads 100 lux. We should get max brightness. 702 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux)); 703 assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f); 704 assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f); 705 706 // Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState()) 707 final float throttledBrightness = 0.123f; 708 when(mBrightnessThrottler.getBrightnessCap()).thenReturn(throttledBrightness); 709 when(mBrightnessThrottler.isThrottled()).thenReturn(true); 710 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 711 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */, 712 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 713 /* shouldResetShortTermModel= */ true); 714 assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f); 715 // The raw brightness value should not have throttling applied 716 assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f); 717 718 // Remove throttling and notify ABC again 719 when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT); 720 when(mBrightnessThrottler.isThrottled()).thenReturn(false); 721 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 722 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */, 723 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 724 /* shouldResetShortTermModel= */ true); 725 assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f); 726 assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f); 727 } 728 729 @Test 730 public void testGetSensorReadings() throws Exception { 731 ArgumentCaptor<SensorEventListener> listenerCaptor = 732 ArgumentCaptor.forClass(SensorEventListener.class); 733 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 734 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 735 SensorEventListener listener = listenerCaptor.getValue(); 736 737 // Choose values such that the ring buffer's capacity is extended and the buffer is pruned 738 int increment = 11; 739 int lux = 5000; 740 for (int i = 0; i < 1000; i++) { 741 lux += increment; 742 mClock.fastForward(increment); 743 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux)); 744 } 745 746 int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment + 1); 747 float[] sensorValues = mController.getLastSensorValues(); 748 long[] sensorTimestamps = mController.getLastSensorTimestamps(); 749 750 // Only the values within the horizon should be kept 751 assertEquals(valuesCount, sensorValues.length); 752 assertEquals(valuesCount, sensorTimestamps.length); 753 754 long sensorTimestamp = mClock.now(); 755 for (int i = valuesCount - 1; i >= 1; i--) { 756 assertEquals(lux, sensorValues[i], EPSILON); 757 assertEquals(sensorTimestamp, sensorTimestamps[i]); 758 lux -= increment; 759 sensorTimestamp -= increment; 760 } 761 assertEquals(lux, sensorValues[0], EPSILON); 762 assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]); 763 } 764 765 @Test 766 public void testGetSensorReadingsFullBuffer() throws Exception { 767 ArgumentCaptor<SensorEventListener> listenerCaptor = 768 ArgumentCaptor.forClass(SensorEventListener.class); 769 verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), 770 eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); 771 SensorEventListener listener = listenerCaptor.getValue(); 772 int initialCapacity = 150; 773 774 // Choose values such that the ring buffer is pruned 775 int increment1 = 200; 776 int lux = 5000; 777 for (int i = 0; i < 20; i++) { 778 lux += increment1; 779 mClock.fastForward(increment1); 780 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux)); 781 } 782 783 int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment1 + 1); 784 785 // Choose values such that the buffer becomes full 786 int increment2 = 1; 787 for (int i = 0; i < initialCapacity - valuesCount; i++) { 788 lux += increment2; 789 mClock.fastForward(increment2); 790 listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux)); 791 } 792 793 float[] sensorValues = mController.getLastSensorValues(); 794 long[] sensorTimestamps = mController.getLastSensorTimestamps(); 795 796 // The buffer should be full 797 assertEquals(initialCapacity, sensorValues.length); 798 assertEquals(initialCapacity, sensorTimestamps.length); 799 800 long sensorTimestamp = mClock.now(); 801 for (int i = initialCapacity - 1; i >= 1; i--) { 802 assertEquals(lux, sensorValues[i], EPSILON); 803 assertEquals(sensorTimestamp, sensorTimestamps[i]); 804 805 if (i >= valuesCount) { 806 lux -= increment2; 807 sensorTimestamp -= increment2; 808 } else { 809 lux -= increment1; 810 sensorTimestamp -= increment1; 811 } 812 } 813 assertEquals(lux, sensorValues[0], EPSILON); 814 assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]); 815 } 816 817 @Test 818 public void testResetShortTermModelWhenConfigChanges() { 819 when(mBrightnessMappingStrategy.isForIdleMode()).thenReturn(false); 820 when(mBrightnessMappingStrategy.setBrightnessConfiguration(any())).thenReturn(true); 821 822 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 823 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */, 824 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 825 /* shouldResetShortTermModel= */ false); 826 verify(mBrightnessMappingStrategy, never()).clearUserDataPoints(); 827 828 mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */, 829 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */, 830 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, 831 /* shouldResetShortTermModel= */ true); 832 verify(mBrightnessMappingStrategy).clearUserDataPoints(); 833 } 834 835 @Test 836 public void testUseProvidedShortTermModel() { 837 verify(mBrightnessMappingStrategy, never()).addUserDataPoint(anyFloat(), anyFloat()); 838 839 float userLux = 1000; 840 float userBrightness = 0.3f; 841 setupController(mLightSensor, userLux, userBrightness); 842 verify(mBrightnessMappingStrategy).addUserDataPoint(userLux, userBrightness); 843 } 844 } 845