1 /* 2 * Copyright (C) 2018 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 android.display.cts; 18 19 import static android.hardware.display.BrightnessCorrection.createScaleAndTranslateLog; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertNull; 27 import static org.junit.Assert.assertThrows; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assert.fail; 30 import static org.junit.Assume.assumeNotNull; 31 import static org.junit.Assume.assumeTrue; 32 33 import android.Manifest; 34 import android.content.Context; 35 import android.content.pm.ApplicationInfo; 36 import android.content.pm.PackageInfo; 37 import android.content.pm.PackageManager; 38 import android.hardware.display.BrightnessChangeEvent; 39 import android.hardware.display.BrightnessConfiguration; 40 import android.hardware.display.DisplayManager; 41 import android.platform.test.annotations.AppModeFull; 42 import android.provider.Settings; 43 import android.util.Pair; 44 45 import androidx.test.filters.MediumTest; 46 import androidx.test.platform.app.InstrumentationRegistry; 47 import androidx.test.runner.AndroidJUnit4; 48 49 import com.google.common.collect.Range; 50 51 import org.junit.Before; 52 import org.junit.Ignore; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 56 import java.util.ArrayList; 57 import java.util.HashMap; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.function.Predicate; 61 62 @AppModeFull 63 @MediumTest 64 @RunWith(AndroidJUnit4.class) 65 public class BrightnessTest extends TestBase { 66 67 private Map<Long, BrightnessChangeEvent> mLastReadEvents = new HashMap<>(); 68 private DisplayManager mDisplayManager; 69 private Context mContext; 70 private PackageManager mPackageManager; 71 72 @Before setUp()73 public void setUp() { 74 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 75 mDisplayManager = mContext.getSystemService(DisplayManager.class); 76 mPackageManager = mContext.getPackageManager(); 77 launchScreenOnActivity(); 78 revokePermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS); 79 try (var usage = new PermissionClosable(Manifest.permission.BRIGHTNESS_SLIDER_USAGE)) { 80 recordSliderEvents(); 81 } 82 } 83 84 @Ignore("b/359582534") 85 @Test testBrightnessSliderTracking()86 public void testBrightnessSliderTracking() throws InterruptedException { 87 // Only run if we have a valid ambient light sensor. 88 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT)); 89 90 // Don't run as there is no app that has permission to access slider usage. 91 assumeTrue( 92 numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0); 93 94 assumeTrue( 95 numberOfSystemAppsWithPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) 96 > 0); 97 98 try (var brtClosable = new BrightnessClosable()) { 99 var defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 100 // This might be null, meaning that the device doesn't support autobrightness 101 assumeNotNull(defaultConfig); 102 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 103 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 104 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 105 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode); 106 waitForFirstSliderEvent(); 107 setDisplayBrightness(brtClosable.getMinimumBrightness()); 108 109 // Update brightness 110 var newEvents = setDisplayBrightness(brtClosable.getMiddleBrightness()); 111 assertEquals(1, newEvents.size()); 112 BrightnessChangeEvent firstEvent = newEvents.get(0); 113 assertValidLuxData(firstEvent); 114 115 // Update brightness again 116 newEvents = setDisplayBrightness(brtClosable.getMaximumBrightness()); 117 assertEquals(1, newEvents.size()); 118 BrightnessChangeEvent secondEvent = newEvents.get(0); 119 assertValidLuxData(secondEvent); 120 assertEquals(secondEvent.lastBrightness, firstEvent.brightness, 1.0f); 121 assertTrue(secondEvent.isUserSetBrightness); 122 assertTrue("failed " + secondEvent.brightness + " not greater than " + 123 firstEvent.brightness, secondEvent.brightness > firstEvent.brightness); 124 } 125 } 126 127 @Test testBrightnesSliderTrackingDecrease()128 public void testBrightnesSliderTrackingDecrease() throws InterruptedException { 129 // Only run if we have a valid ambient light sensor. 130 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT)); 131 132 // Don't run as there is no app that has permission to access slider usage. 133 assumeTrue( 134 numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0); 135 136 assumeTrue( 137 numberOfSystemAppsWithPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) 138 > 0); 139 140 try (var brtClosable = new BrightnessClosable()) { 141 var defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 142 // This might be null, meaning that the device doesn't support autobrightness 143 assumeNotNull(defaultConfig); 144 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 145 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 146 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 147 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode); 148 waitForFirstSliderEvent(); 149 setDisplayBrightness(brtClosable.getMaximumBrightness()); 150 var newEvents = setDisplayBrightness(brtClosable.getMiddleBrightness()); 151 assertEquals(1, newEvents.size()); 152 BrightnessChangeEvent firstEvent = newEvents.get(0); 153 assertValidLuxData(firstEvent); 154 // Update brightness again 155 newEvents = setDisplayBrightness(brtClosable.getMinimumBrightness()); 156 assertEquals(1, newEvents.size()); 157 BrightnessChangeEvent secondEvent = newEvents.get(0); 158 assertValidLuxData(secondEvent); 159 assertEquals(secondEvent.lastBrightness, firstEvent.brightness, 1.0f); 160 assertTrue(secondEvent.isUserSetBrightness); 161 assertTrue("failed " + secondEvent.brightness + " not less than " 162 + firstEvent.brightness, secondEvent.brightness < firstEvent.brightness); 163 } 164 } 165 166 @Test testNoTrackingForManualBrightness()167 public void testNoTrackingForManualBrightness() throws InterruptedException { 168 // Don't run as there is no app that has permission to access slider usage. 169 assumeTrue( 170 numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0); 171 172 try (var brtClosable = new BrightnessClosable()) { 173 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 174 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); 175 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 176 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, mode); 177 recordSliderEvents(); 178 var newEvents = setDisplayBrightness(brtClosable.getMinimumBrightness()); 179 assertTrue(newEvents.isEmpty()); 180 // Then change the brightness 181 newEvents = setDisplayBrightness(brtClosable.getMaximumBrightness()); 182 // There shouldn't be any events. 183 assertTrue(newEvents.isEmpty()); 184 } 185 } 186 187 @Test testNoColorSampleData()188 public void testNoColorSampleData() throws InterruptedException { 189 // Only run if we have a valid ambient light sensor. 190 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT)); 191 192 // Don't run as there is no app that has permission to access slider usage. 193 assumeTrue( 194 numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0); 195 196 // Don't run as there is no app that has permission to push curves. 197 assumeTrue(numberOfSystemAppsWithPermission( 198 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0); 199 200 try (var brtClosable = new BrightnessClosable()) { 201 var defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 202 // This might be null, meaning that the device doesn't support autobrightness 203 assumeNotNull(defaultConfig); 204 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 205 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 206 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 207 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode); 208 209 // Set brightness config to not sample color. 210 BrightnessConfiguration config = 211 new BrightnessConfiguration.Builder( 212 new float[]{0.0f, 1000.0f}, new float[]{20.0f, 500.0f}) 213 .setShouldCollectColorSamples(false).build(); 214 mDisplayManager.setBrightnessConfiguration(config); 215 waitForFirstSliderEvent(); 216 var newEvents = setDisplayBrightness(brtClosable.getMinimumBrightness()); 217 // No color samples. 218 assertEquals(0, newEvents.get(0).colorSampleDuration); 219 assertNull(newEvents.get(0).colorValueBuckets); 220 // No test for sampling color as support is optional. 221 } 222 } 223 224 @Test testSliderUsagePermission()225 public void testSliderUsagePermission() { 226 assertThrows(SecurityException.class, mDisplayManager::getBrightnessEvents); 227 } 228 229 @Test testConfigureBrightnessPermission()230 public void testConfigureBrightnessPermission() { 231 BrightnessConfiguration config = 232 new BrightnessConfiguration.Builder( 233 new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f}) 234 .setDescription("some test").build(); 235 236 assertThrows(SecurityException.class, 237 () -> mDisplayManager.setBrightnessConfiguration(config)); 238 } 239 240 @Test testSetGetSimpleCurve()241 public void testSetGetSimpleCurve() { 242 // Only run if we have a valid ambient light sensor. 243 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT)); 244 245 // Don't run as there is no app that has permission to push curves. 246 assumeTrue(numberOfSystemAppsWithPermission( 247 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0); 248 249 try (var brt = new PermissionClosable(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)) { 250 var defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 251 // This might be null, meaning that the device doesn't support brightness configuration 252 assumeNotNull(defaultConfig); 253 254 BrightnessConfiguration config = 255 new BrightnessConfiguration.Builder( 256 new float[]{0.0f, 1000.0f}, new float[]{20.0f, 500.0f}) 257 .addCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE, 258 createScaleAndTranslateLog(0.80f, 0.2f)) 259 .addCorrectionByPackageName("some.package.name", 260 createScaleAndTranslateLog(0.70f, 0.1f)) 261 .setShortTermModelTimeoutMillis( 262 defaultConfig.getShortTermModelTimeoutMillis() + 1000L) 263 .setShortTermModelLowerLuxMultiplier( 264 defaultConfig.getShortTermModelLowerLuxMultiplier() + 0.2f) 265 .setShortTermModelUpperLuxMultiplier( 266 defaultConfig.getShortTermModelUpperLuxMultiplier() + 0.3f) 267 .setDescription("some test").build(); 268 mDisplayManager.setBrightnessConfiguration(config); 269 BrightnessConfiguration returnedConfig = mDisplayManager.getBrightnessConfiguration(); 270 assertEquals(config, returnedConfig); 271 assertEquals(returnedConfig.getCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE), 272 createScaleAndTranslateLog(0.80f, 0.2f)); 273 assertEquals(returnedConfig.getCorrectionByPackageName("some.package.name"), 274 createScaleAndTranslateLog(0.70f, 0.1f)); 275 assertNull(returnedConfig.getCorrectionByCategory(ApplicationInfo.CATEGORY_GAME)); 276 assertNull(returnedConfig.getCorrectionByPackageName("someother.package.name")); 277 assertEquals(defaultConfig.getShortTermModelTimeoutMillis() + 1000L, 278 returnedConfig.getShortTermModelTimeoutMillis()); 279 assertEquals(defaultConfig.getShortTermModelLowerLuxMultiplier() + 0.2f, 280 returnedConfig.getShortTermModelLowerLuxMultiplier(), 0.001f); 281 assertEquals(defaultConfig.getShortTermModelUpperLuxMultiplier() + 0.3f, 282 returnedConfig.getShortTermModelUpperLuxMultiplier(), 0.001f); 283 284 // After clearing the curve we should get back the default curve. 285 mDisplayManager.setBrightnessConfiguration(null); 286 returnedConfig = mDisplayManager.getBrightnessConfiguration(); 287 assertEquals(mDisplayManager.getDefaultBrightnessConfiguration(), returnedConfig); 288 } 289 } 290 291 @Test testGetDefaultCurve()292 public void testGetDefaultCurve() { 293 // Don't run as there is no app that has permission to push curves. 294 assumeTrue(numberOfSystemAppsWithPermission( 295 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0); 296 297 try (var brt = new PermissionClosable(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)) { 298 var defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 299 assumeNotNull(defaultConfig); 300 301 Pair<float[], float[]> curve = defaultConfig.getCurve(); 302 assertTrue(curve.first.length > 0); 303 assertEquals(curve.first.length, curve.second.length); 304 assertInRange(curve.first, 0, Float.MAX_VALUE); 305 assertInRange(curve.second, 0, Float.MAX_VALUE); 306 assertEquals(0.0, curve.first[0], 0.1); 307 assertMonotonic(curve.first, true /*strictly increasing*/, "lux"); 308 assertMonotonic(curve.second, false /*strictly increasing*/, "nits"); 309 assertTrue(defaultConfig.getShortTermModelLowerLuxMultiplier() > 0.0f); 310 assertTrue(defaultConfig.getShortTermModelLowerLuxMultiplier() < 10.0f); 311 assertTrue(defaultConfig.getShortTermModelUpperLuxMultiplier() > 0.0f); 312 assertTrue(defaultConfig.getShortTermModelUpperLuxMultiplier() < 10.0f); 313 assertTrue(defaultConfig.getShortTermModelTimeoutMillis() > 0L); 314 assertTrue(defaultConfig.getShortTermModelTimeoutMillis() < 24 * 60 * 60 * 1000L); 315 assertFalse(defaultConfig.shouldCollectColorSamples()); 316 } 317 } 318 319 320 @Test 321 public void testSliderEventsReflectCurves() throws InterruptedException { 322 // Only run if we have a valid ambient light sensor. 323 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT)); 324 325 // Don't run as there is no app that has permission to access slider usage. 326 assumeTrue( 327 numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0); 328 // Don't run as there is no app that has permission to push curves. 329 assumeTrue(numberOfSystemAppsWithPermission( 330 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0); 331 332 BrightnessConfiguration config = 333 new BrightnessConfiguration.Builder( 334 new float[]{0.0f, 10000.0f},new float[]{15.0f, 400.0f}) 335 .setDescription("model:8").build(); 336 try(var brtClosable = new BrightnessClosable())337 try (var brtClosable = new BrightnessClosable()) { 338 var defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 339 // This might be null, meaning that the device doesn't support autobrightness 340 assumeNotNull(defaultConfig); 341 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 342 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 343 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 344 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode); 345 waitForFirstSliderEvent(); 346 setDisplayBrightness(brtClosable.getMinimumBrightness()); 347 348 // Update brightness while we have a custom curve. 349 mDisplayManager.setBrightnessConfiguration(config); 350 var newEvents = setDisplayBrightness(brtClosable.getMiddleBrightness(), 351 (e) -> !e.isDefaultBrightnessConfig); 352 assertFalse(newEvents.isEmpty()); 353 BrightnessChangeEvent firstEvent = newEvents.get(newEvents.size() - 1); 354 assertValidLuxData(firstEvent); 355 356 // Update brightness again now with default curve. 357 mDisplayManager.setBrightnessConfiguration(null); 358 newEvents = setDisplayBrightness(brtClosable.getMaximumBrightness(), 359 (e) -> e.isDefaultBrightnessConfig); 360 assertFalse(newEvents.isEmpty()); 361 BrightnessChangeEvent secondEvent = newEvents.get(newEvents.size() - 1); 362 assertValidLuxData(secondEvent); 363 } 364 } 365 366 @Test testAtMostOneAppHoldsBrightnessConfigurationPermission()367 public void testAtMostOneAppHoldsBrightnessConfigurationPermission() { 368 assertTrue(numberOfSystemAppsWithPermission( 369 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) < 2); 370 } 371 372 @Test 373 public void testSetAndGetBrightnessConfiguration() { 374 assumeTrue(numberOfSystemAppsWithPermission( 375 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0); 376 try(var brightnessAutoClosable = new BrightnessClosable())377 try (var brightnessAutoClosable = new BrightnessClosable()) { 378 BrightnessConfiguration configSet = 379 new BrightnessConfiguration.Builder( 380 new float[]{0.0f, 1345.0f}, new float[]{15.0f, 250.0f}) 381 .setDescription("model:8").build(); 382 BrightnessConfiguration configGet; 383 384 mDisplayManager.setBrightnessConfiguration(configSet); 385 configGet = mDisplayManager.getBrightnessConfiguration(); 386 387 assertNotNull(configGet); 388 assertEquals(configSet, configGet); 389 } 390 } 391 392 @Test testSetAndGetPerDisplay()393 public void testSetAndGetPerDisplay() throws InterruptedException{ 394 // Only run if we have a valid ambient light sensor. 395 assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT)); 396 397 assumeTrue(numberOfSystemAppsWithPermission( 398 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0); 399 400 try (var brtClosable = new BrightnessClosable()) { 401 var defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 402 // This might be null, meaning that the device doesn't support autobrightness 403 assumeNotNull(defaultConfig); 404 // Setup slider events. 405 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 406 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 407 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 408 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode); 409 waitForFirstSliderEvent(); 410 setDisplayBrightness(brtClosable.getMinimumBrightness()); 411 412 // Get a unique display id via brightness change event 413 var newEvents = setDisplayBrightness(brtClosable.getMiddleBrightness()); 414 BrightnessChangeEvent firstEvent = newEvents.get(0); 415 String uniqueDisplayId = firstEvent.uniqueDisplayId; 416 assertNotNull(uniqueDisplayId); 417 418 // Set & get a configuration for that specific display 419 BrightnessConfiguration configSet = 420 new BrightnessConfiguration.Builder( 421 new float[]{0.0f, 12345.0f}, new float[]{15.0f, 200.0f}) 422 .setDescription("test:0").build(); 423 mDisplayManager.setBrightnessConfigurationForDisplay(configSet, uniqueDisplayId); 424 BrightnessConfiguration returnedConfig = 425 mDisplayManager.getBrightnessConfigurationForDisplay(uniqueDisplayId); 426 427 assertEquals(configSet, returnedConfig); 428 429 // Set & get a different configuration for that specific display 430 BrightnessConfiguration configSetTwo = 431 new BrightnessConfiguration.Builder( 432 new float[]{0.0f, 678.0f}, new float[]{15.0f, 500.0f}) 433 .setDescription("test:1").build(); 434 mDisplayManager.setBrightnessConfigurationForDisplay(configSetTwo, uniqueDisplayId); 435 BrightnessConfiguration returnedConfigTwo = 436 mDisplayManager.getBrightnessConfigurationForDisplay(uniqueDisplayId); 437 438 assertEquals(configSetTwo, returnedConfigTwo); 439 440 // Since brightness change event will happen on the default display, this should also 441 // return the same value. 442 BrightnessConfiguration unspecifiedDisplayConfig = 443 mDisplayManager.getBrightnessConfiguration(); 444 assertEquals(configSetTwo, unspecifiedDisplayConfig); 445 } 446 } 447 assertValidLuxData(BrightnessChangeEvent event)448 private void assertValidLuxData(BrightnessChangeEvent event) { 449 assertNotNull(event.luxTimestamps); 450 assertNotNull(event.luxValues); 451 assertTrue(event.luxTimestamps.length > 0); 452 assertEquals(event.luxValues.length, event.luxTimestamps.length); 453 for (int i = 1; i < event.luxTimestamps.length; ++i) { 454 assertTrue(event.luxTimestamps[i - 1] <= event.luxTimestamps[i]); 455 } 456 for (int i = 0; i < event.luxValues.length; ++i) { 457 assertTrue(event.luxValues[i] >= 0.0f); 458 assertTrue(event.luxValues[i] <= Float.MAX_VALUE); 459 assertFalse(Float.isNaN(event.luxValues[i])); 460 } 461 } 462 463 /** 464 * Returns the number of system apps with the given permission. 465 */ numberOfSystemAppsWithPermission(String permission)466 private int numberOfSystemAppsWithPermission(String permission) { 467 List<PackageInfo> packages = mContext.getPackageManager().getPackagesHoldingPermissions( 468 new String[]{permission}, PackageManager.MATCH_SYSTEM_ONLY); 469 packages.removeIf(packageInfo -> packageInfo.packageName.equals("com.android.shell")); 470 return packages.size(); 471 } 472 getNewEvents(int expected)473 private List<BrightnessChangeEvent> getNewEvents(int expected) throws InterruptedException { 474 return getNewEvents(expected, (e) -> true); 475 } 476 getNewEvents(int expected, Predicate<BrightnessChangeEvent> pred)477 private List<BrightnessChangeEvent> getNewEvents(int expected, 478 Predicate<BrightnessChangeEvent> pred) throws InterruptedException { 479 List<BrightnessChangeEvent> newEvents = new ArrayList<>(); 480 for (int i = 0; newEvents.size() < expected && i < 20; ++i) { 481 if (i != 0) { 482 Thread.sleep(100); 483 } 484 for (BrightnessChangeEvent e : getNewEvents()) { 485 if (pred.test(e)) { 486 newEvents.add(e); 487 } 488 } 489 } 490 return newEvents; 491 } 492 getNewEvents()493 private List<BrightnessChangeEvent> getNewEvents() { 494 List<BrightnessChangeEvent> newEvents = new ArrayList<>(); 495 List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents(); 496 for (BrightnessChangeEvent event : events) { 497 if (!mLastReadEvents.containsKey(event.timeStamp)) { 498 newEvents.add(event); 499 } 500 } 501 mLastReadEvents = new HashMap<>(); 502 for (BrightnessChangeEvent event : events) { 503 mLastReadEvents.put(event.timeStamp, event); 504 } 505 return newEvents; 506 } 507 recordSliderEvents()508 private void recordSliderEvents() { 509 mLastReadEvents = new HashMap<>(); 510 List<BrightnessChangeEvent> eventsBefore = mDisplayManager.getBrightnessEvents(); 511 for (BrightnessChangeEvent event : eventsBefore) { 512 mLastReadEvents.put(event.timeStamp, event); 513 } 514 } 515 waitForFirstSliderEvent()516 private void waitForFirstSliderEvent() throws InterruptedException { 517 recordSliderEvents(); 518 // Keep changing brightness until we get an event to handle devices with sensors 519 // that take a while to warm up. 520 int brightness = 25; 521 for (int i = 0; i < 20; ++i) { 522 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, brightness); 523 brightness = brightness == 25 ? 80 : 25; 524 Thread.sleep(100); 525 if (!getNewEvents().isEmpty()) { 526 return; 527 } 528 } 529 fail("Failed to fetch first slider event. Is the ambient brightness sensor working?"); 530 } 531 getSystemSetting(String setting)532 private int getSystemSetting(String setting) { 533 return Integer.parseInt(runShellCommand("settings get system " + setting)); 534 } 535 setSystemSetting(String setting, int value)536 private void setSystemSetting(String setting, int value) { 537 runShellCommand("settings put system " + setting + " " + value); 538 } 539 setDisplayBrightness(float value)540 private List<BrightnessChangeEvent> setDisplayBrightness(float value) { 541 return setDisplayBrightness(value, (e) -> true); 542 } 543 setDisplayBrightness(float value, Predicate<BrightnessChangeEvent> pred)544 private List<BrightnessChangeEvent> setDisplayBrightness(float value, 545 Predicate<BrightnessChangeEvent> pred) { 546 runShellCommand("cmd display set-brightness " + value); 547 try { 548 return getNewEvents(1, pred); 549 } catch (InterruptedException e) { 550 // If Thread.sleep gets interrupted rethrow as runtime exception to avoid annotation. 551 throw new RuntimeException(e); 552 } 553 } 554 grantPermission(String permission)555 private void grantPermission(String permission) { 556 InstrumentationRegistry.getInstrumentation().getUiAutomation() 557 .grantRuntimePermission(mContext.getPackageName(), permission); 558 } 559 revokePermission(String permission)560 private void revokePermission(String permission) { 561 InstrumentationRegistry.getInstrumentation().getUiAutomation() 562 .revokeRuntimePermission(mContext.getPackageName(), permission); 563 } 564 assertInRange(float[] values, float min, float max)565 private static void assertInRange(float[] values, float min, float max) { 566 for (int i = 0; i < values.length; i++) { 567 assertFalse(Float.isNaN(values[i])); 568 assertTrue(values[i] >= min); 569 assertTrue(values[i] <= max); 570 } 571 } 572 assertMonotonic(float[] values, boolean strictlyIncreasing, String name)573 private static void assertMonotonic(float[] values, boolean strictlyIncreasing, String name) { 574 if (values.length <= 1) { 575 return; 576 } 577 float prev = values[0]; 578 for (int i = 1; i < values.length; i++) { 579 if (prev > values[i] || (prev == values[i] && strictlyIncreasing)) { 580 String condition = strictlyIncreasing ? "strictly increasing" : "monotonic"; 581 fail(name + " values must be " + condition); 582 } 583 prev = values[i]; 584 } 585 } 586 587 private class BrightnessClosable implements AutoCloseable { 588 private final float mPrevBrightness; 589 private final int mPrevBrightnessMode; 590 private final BrightnessConfiguration mPrevBrightnessConfig; 591 private final float mMaxBrightness; 592 private final float mMinBrightness; 593 private final PermissionClosable mBrightnessPermission; 594 private final PermissionClosable mSliderPermission; 595 BrightnessClosable()596 BrightnessClosable() { 597 mBrightnessPermission = new PermissionClosable( 598 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS); 599 mSliderPermission = new PermissionClosable(Manifest.permission.BRIGHTNESS_SLIDER_USAGE); 600 mPrevBrightness = brightnessIntToFloat(getSystemSetting( 601 Settings.System.SCREEN_BRIGHTNESS)); 602 mPrevBrightnessMode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 603 mPrevBrightnessConfig = mDisplayManager.getBrightnessConfiguration(); 604 // Enforce min brightness to get the system absolute min brightness 605 setDisplayBrightness(0f); 606 mMinBrightness = brightnessIntToFloat(getSystemSetting( 607 Settings.System.SCREEN_BRIGHTNESS)); 608 // Enforce max brightness to get the system absolute max brightness 609 setDisplayBrightness(1.0f); 610 mMaxBrightness = brightnessIntToFloat(getSystemSetting( 611 Settings.System.SCREEN_BRIGHTNESS)); 612 } 613 614 @Override close()615 public void close() { 616 setDisplayBrightness(mPrevBrightness); 617 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, mPrevBrightnessMode); 618 mDisplayManager.setBrightnessConfiguration(mPrevBrightnessConfig); 619 mSliderPermission.close(); 620 mBrightnessPermission.close(); 621 } 622 getMinimumBrightness()623 float getMinimumBrightness() { 624 return mMinBrightness; 625 } 626 getMaximumBrightness()627 float getMaximumBrightness() { 628 return mMaxBrightness; 629 } 630 getMiddleBrightness()631 float getMiddleBrightness() { 632 return (getMinimumBrightness() + getMaximumBrightness()) / 2f; 633 } 634 635 /** 636 * Converts between the int brightness system and the float brightness system. 637 */ brightnessIntToFloat(int brightnessInt)638 private static float brightnessIntToFloat(int brightnessInt) { 639 assertThat(brightnessInt).isIn(Range.closed(1, 255)); 640 return (float) (brightnessInt - 1) / 254f; 641 } 642 } 643 644 private class PermissionClosable implements AutoCloseable { 645 private final String mPermission; 646 PermissionClosable(String permission)647 PermissionClosable(String permission) { 648 mPermission = permission; 649 grantPermission(mPermission); 650 } 651 652 @Override close()653 public void close() { 654 revokePermission(mPermission); 655 } 656 } 657 } 658