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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import android.Manifest; 27 import android.app.UiAutomation; 28 import android.content.Context; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageInfo; 31 import android.content.pm.PackageManager; 32 import android.hardware.display.BrightnessChangeEvent; 33 import android.hardware.display.BrightnessConfiguration; 34 import android.hardware.display.BrightnessCorrection; 35 import android.hardware.display.DisplayManager; 36 import android.os.ParcelFileDescriptor; 37 import android.os.PowerManager; 38 import android.provider.Settings; 39 import android.util.Pair; 40 41 import androidx.test.InstrumentationRegistry; 42 import androidx.test.filters.MediumTest; 43 import androidx.test.runner.AndroidJUnit4; 44 45 import org.junit.After; 46 import org.junit.Before; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import java.io.FileDescriptor; 51 import java.io.FileInputStream; 52 import java.util.ArrayList; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Map; 56 import java.util.Scanner; 57 58 @MediumTest 59 @RunWith(AndroidJUnit4.class) 60 public class BrightnessTest { 61 62 private Map<Long, BrightnessChangeEvent> mLastReadEvents = new HashMap<>(); 63 private DisplayManager mDisplayManager; 64 private PowerManager.WakeLock mWakeLock; 65 private Context mContext; 66 67 @Before setUp()68 public void setUp() { 69 mContext = InstrumentationRegistry.getContext(); 70 mDisplayManager = mContext.getSystemService(DisplayManager.class); 71 PowerManager pm = mContext.getSystemService(PowerManager.class); 72 73 mWakeLock = pm.newWakeLock( 74 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 75 "BrightnessTest"); 76 mWakeLock.acquire(); 77 } 78 79 @After tearDown()80 public void tearDown() { 81 if (mWakeLock != null) { 82 mWakeLock.release(); 83 } 84 85 revokePermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS); 86 revokePermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE); 87 } 88 89 @Test testBrightnessSliderTracking()90 public void testBrightnessSliderTracking() throws InterruptedException { 91 if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) { 92 // Don't run as there is no app that has permission to access slider usage. 93 return; 94 } 95 96 int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS); 97 int previousBrightnessMode = 98 getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 99 try { 100 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 101 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 102 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 103 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode); 104 105 grantPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE); 106 107 // Setup and remember some initial state. 108 recordSliderEvents(); 109 waitForFirstSliderEvent(); 110 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20); 111 getNewEvents(1); 112 113 // Update brightness 114 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 60); 115 116 // Check we got a slider event for the change. 117 List<BrightnessChangeEvent> newEvents = getNewEvents(1); 118 assertEquals(1, newEvents.size()); 119 BrightnessChangeEvent firstEvent = newEvents.get(0); 120 assertValidLuxData(firstEvent); 121 122 // Update brightness again 123 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 200); 124 125 // Check we get a second slider event. 126 newEvents = getNewEvents(1); 127 assertEquals(1, newEvents.size()); 128 BrightnessChangeEvent secondEvent = newEvents.get(0); 129 assertValidLuxData(secondEvent); 130 assertEquals(secondEvent.lastBrightness, firstEvent.brightness, 1.0f); 131 assertTrue(secondEvent.isUserSetBrightness); 132 assertTrue("failed " + secondEvent.brightness + " not greater than " + 133 firstEvent.brightness, secondEvent.brightness > firstEvent.brightness); 134 } finally { 135 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness); 136 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode); 137 } 138 } 139 140 @Test testNoTrackingForManualBrightness()141 public void testNoTrackingForManualBrightness() throws InterruptedException { 142 if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) { 143 // Don't run as there is no app that has permission to access slider usage. 144 return; 145 } 146 int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS); 147 int previousBrightnessMode = 148 getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 149 try { 150 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 151 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); 152 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 153 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, mode); 154 155 grantPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE); 156 157 // Setup and remember some initial state. 158 recordSliderEvents(); 159 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20); 160 assertTrue(getNewEvents().isEmpty()); 161 162 // Then change the brightness 163 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 80); 164 Thread.sleep(200); 165 // There shouldn't be any events. 166 assertTrue(getNewEvents().isEmpty()); 167 } finally { 168 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness); 169 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode); 170 } 171 } 172 173 @Test testSliderUsagePermission()174 public void testSliderUsagePermission() { 175 revokePermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE); 176 177 try { 178 mDisplayManager.getBrightnessEvents(); 179 } catch (SecurityException e) { 180 // Expected 181 return; 182 } 183 fail(); 184 } 185 186 @Test testConfigureBrightnessPermission()187 public void testConfigureBrightnessPermission() { 188 revokePermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS); 189 190 BrightnessConfiguration config = 191 new BrightnessConfiguration.Builder( 192 new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f}) 193 .setDescription("some test").build(); 194 195 try { 196 mDisplayManager.setBrightnessConfiguration(config); 197 } catch (SecurityException e) { 198 // Expected 199 return; 200 } 201 fail(); 202 } 203 204 @Test testSetGetSimpleCurve()205 public void testSetGetSimpleCurve() { 206 if (numberOfSystemAppsWithPermission( 207 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) == 0) { 208 // Don't run as there is no app that has permission to push curves. 209 return; 210 } 211 212 grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS); 213 214 BrightnessConfiguration config = 215 new BrightnessConfiguration.Builder( 216 new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f}) 217 .addCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE, 218 BrightnessCorrection.createScaleAndTranslateLog(0.80f, 0.2f)) 219 .addCorrectionByPackageName("some.package.name", 220 BrightnessCorrection.createScaleAndTranslateLog(0.70f, 0.1f)) 221 .setDescription("some test").build(); 222 mDisplayManager.setBrightnessConfiguration(config); 223 BrightnessConfiguration returnedConfig = mDisplayManager.getBrightnessConfiguration(); 224 assertEquals(config, returnedConfig); 225 assertEquals(config.getCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE), 226 BrightnessCorrection.createScaleAndTranslateLog(0.80f, 0.2f)); 227 assertEquals(config.getCorrectionByPackageName("some.package.name"), 228 BrightnessCorrection.createScaleAndTranslateLog(0.70f, 0.1f)); 229 assertNull(config.getCorrectionByCategory(ApplicationInfo.CATEGORY_GAME)); 230 assertNull(config.getCorrectionByPackageName("someother.package.name")); 231 232 // After clearing the curve we should get back the default curve. 233 mDisplayManager.setBrightnessConfiguration(null); 234 returnedConfig = mDisplayManager.getBrightnessConfiguration(); 235 assertEquals(mDisplayManager.getDefaultBrightnessConfiguration(), returnedConfig); 236 } 237 238 @Test testGetDefaultCurve()239 public void testGetDefaultCurve() { 240 if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) { 241 // Don't run as there is no app that has permission to push curves. 242 return; 243 } 244 245 grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS); 246 247 BrightnessConfiguration defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration(); 248 // Must provide a default config if an app with CONFIGURE_DISPLAY_BRIGHTNESS exists. 249 assertNotNull(defaultConfig); 250 251 Pair<float[], float[]> curve = defaultConfig.getCurve(); 252 assertTrue(curve.first.length > 0); 253 assertEquals(curve.first.length, curve.second.length); 254 assertInRange(curve.first, 0, Float.MAX_VALUE); 255 assertInRange(curve.second, 0, Float.MAX_VALUE); 256 assertEquals(0.0, curve.first[0], 0.1); 257 assertMonotonic(curve.first, true /*strictly increasing*/, "lux"); 258 assertMonotonic(curve.second, false /*strictly increasing*/, "nits"); 259 } 260 261 262 @Test testSliderEventsReflectCurves()263 public void testSliderEventsReflectCurves() throws InterruptedException { 264 if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) { 265 // Don't run as there is no app that has permission to access slider usage. 266 return; 267 } 268 if (numberOfSystemAppsWithPermission( 269 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) == 0) { 270 // Don't run as there is no app that has permission to push curves. 271 return; 272 } 273 274 BrightnessConfiguration config = 275 new BrightnessConfiguration.Builder( 276 new float[]{0.0f, 10000.0f},new float[]{15.0f, 400.0f}) 277 .setDescription("model:8").build(); 278 279 int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS); 280 int previousBrightnessMode = 281 getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 282 try { 283 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, 284 Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 285 int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE); 286 assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode); 287 288 grantPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE); 289 grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS); 290 291 // Setup and remember some initial state. 292 recordSliderEvents(); 293 waitForFirstSliderEvent(); 294 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20); 295 getNewEvents(1); 296 297 // Update brightness while we have a custom curve. 298 mDisplayManager.setBrightnessConfiguration(config); 299 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 60); 300 301 // Check we got a slider event for the change. 302 List<BrightnessChangeEvent> newEvents = getNewEvents(1); 303 assertEquals(1, newEvents.size()); 304 BrightnessChangeEvent firstEvent = newEvents.get(0); 305 assertValidLuxData(firstEvent); 306 assertFalse(firstEvent.isDefaultBrightnessConfig); 307 308 // Update brightness again now with default curve. 309 mDisplayManager.setBrightnessConfiguration(null); 310 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 200); 311 312 // Check we get a second slider event. 313 newEvents = getNewEvents(1); 314 assertEquals(1, newEvents.size()); 315 BrightnessChangeEvent secondEvent = newEvents.get(0); 316 assertValidLuxData(secondEvent); 317 assertTrue(secondEvent.isDefaultBrightnessConfig); 318 } finally { 319 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness); 320 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode); 321 } 322 } 323 324 @Test testAtMostOneAppHoldsBrightnessConfigurationPermission()325 public void testAtMostOneAppHoldsBrightnessConfigurationPermission() { 326 assertTrue(numberOfSystemAppsWithPermission( 327 Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) < 2); 328 } 329 330 private void assertValidLuxData(BrightnessChangeEvent event) { 331 assertNotNull(event.luxTimestamps); 332 assertNotNull(event.luxValues); 333 assertTrue(event.luxTimestamps.length > 0); 334 assertEquals(event.luxValues.length, event.luxTimestamps.length); 335 for (int i = 1; i < event.luxTimestamps.length; ++i) { 336 assertTrue(event.luxTimestamps[i - 1] <= event.luxTimestamps[i]); 337 } 338 for (int i = 0; i < event.luxValues.length; ++i) { 339 assertTrue(event.luxValues[i] >= 0.0f); 340 assertTrue(event.luxValues[i] <= Float.MAX_VALUE); 341 assertFalse(Float.isNaN(event.luxValues[i])); 342 } 343 } 344 345 /** 346 * Returns the number of system apps with the given permission. 347 */ numberOfSystemAppsWithPermission(String permission)348 private int numberOfSystemAppsWithPermission(String permission) { 349 List<PackageInfo> packages = mContext.getPackageManager().getPackagesHoldingPermissions( 350 new String[] {permission}, PackageManager.MATCH_SYSTEM_ONLY); 351 return packages.size(); 352 } 353 getNewEvents(int expected)354 private List<BrightnessChangeEvent> getNewEvents(int expected) 355 throws InterruptedException { 356 List<BrightnessChangeEvent> newEvents = new ArrayList<>(); 357 for (int i = 0; newEvents.size() < expected && i < 20; ++i) { 358 if (i != 0) { 359 Thread.sleep(100); 360 } 361 newEvents.addAll(getNewEvents()); 362 } 363 return newEvents; 364 } 365 getNewEvents()366 private List<BrightnessChangeEvent> getNewEvents() { 367 List<BrightnessChangeEvent> newEvents = new ArrayList<>(); 368 List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents(); 369 for (BrightnessChangeEvent event : events) { 370 if (!mLastReadEvents.containsKey(event.timeStamp)) { 371 newEvents.add(event); 372 } 373 } 374 mLastReadEvents = new HashMap<>(); 375 for (BrightnessChangeEvent event : events) { 376 mLastReadEvents.put(event.timeStamp, event); 377 } 378 return newEvents; 379 } 380 recordSliderEvents()381 private void recordSliderEvents() { 382 mLastReadEvents = new HashMap<>(); 383 List<BrightnessChangeEvent> eventsBefore = mDisplayManager.getBrightnessEvents(); 384 for (BrightnessChangeEvent event : eventsBefore) { 385 mLastReadEvents.put(event.timeStamp, event); 386 } 387 } 388 waitForFirstSliderEvent()389 private void waitForFirstSliderEvent() throws InterruptedException { 390 // Keep changing brightness until we get an event to handle devices with sensors 391 // that take a while to warm up. 392 int brightness = 25; 393 for (int i = 0; i < 20; ++i) { 394 setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, brightness); 395 brightness = brightness == 25 ? 80 : 25; 396 Thread.sleep(100); 397 if (!getNewEvents().isEmpty()) { 398 return; 399 } 400 } 401 fail("Failed to fetch first slider event. Is the ambient brightness sensor working?"); 402 } 403 getSystemSetting(String setting)404 private int getSystemSetting(String setting) { 405 return Integer.parseInt(runShellCommand("settings get system " + setting)); 406 } 407 setSystemSetting(String setting, int value)408 private void setSystemSetting(String setting, int value) { 409 runShellCommand("settings put system " + setting + " " + Integer.toString(value)); 410 } 411 grantPermission(String permission)412 private void grantPermission(String permission) { 413 InstrumentationRegistry.getInstrumentation().getUiAutomation() 414 .grantRuntimePermission(mContext.getPackageName(), permission); 415 } 416 revokePermission(String permission)417 private void revokePermission(String permission) { 418 InstrumentationRegistry.getInstrumentation().getUiAutomation() 419 .revokeRuntimePermission(mContext.getPackageName(), permission); 420 } 421 runShellCommand(String cmd)422 private String runShellCommand(String cmd) { 423 UiAutomation automation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 424 ParcelFileDescriptor output = automation.executeShellCommand(cmd); 425 String result = convertFileDescriptorToString(output.getFileDescriptor()); 426 return result.trim(); 427 } 428 convertFileDescriptorToString(FileDescriptor desc)429 private String convertFileDescriptorToString(FileDescriptor desc) { 430 try (Scanner s = new Scanner(new FileInputStream(desc)).useDelimiter("\\Z")) { 431 return s.hasNext() ? s.next() : ""; 432 } 433 } 434 assertInRange(float[] values, float min, float max)435 private static void assertInRange(float[] values, float min, float max) { 436 for (int i = 0; i < values.length; i++) { 437 assertFalse(Float.isNaN(values[i])); 438 assertTrue(values[i] >= min); 439 assertTrue(values[i] <= max); 440 } 441 } 442 assertMonotonic(float[] values, boolean strictlyIncreasing, String name)443 private static void assertMonotonic(float[] values, boolean strictlyIncreasing, String name) { 444 if (values.length <= 1) { 445 return; 446 } 447 float prev = values[0]; 448 for (int i = 1; i < values.length; i++) { 449 if (prev > values[i] || (prev == values[i] && strictlyIncreasing)) { 450 String condition = strictlyIncreasing ? "strictly increasing" : "monotonic"; 451 fail(name + " values must be " + condition); 452 } 453 prev = values[i]; 454 } 455 } 456 } 457