1 /* 2 * Copyright 2017 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 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 25 import android.content.Context; 26 import android.hardware.display.BrightnessConfiguration; 27 import android.os.Handler; 28 import android.os.test.TestLooper; 29 import android.util.Pair; 30 31 import androidx.test.InstrumentationRegistry; 32 import androidx.test.filters.SmallTest; 33 import androidx.test.runner.AndroidJUnit4; 34 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.io.ByteArrayInputStream; 40 import java.io.ByteArrayOutputStream; 41 import java.io.FileNotFoundException; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.io.OutputStream; 45 import java.nio.charset.StandardCharsets; 46 47 @SmallTest 48 @RunWith(AndroidJUnit4.class) 49 public class PersistentDataStoreTest { 50 private PersistentDataStore mDataStore; 51 private TestInjector mInjector; 52 private TestLooper mTestLooper; 53 54 @Before setUp()55 public void setUp() { 56 mInjector = new TestInjector(); 57 mTestLooper = new TestLooper(); 58 Handler handler = new Handler(mTestLooper.getLooper()); 59 mDataStore = new PersistentDataStore(mInjector, handler); 60 } 61 62 @Test testLoadingBrightnessConfigurations()63 public void testLoadingBrightnessConfigurations() { 64 String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 65 + "<display-manager-state>\n" 66 + " <brightness-configurations>\n" 67 + " <brightness-configuration" 68 + " user-serial=\"1\"" 69 + " package-name=\"example.com\"" 70 + " timestamp=\"123456\">\n" 71 + " <brightness-curve description=\"something\">\n" 72 + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n" 73 + " <brightness-point lux=\"25\" nits=\"35.94\"/>\n" 74 + " </brightness-curve>\n" 75 + " </brightness-configuration>\n" 76 + " <brightness-configuration user-serial=\"3\">\n" 77 + " <brightness-curve>\n" 78 + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n" 79 + " <brightness-point lux=\"10.2\" nits=\"15\"/>\n" 80 + " </brightness-curve>\n" 81 + " </brightness-configuration>\n" 82 + " </brightness-configurations>\n" 83 + "</display-manager-state>\n"; 84 InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); 85 mInjector.setReadStream(is); 86 mDataStore.loadIfNeeded(); 87 BrightnessConfiguration config = mDataStore.getBrightnessConfiguration(1 /*userSerial*/); 88 Pair<float[], float[]> curve = config.getCurve(); 89 float[] expectedLux = { 0f, 25f }; 90 float[] expectedNits = { 13.25f, 35.94f }; 91 assertArrayEquals(expectedLux, curve.first, "lux"); 92 assertArrayEquals(expectedNits, curve.second, "nits"); 93 assertEquals("something", config.getDescription()); 94 95 config = mDataStore.getBrightnessConfiguration(3 /*userSerial*/); 96 curve = config.getCurve(); 97 expectedLux = new float[] { 0f, 10.2f }; 98 expectedNits = new float[] { 13.25f, 15f }; 99 assertArrayEquals(expectedLux, curve.first, "lux"); 100 assertArrayEquals(expectedNits, curve.second, "nits"); 101 assertNull(config.getDescription()); 102 } 103 104 @Test testBrightnessConfigWithInvalidCurveIsIgnored()105 public void testBrightnessConfigWithInvalidCurveIsIgnored() { 106 String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 107 + "<display-manager-state>\n" 108 + " <brightness-configurations>\n" 109 + " <brightness-configuration user-serial=\"0\">\n" 110 + " <brightness-curve>\n" 111 + " <brightness-point lux=\"1\" nits=\"13.25\"/>\n" 112 + " <brightness-point lux=\"25\" nits=\"35.94\"/>\n" 113 + " </brightness-curve>\n" 114 + " </brightness-configuration>\n" 115 + " </brightness-configurations>\n" 116 + "</display-manager-state>\n"; 117 InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); 118 mInjector.setReadStream(is); 119 mDataStore.loadIfNeeded(); 120 assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); 121 } 122 123 @Test testBrightnessConfigWithInvalidFloatsIsIgnored()124 public void testBrightnessConfigWithInvalidFloatsIsIgnored() { 125 String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 126 + "<display-manager-state>\n" 127 + " <brightness-configurations>\n" 128 + " <brightness-configuration user-serial=\"0\">\n" 129 + " <brightness-curve>\n" 130 + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n" 131 + " <brightness-point lux=\"0xFF\" nits=\"foo\"/>\n" 132 + " </brightness-curve>\n" 133 + " </brightness-configuration>\n" 134 + " </brightness-configurations>\n" 135 + "</display-manager-state>\n"; 136 InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); 137 mInjector.setReadStream(is); 138 mDataStore.loadIfNeeded(); 139 assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); 140 } 141 142 @Test testEmptyBrightnessConfigurationsDoesntCrash()143 public void testEmptyBrightnessConfigurationsDoesntCrash() { 144 String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 145 + "<display-manager-state>\n" 146 + " <brightness-configurations />\n" 147 + "</display-manager-state>\n"; 148 InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); 149 mInjector.setReadStream(is); 150 mDataStore.loadIfNeeded(); 151 assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); 152 } 153 154 @Test testStoreAndReloadOfDisplayBrightnessConfigurations()155 public void testStoreAndReloadOfDisplayBrightnessConfigurations() throws InterruptedException { 156 final String uniqueDisplayId = "test:123"; 157 int userSerial = 0; 158 String packageName = "pdsTestPackage"; 159 final float[] lux = { 0f, 10f }; 160 final float[] nits = {1f, 100f }; 161 final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) 162 .setDescription("a description") 163 .build(); 164 mDataStore.loadIfNeeded(); 165 assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, 166 userSerial)); 167 168 DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) { 169 @Override 170 public boolean hasStableUniqueId() { 171 return true; 172 } 173 174 @Override 175 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 176 return null; 177 } 178 }; 179 180 mDataStore.setBrightnessConfigurationForDisplayLocked(config, testDisplayDevice, userSerial, 181 packageName); 182 183 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 184 mInjector.setWriteStream(baos); 185 mDataStore.saveIfNeeded(); 186 mTestLooper.dispatchAll(); 187 assertTrue(mInjector.wasWriteSuccessful()); 188 TestInjector newInjector = new TestInjector(); 189 PersistentDataStore newDataStore = new PersistentDataStore(newInjector); 190 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 191 newInjector.setReadStream(bais); 192 newDataStore.loadIfNeeded(); 193 assertNotNull(newDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, 194 userSerial)); 195 assertEquals(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, 196 userSerial), newDataStore.getBrightnessConfigurationForDisplayLocked( 197 uniqueDisplayId, userSerial)); 198 } 199 200 @Test testSetBrightnessConfigurationFailsWithUnstableId()201 public void testSetBrightnessConfigurationFailsWithUnstableId() { 202 final String uniqueDisplayId = "test:123"; 203 int userSerial = 0; 204 String packageName = "pdsTestPackage"; 205 final float[] lux = { 0f, 10f }; 206 final float[] nits = {1f, 100f }; 207 final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) 208 .setDescription("a description") 209 .build(); 210 mDataStore.loadIfNeeded(); 211 assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId, 212 userSerial)); 213 214 DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) { 215 @Override 216 public boolean hasStableUniqueId() { 217 return false; 218 } 219 220 @Override 221 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 222 return null; 223 } 224 }; 225 226 assertFalse(mDataStore.setBrightnessConfigurationForDisplayLocked( 227 config, testDisplayDevice, userSerial, packageName)); 228 } 229 230 @Test testStoreAndReloadOfBrightnessConfigurations()231 public void testStoreAndReloadOfBrightnessConfigurations() throws InterruptedException { 232 final float[] lux = { 0f, 10f }; 233 final float[] nits = {1f, 100f }; 234 final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) 235 .setDescription("a description") 236 .build(); 237 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 238 String packageName = context.getPackageName(); 239 240 mDataStore.loadIfNeeded(); 241 assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); 242 mDataStore.setBrightnessConfigurationForUser(config, 0, packageName); 243 244 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 245 mInjector.setWriteStream(baos); 246 mDataStore.saveIfNeeded(); 247 mTestLooper.dispatchAll(); 248 assertTrue(mInjector.wasWriteSuccessful()); 249 250 TestInjector newInjector = new TestInjector(); 251 PersistentDataStore newDataStore = new PersistentDataStore(newInjector); 252 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 253 newInjector.setReadStream(bais); 254 newDataStore.loadIfNeeded(); 255 assertNotNull(newDataStore.getBrightnessConfiguration(0 /*userSerial*/)); 256 assertEquals(mDataStore.getBrightnessConfiguration(0 /*userSerial*/), 257 newDataStore.getBrightnessConfiguration(0 /*userSerial*/)); 258 } 259 260 @Test testNullBrightnessConfiguration()261 public void testNullBrightnessConfiguration() { 262 final float[] lux = { 0f, 10f }; 263 final float[] nits = {1f, 100f }; 264 int userSerial = 0; 265 final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) 266 .setDescription("a description") 267 .build(); 268 mDataStore.loadIfNeeded(); 269 assertNull(mDataStore.getBrightnessConfiguration(userSerial)); 270 271 mDataStore.setBrightnessConfigurationForUser(config, userSerial, "packagename"); 272 assertNotNull(mDataStore.getBrightnessConfiguration(userSerial)); 273 274 mDataStore.setBrightnessConfigurationForUser(null, userSerial, "packagename"); 275 assertNull(mDataStore.getBrightnessConfiguration(userSerial)); 276 } 277 278 @Test testStoreAndRestoreResolution()279 public void testStoreAndRestoreResolution() { 280 final String uniqueDisplayId = "test:123"; 281 DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) { 282 @Override 283 public boolean hasStableUniqueId() { 284 return true; 285 } 286 287 @Override 288 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 289 return null; 290 } 291 }; 292 int width = 35; 293 int height = 45; 294 mDataStore.loadIfNeeded(); 295 mDataStore.setUserPreferredResolution(testDisplayDevice, width, height); 296 297 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 298 mInjector.setWriteStream(baos); 299 mDataStore.saveIfNeeded(); 300 mTestLooper.dispatchAll(); 301 assertTrue(mInjector.wasWriteSuccessful()); 302 TestInjector newInjector = new TestInjector(); 303 PersistentDataStore newDataStore = new PersistentDataStore(newInjector); 304 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 305 newInjector.setReadStream(bais); 306 newDataStore.loadIfNeeded(); 307 assertNotNull(newDataStore.getUserPreferredResolution(testDisplayDevice)); 308 assertEquals(35, newDataStore.getUserPreferredResolution(testDisplayDevice).x); 309 assertEquals(35, mDataStore.getUserPreferredResolution(testDisplayDevice).x); 310 assertEquals(45, newDataStore.getUserPreferredResolution(testDisplayDevice).y); 311 assertEquals(45, mDataStore.getUserPreferredResolution(testDisplayDevice).y); 312 } 313 314 @Test testStoreAndRestoreRefreshRate()315 public void testStoreAndRestoreRefreshRate() { 316 final String uniqueDisplayId = "test:123"; 317 DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) { 318 @Override 319 public boolean hasStableUniqueId() { 320 return true; 321 } 322 323 @Override 324 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 325 return null; 326 } 327 }; 328 float refreshRate = 85.3f; 329 mDataStore.loadIfNeeded(); 330 mDataStore.setUserPreferredRefreshRate(testDisplayDevice, refreshRate); 331 332 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 333 mInjector.setWriteStream(baos); 334 mDataStore.saveIfNeeded(); 335 mTestLooper.dispatchAll(); 336 assertTrue(mInjector.wasWriteSuccessful()); 337 TestInjector newInjector = new TestInjector(); 338 PersistentDataStore newDataStore = new PersistentDataStore(newInjector); 339 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 340 newInjector.setReadStream(bais); 341 newDataStore.loadIfNeeded(); 342 assertNotNull(newDataStore.getUserPreferredRefreshRate(testDisplayDevice)); 343 assertEquals(85.3f, mDataStore.getUserPreferredRefreshRate(testDisplayDevice), 01.f); 344 assertEquals(85.3f, newDataStore.getUserPreferredRefreshRate(testDisplayDevice), 0.1f); 345 } 346 347 @Test testBrightnessInitialisesWithInvalidFloat()348 public void testBrightnessInitialisesWithInvalidFloat() { 349 final String uniqueDisplayId = "test:123"; 350 DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) { 351 @Override 352 public boolean hasStableUniqueId() { 353 return true; 354 } 355 356 @Override 357 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 358 return null; 359 } 360 }; 361 362 // Set any value which initialises Display state 363 float refreshRate = 85.3f; 364 mDataStore.loadIfNeeded(); 365 mDataStore.setUserPreferredRefreshRate(testDisplayDevice, refreshRate); 366 367 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 368 mInjector.setWriteStream(baos); 369 mDataStore.saveIfNeeded(); 370 mTestLooper.dispatchAll(); 371 assertTrue(mInjector.wasWriteSuccessful()); 372 TestInjector newInjector = new TestInjector(); 373 PersistentDataStore newDataStore = new PersistentDataStore(newInjector); 374 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 375 newInjector.setReadStream(bais); 376 newDataStore.loadIfNeeded(); 377 assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice))); 378 } 379 380 @Test testStoreAndRestoreBrightnessNitsForDefaultDisplay()381 public void testStoreAndRestoreBrightnessNitsForDefaultDisplay() { 382 float brightnessNitsForDefaultDisplay = 190; 383 mDataStore.loadIfNeeded(); 384 mDataStore.setBrightnessNitsForDefaultDisplay(brightnessNitsForDefaultDisplay); 385 386 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 387 mInjector.setWriteStream(baos); 388 mDataStore.saveIfNeeded(); 389 mTestLooper.dispatchAll(); 390 assertTrue(mInjector.wasWriteSuccessful()); 391 TestInjector newInjector = new TestInjector(); 392 PersistentDataStore newDataStore = new PersistentDataStore(newInjector); 393 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 394 newInjector.setReadStream(bais); 395 newDataStore.loadIfNeeded(); 396 assertEquals(brightnessNitsForDefaultDisplay, 397 mDataStore.getBrightnessNitsForDefaultDisplay(), 0); 398 assertEquals(brightnessNitsForDefaultDisplay, 399 newDataStore.getBrightnessNitsForDefaultDisplay(), 0); 400 } 401 402 @Test testInitialBrightnessNitsForDefaultDisplay()403 public void testInitialBrightnessNitsForDefaultDisplay() { 404 mDataStore.loadIfNeeded(); 405 assertEquals(-1, mDataStore.getBrightnessNitsForDefaultDisplay(), 0); 406 } 407 408 public class TestInjector extends PersistentDataStore.Injector { 409 private InputStream mReadStream; 410 private OutputStream mWriteStream; 411 412 private boolean mWasSuccessful; 413 414 @Override openRead()415 public InputStream openRead() throws FileNotFoundException { 416 if (mReadStream != null) { 417 return mReadStream; 418 } else { 419 throw new FileNotFoundException(); 420 } 421 } 422 423 @Override startWrite()424 public OutputStream startWrite() { 425 return mWriteStream; 426 } 427 428 @Override finishWrite(OutputStream os, boolean success)429 public void finishWrite(OutputStream os, boolean success) { 430 mWasSuccessful = success; 431 try { 432 os.close(); 433 } catch (IOException e) { 434 // This method can't throw IOException since the super implementation doesn't, so 435 // we just wrap it in a RuntimeException so we end up crashing the test all the 436 // same. 437 throw new RuntimeException(e); 438 } 439 } 440 setReadStream(InputStream is)441 public void setReadStream(InputStream is) { 442 mReadStream = is; 443 } 444 setWriteStream(OutputStream os)445 public void setWriteStream(OutputStream os) { 446 mWriteStream = os; 447 } 448 wasWriteSuccessful()449 public boolean wasWriteSuccessful() { 450 return mWasSuccessful; 451 } 452 } 453 assertArrayEquals(float[] expected, float[] actual, String name)454 private static void assertArrayEquals(float[] expected, float[] actual, String name) { 455 assertEquals("Expected " + name + " arrays to be the same length!", 456 expected.length, actual.length); 457 for (int i = 0; i < expected.length; i++) { 458 assertEquals("Expected " + name + " arrays to be equivalent when value " + i 459 + "differs", expected[i], actual[i], 0.01 /*tolerance*/); 460 } 461 } 462 } 463