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.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertNull; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 27 import android.app.ActivityManager; 28 import android.content.BroadcastReceiver; 29 import android.content.ComponentName; 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.ParceledListSlice; 35 import android.database.ContentObserver; 36 import android.hardware.SensorEvent; 37 import android.hardware.SensorEventListener; 38 import android.hardware.display.AmbientBrightnessDayStats; 39 import android.hardware.display.BrightnessChangeEvent; 40 import android.hardware.display.BrightnessConfiguration; 41 import android.hardware.display.DisplayManager; 42 import android.hardware.display.DisplayedContentSample; 43 import android.hardware.display.DisplayedContentSamplingAttributes; 44 import android.os.BatteryManager; 45 import android.os.Handler; 46 import android.os.HandlerThread; 47 import android.os.MessageQueue; 48 import android.os.Parcel; 49 import android.os.RemoteException; 50 import android.os.SystemClock; 51 import android.os.UserManager; 52 import android.provider.Settings; 53 import android.util.AtomicFile; 54 import android.view.Display; 55 56 import androidx.test.InstrumentationRegistry; 57 import androidx.test.filters.SmallTest; 58 import androidx.test.runner.AndroidJUnit4; 59 60 import com.android.internal.R; 61 62 import org.junit.Before; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 66 import java.io.ByteArrayInputStream; 67 import java.io.ByteArrayOutputStream; 68 import java.io.IOException; 69 import java.io.InputStream; 70 import java.lang.reflect.Constructor; 71 import java.nio.charset.StandardCharsets; 72 import java.util.HashMap; 73 import java.util.List; 74 import java.util.Map; 75 import java.util.concurrent.CountDownLatch; 76 import java.util.concurrent.TimeUnit; 77 78 @SmallTest 79 @RunWith(AndroidJUnit4.class) 80 public class BrightnessTrackerTest { 81 private static final float DEFAULT_INITIAL_BRIGHTNESS = 2.5f; 82 private static final boolean DEFAULT_COLOR_SAMPLING_ENABLED = true; 83 private static final float FLOAT_DELTA = 0.01f; 84 85 private BrightnessTracker mTracker; 86 private TestInjector mInjector; 87 88 private static Object sHandlerLock = new Object(); 89 private static Handler sHandler; 90 private static HandlerThread sThread = 91 new HandlerThread("brightness.test", android.os.Process.THREAD_PRIORITY_BACKGROUND); 92 93 private int mDefaultNightModeColorTemperature; 94 ensureHandler()95 private static Handler ensureHandler() { 96 synchronized (sHandlerLock) { 97 if (sHandler == null) { 98 sThread.start(); 99 sHandler = new Handler(sThread.getLooper()); 100 } 101 return sHandler; 102 } 103 } 104 105 106 @Before setUp()107 public void setUp() throws Exception { 108 mInjector = new TestInjector(ensureHandler()); 109 110 mTracker = new BrightnessTracker(InstrumentationRegistry.getContext(), mInjector); 111 mDefaultNightModeColorTemperature = 112 InstrumentationRegistry.getContext().getResources().getInteger( 113 R.integer.config_nightDisplayColorTemperatureDefault); 114 } 115 116 @Test testStartStopTrackerScreenOnOff()117 public void testStartStopTrackerScreenOnOff() { 118 mInjector.mInteractive = false; 119 startTracker(mTracker); 120 assertNull(mInjector.mSensorListener); 121 assertNotNull(mInjector.mBroadcastReceiver); 122 assertTrue(mInjector.mIdleScheduled); 123 mInjector.sendScreenChange(/*screen on */ true); 124 assertNotNull(mInjector.mSensorListener); 125 assertTrue(mInjector.mColorSamplingEnabled); 126 127 mInjector.sendScreenChange(/*screen on */ false); 128 assertNull(mInjector.mSensorListener); 129 assertFalse(mInjector.mColorSamplingEnabled); 130 131 // Turn screen on while brightness mode is manual 132 mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ false); 133 mInjector.sendScreenChange(/*screen on */ true); 134 assertNull(mInjector.mSensorListener); 135 assertFalse(mInjector.mColorSamplingEnabled); 136 137 // Set brightness mode to automatic while screen is off. 138 mInjector.sendScreenChange(/*screen on */ false); 139 mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ true); 140 assertNull(mInjector.mSensorListener); 141 assertFalse(mInjector.mColorSamplingEnabled); 142 143 // Turn on screen while brightness mode is automatic. 144 mInjector.sendScreenChange(/*screen on */ true); 145 assertNotNull(mInjector.mSensorListener); 146 assertTrue(mInjector.mColorSamplingEnabled); 147 148 mTracker.stop(); 149 assertNull(mInjector.mSensorListener); 150 assertNull(mInjector.mBroadcastReceiver); 151 assertFalse(mInjector.mIdleScheduled); 152 assertFalse(mInjector.mColorSamplingEnabled); 153 } 154 155 @Test testModifyBrightnessConfiguration()156 public void testModifyBrightnessConfiguration() { 157 mInjector.mInteractive = true; 158 // Start with tracker not listening for color samples. 159 startTracker(mTracker, DEFAULT_INITIAL_BRIGHTNESS, /* collectColorSamples= */ false); 160 assertFalse(mInjector.mColorSamplingEnabled); 161 162 // Update brightness config to enabled color sampling. 163 mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( 164 /* collectColorSamples= */ true)); 165 mInjector.waitForHandler(); 166 assertTrue(mInjector.mColorSamplingEnabled); 167 168 // Update brightness config to disable color sampling. 169 mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( 170 /* collectColorSamples= */ false)); 171 mInjector.waitForHandler(); 172 assertFalse(mInjector.mColorSamplingEnabled); 173 174 // Pretend screen is off, update config to turn on color sampling. 175 mInjector.sendScreenChange(/*screen on */ false); 176 mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( 177 /* collectColorSamples= */ true)); 178 mInjector.waitForHandler(); 179 assertFalse(mInjector.mColorSamplingEnabled); 180 181 // Pretend screen is on. 182 mInjector.sendScreenChange(/*screen on */ true); 183 assertTrue(mInjector.mColorSamplingEnabled); 184 185 mTracker.stop(); 186 assertFalse(mInjector.mColorSamplingEnabled); 187 } 188 189 @Test testNoColorSampling_WrongPixelFormat()190 public void testNoColorSampling_WrongPixelFormat() { 191 mInjector.mDefaultSamplingAttributes = 192 new DisplayedContentSamplingAttributes( 193 0x23, 194 mInjector.mDefaultSamplingAttributes.getDataspace(), 195 mInjector.mDefaultSamplingAttributes.getComponentMask()); 196 startTracker(mTracker); 197 assertFalse(mInjector.mColorSamplingEnabled); 198 assertNull(mInjector.mDisplayListener); 199 } 200 201 @Test testNoColorSampling_MissingComponent()202 public void testNoColorSampling_MissingComponent() { 203 mInjector.mDefaultSamplingAttributes = 204 new DisplayedContentSamplingAttributes( 205 mInjector.mDefaultSamplingAttributes.getPixelFormat(), 206 mInjector.mDefaultSamplingAttributes.getDataspace(), 207 0x2); 208 startTracker(mTracker); 209 assertFalse(mInjector.mColorSamplingEnabled); 210 assertNull(mInjector.mDisplayListener); 211 } 212 213 @Test testNoColorSampling_NoSupport()214 public void testNoColorSampling_NoSupport() { 215 mInjector.mDefaultSamplingAttributes = null; 216 startTracker(mTracker); 217 assertFalse(mInjector.mColorSamplingEnabled); 218 assertNull(mInjector.mDisplayListener); 219 } 220 221 @Test testColorSampling_FrameRateChange()222 public void testColorSampling_FrameRateChange() { 223 startTracker(mTracker); 224 assertTrue(mInjector.mColorSamplingEnabled); 225 assertNotNull(mInjector.mDisplayListener); 226 int noFramesSampled = mInjector.mNoColorSamplingFrames; 227 mInjector.mFrameRate = 120.0f; 228 // Wrong display 229 mInjector.mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY + 10); 230 assertEquals(noFramesSampled, mInjector.mNoColorSamplingFrames); 231 // Correct display 232 mInjector.mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY); 233 assertEquals(noFramesSampled * 2, mInjector.mNoColorSamplingFrames); 234 } 235 236 @Test testAdaptiveOnOff()237 public void testAdaptiveOnOff() { 238 mInjector.mInteractive = true; 239 mInjector.mIsBrightnessModeAutomatic = false; 240 startTracker(mTracker); 241 assertNull(mInjector.mSensorListener); 242 assertNotNull(mInjector.mBroadcastReceiver); 243 assertNotNull(mInjector.mContentObserver); 244 assertTrue(mInjector.mIdleScheduled); 245 assertFalse(mInjector.mColorSamplingEnabled); 246 assertNull(mInjector.mDisplayListener); 247 248 mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true); 249 assertNotNull(mInjector.mSensorListener); 250 assertTrue(mInjector.mColorSamplingEnabled); 251 assertNotNull(mInjector.mDisplayListener); 252 253 SensorEventListener listener = mInjector.mSensorListener; 254 DisplayManager.DisplayListener displayListener = mInjector.mDisplayListener; 255 mInjector.mSensorListener = null; 256 mInjector.mColorSamplingEnabled = false; 257 mInjector.mDisplayListener = null; 258 // Duplicate notification 259 mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true); 260 // Sensor shouldn't have been registered as it was already registered. 261 assertNull(mInjector.mSensorListener); 262 assertFalse(mInjector.mColorSamplingEnabled); 263 assertNull(mInjector.mDisplayListener); 264 mInjector.mSensorListener = listener; 265 mInjector.mDisplayListener = displayListener; 266 mInjector.mColorSamplingEnabled = true; 267 268 mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ false); 269 assertNull(mInjector.mSensorListener); 270 assertFalse(mInjector.mColorSamplingEnabled); 271 assertNull(mInjector.mDisplayListener); 272 273 mTracker.stop(); 274 assertNull(mInjector.mSensorListener); 275 assertNull(mInjector.mBroadcastReceiver); 276 assertNull(mInjector.mContentObserver); 277 assertFalse(mInjector.mIdleScheduled); 278 assertFalse(mInjector.mColorSamplingEnabled); 279 assertNull(mInjector.mDisplayListener); 280 } 281 282 @Test testBrightnessEvent()283 public void testBrightnessEvent() { 284 final int brightness = 20; 285 286 startTracker(mTracker); 287 mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); 288 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); 289 notifyBrightnessChanged(mTracker, brightness); 290 List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); 291 mTracker.stop(); 292 293 assertEquals(1, events.size()); 294 BrightnessChangeEvent event = events.get(0); 295 assertEquals(mInjector.currentTimeMillis(), event.timeStamp); 296 assertEquals(1, event.luxValues.length); 297 assertEquals(1.0f, event.luxValues[0], FLOAT_DELTA); 298 assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2), 299 event.luxTimestamps[0]); 300 assertEquals(brightness, event.brightness, FLOAT_DELTA); 301 assertEquals(DEFAULT_INITIAL_BRIGHTNESS, event.lastBrightness, FLOAT_DELTA); 302 303 // System had no data so these should all be at defaults. 304 assertEquals(Float.NaN, event.batteryLevel, 0.0); 305 assertFalse(event.nightMode); 306 assertEquals(mDefaultNightModeColorTemperature, event.colorTemperature); 307 } 308 309 @Test testBrightnessFullPopulatedEvent()310 public void testBrightnessFullPopulatedEvent() { 311 final int initialBrightness = 230; 312 final int brightness = 130; 313 314 mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); 315 mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3333); 316 317 startTracker(mTracker, initialBrightness, DEFAULT_COLOR_SAMPLING_ENABLED); 318 mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(), 319 batteryChangeEvent(30, 60)); 320 mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f)); 321 final long sensorTime = mInjector.currentTimeMillis(); 322 notifyBrightnessChanged(mTracker, brightness); 323 List<BrightnessChangeEvent> eventsNoPackage 324 = mTracker.getEvents(0, false).getList(); 325 List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); 326 mTracker.stop(); 327 328 assertEquals(1, events.size()); 329 BrightnessChangeEvent event = events.get(0); 330 assertEquals(event.timeStamp, mInjector.currentTimeMillis()); 331 assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f); 332 assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps); 333 assertEquals(brightness, event.brightness, FLOAT_DELTA); 334 assertEquals(initialBrightness, event.lastBrightness, FLOAT_DELTA); 335 assertEquals(0.5, event.batteryLevel, FLOAT_DELTA); 336 assertTrue(event.nightMode); 337 assertEquals(3333, event.colorTemperature); 338 assertEquals("a.package", event.packageName); 339 assertEquals(0, event.userId); 340 assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets); 341 assertEquals(10000, event.colorSampleDuration); 342 343 assertEquals(1, eventsNoPackage.size()); 344 assertNull(eventsNoPackage.get(0).packageName); 345 } 346 347 @Test testIgnoreAutomaticBrightnessChange()348 public void testIgnoreAutomaticBrightnessChange() { 349 final int initialBrightness = 30; 350 startTracker(mTracker, initialBrightness, DEFAULT_COLOR_SAMPLING_ENABLED); 351 mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); 352 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1)); 353 354 final int systemUpdatedBrightness = 20; 355 notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/, 356 0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/, 357 false /*isDefaultBrightnessConfig*/); 358 List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); 359 // No events because we filtered out our change. 360 assertEquals(0, events.size()); 361 362 final int firstUserUpdateBrightness = 20; 363 // Then change comes from somewhere else so we shouldn't filter. 364 notifyBrightnessChanged(mTracker, firstUserUpdateBrightness); 365 366 // and with a different brightness value. 367 final int secondUserUpdateBrightness = 34; 368 notifyBrightnessChanged(mTracker, secondUserUpdateBrightness); 369 events = mTracker.getEvents(0, true).getList(); 370 371 assertEquals(2, events.size()); 372 // First event is change from system update (20) to first user update (20) 373 assertEquals(systemUpdatedBrightness, events.get(0).lastBrightness, FLOAT_DELTA); 374 assertEquals(firstUserUpdateBrightness, events.get(0).brightness, FLOAT_DELTA); 375 // Second event is from first to second user update. 376 assertEquals(firstUserUpdateBrightness, events.get(1).lastBrightness, FLOAT_DELTA); 377 assertEquals(secondUserUpdateBrightness, events.get(1).brightness, FLOAT_DELTA); 378 379 mTracker.stop(); 380 } 381 382 @Test testLimitedBufferSize()383 public void testLimitedBufferSize() { 384 startTracker(mTracker); 385 mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); 386 387 for (int brightness = 0; brightness <= 255; ++brightness) { 388 mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); 389 mInjector.incrementTime(TimeUnit.SECONDS.toNanos(1)); 390 notifyBrightnessChanged(mTracker, brightness); 391 } 392 List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); 393 mTracker.stop(); 394 395 // Should be capped at 100 events, and they should be the most recent 100. 396 assertEquals(100, events.size()); 397 for (int i = 0; i < events.size(); i++) { 398 BrightnessChangeEvent event = events.get(i); 399 assertEquals(156 + i, event.brightness, FLOAT_DELTA); 400 } 401 } 402 403 @Test testLimitedSensorEvents()404 public void testLimitedSensorEvents() { 405 final int brightness = 20; 406 407 startTracker(mTracker); 408 // 20 Sensor events 1 second apart. 409 for (int i = 0; i < 20; ++i) { 410 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1)); 411 mInjector.mSensorListener.onSensorChanged(createSensorEvent(i + 1.0f)); 412 } 413 notifyBrightnessChanged(mTracker, 20); 414 List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); 415 mTracker.stop(); 416 417 assertEquals(1, events.size()); 418 BrightnessChangeEvent event = events.get(0); 419 assertEquals(mInjector.currentTimeMillis(), event.timeStamp); 420 421 // 12 sensor events, 11 for 0->10 seconds + 1 previous event. 422 assertEquals(12, event.luxValues.length); 423 for (int i = 0; i < 12; ++i) { 424 assertEquals(event.luxTimestamps[11 - i], 425 mInjector.currentTimeMillis() - i * TimeUnit.SECONDS.toMillis(1)); 426 } 427 assertEquals(brightness, event.brightness, FLOAT_DELTA); 428 } 429 430 @Test testReadEvents()431 public void testReadEvents() throws Exception { 432 BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(), 433 mInjector); 434 mInjector.mCurrentTimeMillis = System.currentTimeMillis(); 435 long someTimeAgo = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(12); 436 long twoMonthsAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60); 437 // 3 Events in the file but one too old to read. 438 String eventFile = 439 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 440 + "<events>\n" 441 + "<event nits=\"194.2\" timestamp=\"" 442 + Long.toString(someTimeAgo) + "\" packageName=\"" 443 + "com.example.app\" user=\"10\" " 444 + "lastNits=\"32.333\" " 445 + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n" 446 + "lux=\"32.2,31.1\" luxTimestamps=\"" 447 + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"" 448 + "defaultConfig=\"true\" powerSaveFactor=\"0.5\" userPoint=\"true\" />" 449 + "<event nits=\"71\" timestamp=\"" 450 + Long.toString(someTimeAgo) + "\" packageName=\"" 451 + "com.android.anapp\" user=\"11\" " 452 + "lastNits=\"32\" " 453 + "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\"\n" 454 + "lux=\"132.2,131.1\" luxTimestamps=\"" 455 + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"" 456 + "colorSampleDuration=\"3456\" colorValueBuckets=\"123,598,23,19\"/>" 457 // Event that is too old so shouldn't show up. 458 + "<event nits=\"142\" timestamp=\"" 459 + Long.toString(twoMonthsAgo) + "\" packageName=\"" 460 + "com.example.app\" user=\"10\" " 461 + "lastNits=\"32\" " 462 + "batteryLevel=\"1.0\" nightMode=\"false\" colorTemperature=\"0\"\n" 463 + "lux=\"32.2,31.1\" luxTimestamps=\"" 464 + Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>" 465 + "</events>"; 466 tracker.readEventsLocked(getInputStream(eventFile)); 467 List<BrightnessChangeEvent> events = tracker.getEvents(0, true).getList(); 468 assertEquals(1, events.size()); 469 BrightnessChangeEvent event = events.get(0); 470 assertEquals(someTimeAgo, event.timeStamp); 471 assertEquals(194.2, event.brightness, FLOAT_DELTA); 472 assertArrayEquals(new float[] {32.2f, 31.1f}, event.luxValues, FLOAT_DELTA); 473 assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps); 474 assertEquals(32.333, event.lastBrightness, FLOAT_DELTA); 475 assertEquals(0, event.userId); 476 assertFalse(event.nightMode); 477 assertEquals(1.0f, event.batteryLevel, FLOAT_DELTA); 478 assertEquals("com.example.app", event.packageName); 479 assertTrue(event.isDefaultBrightnessConfig); 480 assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA); 481 assertTrue(event.isUserSetBrightness); 482 assertNull(event.colorValueBuckets); 483 484 events = tracker.getEvents(1, true).getList(); 485 assertEquals(1, events.size()); 486 event = events.get(0); 487 assertEquals(someTimeAgo, event.timeStamp); 488 assertEquals(71, event.brightness, FLOAT_DELTA); 489 assertArrayEquals(new float[] {132.2f, 131.1f}, event.luxValues, FLOAT_DELTA); 490 assertArrayEquals(new long[] {someTimeAgo, someTimeAgo}, event.luxTimestamps); 491 assertEquals(32, event.lastBrightness, FLOAT_DELTA); 492 assertEquals(1, event.userId); 493 assertTrue(event.nightMode); 494 assertEquals(3235, event.colorTemperature); 495 assertEquals(0.5f, event.batteryLevel, FLOAT_DELTA); 496 assertEquals("com.android.anapp", event.packageName); 497 // Not present in the event so default to false. 498 assertFalse(event.isDefaultBrightnessConfig); 499 assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA); 500 assertFalse(event.isUserSetBrightness); 501 assertEquals(3456L, event.colorSampleDuration); 502 assertArrayEquals(new long[] {123L, 598L, 23L, 19L}, event.colorValueBuckets); 503 504 // Pretend user 1 is a profile of user 0. 505 mInjector.mProfiles = new int[]{0, 1}; 506 events = tracker.getEvents(0, true).getList(); 507 // Both events should now be returned. 508 assertEquals(2, events.size()); 509 BrightnessChangeEvent userZeroEvent; 510 BrightnessChangeEvent userOneEvent; 511 if (events.get(0).userId == 0) { 512 userZeroEvent = events.get(0); 513 userOneEvent = events.get(1); 514 } else { 515 userZeroEvent = events.get(1); 516 userOneEvent = events.get(0); 517 } 518 assertEquals(0, userZeroEvent.userId); 519 assertEquals("com.example.app", userZeroEvent.packageName); 520 assertEquals(1, userOneEvent.userId); 521 // Events from user 1 should have the package name redacted 522 assertNull(userOneEvent.packageName); 523 } 524 525 @Test testFailedRead()526 public void testFailedRead() { 527 String someTimeAgo = 528 Long.toString(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(12)); 529 mInjector.mCurrentTimeMillis = System.currentTimeMillis(); 530 531 BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(), 532 mInjector); 533 String eventFile = "junk in the file"; 534 try { 535 tracker.readEventsLocked(getInputStream(eventFile)); 536 } catch (IOException e) { 537 // Expected; 538 } 539 assertEquals(0, tracker.getEvents(0, true).getList().size()); 540 541 // Missing lux value. 542 eventFile = 543 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" 544 + "<events>\n" 545 + "<event nits=\"194\" timestamp=\"" + someTimeAgo + "\" packageName=\"" 546 + "com.example.app\" user=\"10\" " 547 + "batteryLevel=\"0.7\" nightMode=\"false\" colorTemperature=\"0\" />\n" 548 + "</events>"; 549 try { 550 tracker.readEventsLocked(getInputStream(eventFile)); 551 } catch (IOException e) { 552 // Expected; 553 } 554 assertEquals(0, tracker.getEvents(0, true).getList().size()); 555 } 556 557 @Test testWriteThenRead()558 public void testWriteThenRead() throws Exception { 559 final int brightness = 20; 560 561 mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); 562 mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339); 563 564 startTracker(mTracker); 565 mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(), 566 batteryChangeEvent(30, 100)); 567 mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f)); 568 final long firstSensorTime = mInjector.currentTimeMillis(); 569 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); 570 mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f)); 571 final long secondSensorTime = mInjector.currentTimeMillis(); 572 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3)); 573 notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/, 574 0.5f /*powerBrightnessFactor*/, true /*hasUserBrightnessPoints*/, 575 false /*isDefaultBrightnessConfig*/); 576 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 577 mTracker.writeEventsLocked(baos); 578 mTracker.stop(); 579 580 baos.flush(); 581 ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray()); 582 BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(), 583 mInjector); 584 tracker.readEventsLocked(input); 585 List<BrightnessChangeEvent> events = tracker.getEvents(0, true).getList(); 586 587 assertEquals(1, events.size()); 588 BrightnessChangeEvent event = events.get(0); 589 assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA); 590 assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps); 591 assertEquals(brightness, event.brightness, FLOAT_DELTA); 592 assertEquals(0.3, event.batteryLevel, FLOAT_DELTA); 593 assertTrue(event.nightMode); 594 assertEquals(3339, event.colorTemperature); 595 assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA); 596 assertTrue(event.isUserSetBrightness); 597 assertFalse(event.isDefaultBrightnessConfig); 598 assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets); 599 assertEquals(10000, event.colorSampleDuration); 600 } 601 602 @Test testWritePrunesOldEvents()603 public void testWritePrunesOldEvents() throws Exception { 604 final int brightness = 20; 605 606 mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1); 607 mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339); 608 609 startTracker(mTracker); 610 mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(), 611 batteryChangeEvent(30, 100)); 612 mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f)); 613 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1)); 614 mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f)); 615 final long sensorTime = mInjector.currentTimeMillis(); 616 notifyBrightnessChanged(mTracker, brightness); 617 618 // 31 days later 619 mInjector.incrementTime(TimeUnit.DAYS.toMillis(31)); 620 mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f)); 621 notifyBrightnessChanged(mTracker, brightness); 622 final long eventTime = mInjector.currentTimeMillis(); 623 624 List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); 625 assertEquals(2, events.size()); 626 627 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 628 mTracker.writeEventsLocked(baos); 629 events = mTracker.getEvents(0, true).getList(); 630 mTracker.stop(); 631 632 assertEquals(1, events.size()); 633 BrightnessChangeEvent event = events.get(0); 634 assertEquals(eventTime, event.timeStamp); 635 636 // We will keep one of the old sensor events because we keep 1 event outside the window. 637 assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA); 638 assertArrayEquals(new long[] {sensorTime, eventTime}, event.luxTimestamps); 639 assertEquals(brightness, event.brightness, FLOAT_DELTA); 640 assertEquals(0.3, event.batteryLevel, FLOAT_DELTA); 641 assertTrue(event.nightMode); 642 assertEquals(3339, event.colorTemperature); 643 } 644 645 @Test testParcelUnParcel()646 public void testParcelUnParcel() { 647 Parcel parcel = Parcel.obtain(); 648 BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder(); 649 builder.setBrightness(23f); 650 builder.setTimeStamp(345L); 651 builder.setPackageName("com.example"); 652 builder.setUserId(12); 653 float[] luxValues = new float[2]; 654 luxValues[0] = 3000.0f; 655 luxValues[1] = 4000.0f; 656 builder.setLuxValues(luxValues); 657 long[] luxTimestamps = new long[2]; 658 luxTimestamps[0] = 325L; 659 luxTimestamps[1] = 315L; 660 builder.setLuxTimestamps(luxTimestamps); 661 builder.setBatteryLevel(0.7f); 662 builder.setNightMode(false); 663 builder.setColorTemperature(345); 664 builder.setLastBrightness(50f); 665 builder.setColorValues(new long[] {23, 34, 45}, 1000L); 666 BrightnessChangeEvent event = builder.build(); 667 668 event.writeToParcel(parcel, 0); 669 byte[] parceled = parcel.marshall(); 670 parcel.recycle(); 671 672 parcel = Parcel.obtain(); 673 parcel.unmarshall(parceled, 0, parceled.length); 674 parcel.setDataPosition(0); 675 676 BrightnessChangeEvent event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel); 677 parcel.recycle(); 678 assertEquals(event.brightness, event2.brightness, FLOAT_DELTA); 679 assertEquals(event.timeStamp, event2.timeStamp); 680 assertEquals(event.packageName, event2.packageName); 681 assertEquals(event.userId, event2.userId); 682 assertArrayEquals(event.luxValues, event2.luxValues, FLOAT_DELTA); 683 assertArrayEquals(event.luxTimestamps, event2.luxTimestamps); 684 assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA); 685 assertEquals(event.nightMode, event2.nightMode); 686 assertEquals(event.colorTemperature, event2.colorTemperature); 687 assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA); 688 assertArrayEquals(event.colorValueBuckets, event2.colorValueBuckets); 689 assertEquals(event.colorSampleDuration, event2.colorSampleDuration); 690 691 parcel = Parcel.obtain(); 692 builder.setBatteryLevel(Float.NaN); 693 event = builder.build(); 694 event.writeToParcel(parcel, 0); 695 parceled = parcel.marshall(); 696 parcel.recycle(); 697 698 parcel = Parcel.obtain(); 699 parcel.unmarshall(parceled, 0, parceled.length); 700 parcel.setDataPosition(0); 701 event2 = BrightnessChangeEvent.CREATOR.createFromParcel(parcel); 702 assertEquals(event.batteryLevel, event2.batteryLevel, FLOAT_DELTA); 703 } 704 705 @Test testNonNullAmbientStats()706 public void testNonNullAmbientStats() { 707 // getAmbientBrightnessStats should return an empty list rather than null when 708 // tracker isn't started or hasn't collected any data. 709 ParceledListSlice<AmbientBrightnessDayStats> slice = mTracker.getAmbientBrightnessStats(0); 710 assertNotNull(slice); 711 assertTrue(slice.getList().isEmpty()); 712 startTracker(mTracker); 713 slice = mTracker.getAmbientBrightnessStats(0); 714 assertNotNull(slice); 715 assertTrue(slice.getList().isEmpty()); 716 } 717 718 @Test testBackgroundHandlerDelay()719 public void testBackgroundHandlerDelay() { 720 final int brightness = 20; 721 722 // Setup tracker. 723 startTracker(mTracker); 724 mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f)); 725 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); 726 727 // Block handler from running. 728 final CountDownLatch latch = new CountDownLatch(1); 729 mInjector.mHandler.post( 730 () -> { 731 try { 732 latch.await(); 733 } catch (InterruptedException e) { 734 fail(e.getMessage()); 735 } 736 }); 737 738 // Send an event. 739 long eventTime = mInjector.currentTimeMillis(); 740 mTracker.notifyBrightnessChanged(brightness, true /*userInitiated*/, 741 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/, 742 false /*isDefaultBrightnessConfig*/); 743 744 // Time passes before handler can run. 745 mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2)); 746 747 // Let the handler run. 748 latch.countDown(); 749 mInjector.waitForHandler(); 750 751 List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList(); 752 mTracker.stop(); 753 754 // Check event was recorded with time it was sent rather than handler ran. 755 assertEquals(1, events.size()); 756 BrightnessChangeEvent event = events.get(0); 757 assertEquals(eventTime, event.timeStamp); 758 } 759 getInputStream(String data)760 private InputStream getInputStream(String data) { 761 return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); 762 } 763 batteryChangeEvent(int level, int scale)764 private Intent batteryChangeEvent(int level, int scale) { 765 Intent intent = new Intent(); 766 intent.setAction(Intent.ACTION_BATTERY_CHANGED); 767 intent.putExtra(BatteryManager.EXTRA_LEVEL, level); 768 intent.putExtra(BatteryManager.EXTRA_SCALE, scale); 769 return intent; 770 } 771 createSensorEvent(float lux)772 private SensorEvent createSensorEvent(float lux) { 773 SensorEvent event; 774 try { 775 Constructor<SensorEvent> constr = 776 SensorEvent.class.getDeclaredConstructor(Integer.TYPE); 777 constr.setAccessible(true); 778 event = constr.newInstance(1); 779 } catch (Exception e) { 780 throw new RuntimeException(e); 781 } 782 event.values[0] = lux; 783 event.timestamp = mInjector.mElapsedRealtimeNanos; 784 785 return event; 786 } 787 startTracker(BrightnessTracker tracker)788 private void startTracker(BrightnessTracker tracker) { 789 startTracker(tracker, DEFAULT_INITIAL_BRIGHTNESS, DEFAULT_COLOR_SAMPLING_ENABLED); 790 } 791 startTracker(BrightnessTracker tracker, float initialBrightness, boolean collectColorSamples)792 private void startTracker(BrightnessTracker tracker, float initialBrightness, 793 boolean collectColorSamples) { 794 tracker.start(initialBrightness); 795 tracker.setBrightnessConfiguration(buildBrightnessConfiguration(collectColorSamples)); 796 mInjector.waitForHandler(); 797 } 798 notifyBrightnessChanged(BrightnessTracker tracker, float brightness)799 private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness) { 800 notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/, 801 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/, 802 false /*isDefaultBrightnessConfig*/); 803 } 804 notifyBrightnessChanged(BrightnessTracker tracker, float brightness, boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, boolean isDefaultBrightnessConfig)805 private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness, 806 boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, 807 boolean isDefaultBrightnessConfig) { 808 tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor, 809 isUserSetBrightness, isDefaultBrightnessConfig); 810 mInjector.waitForHandler(); 811 } 812 buildBrightnessConfiguration(boolean collectColorSamples)813 private BrightnessConfiguration buildBrightnessConfiguration(boolean collectColorSamples) { 814 BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( 815 /* lux = */ new float[] {0f, 10f, 100f}, 816 /* nits = */ new float[] {1f, 90f, 100f}); 817 builder.setShouldCollectColorSamples(collectColorSamples); 818 return builder.build(); 819 } 820 821 private static final class Idle implements MessageQueue.IdleHandler { 822 private boolean mIdle; 823 824 @Override queueIdle()825 public boolean queueIdle() { 826 synchronized (this) { 827 mIdle = true; 828 notifyAll(); 829 } 830 return false; 831 } 832 waitForIdle()833 public synchronized void waitForIdle() { 834 while (!mIdle) { 835 try { 836 wait(); 837 } catch (InterruptedException e) { 838 } 839 } 840 } 841 } 842 843 private class TestInjector extends BrightnessTracker.Injector { 844 SensorEventListener mSensorListener; 845 BroadcastReceiver mBroadcastReceiver; 846 DisplayManager.DisplayListener mDisplayListener; 847 Map<String, Integer> mSecureIntSettings = new HashMap<>(); 848 long mCurrentTimeMillis = System.currentTimeMillis(); 849 long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); 850 Handler mHandler; 851 boolean mIdleScheduled; 852 boolean mInteractive = true; 853 int[] mProfiles; 854 ContentObserver mContentObserver; 855 boolean mIsBrightnessModeAutomatic = true; 856 boolean mColorSamplingEnabled = false; 857 DisplayedContentSamplingAttributes mDefaultSamplingAttributes = 858 new DisplayedContentSamplingAttributes(0x37, 0, 0x4); 859 float mFrameRate = 60.0f; 860 int mNoColorSamplingFrames; 861 862 TestInjector(Handler handler)863 public TestInjector(Handler handler) { 864 mHandler = handler; 865 } 866 incrementTime(long timeMillis)867 void incrementTime(long timeMillis) { 868 mCurrentTimeMillis += timeMillis; 869 mElapsedRealtimeNanos += TimeUnit.MILLISECONDS.toNanos(timeMillis); 870 } 871 setBrightnessMode(boolean isBrightnessModeAutomatic)872 void setBrightnessMode(boolean isBrightnessModeAutomatic) { 873 mIsBrightnessModeAutomatic = isBrightnessModeAutomatic; 874 mContentObserver.dispatchChange(false, null); 875 waitForHandler(); 876 } 877 sendScreenChange(boolean screenOn)878 void sendScreenChange(boolean screenOn) { 879 mInteractive = screenOn; 880 Intent intent = new Intent(); 881 intent.setAction(screenOn ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF); 882 mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(), intent); 883 waitForHandler(); 884 } 885 waitForHandler()886 void waitForHandler() { 887 Idle idle = new Idle(); 888 mHandler.getLooper().getQueue().addIdleHandler(idle); 889 mHandler.post(() -> {}); 890 idle.waitForIdle(); 891 } 892 893 @Override registerSensorListener(Context context, SensorEventListener sensorListener, Handler handler)894 public void registerSensorListener(Context context, 895 SensorEventListener sensorListener, Handler handler) { 896 mSensorListener = sensorListener; 897 } 898 899 @Override unregisterSensorListener(Context context, SensorEventListener sensorListener)900 public void unregisterSensorListener(Context context, 901 SensorEventListener sensorListener) { 902 mSensorListener = null; 903 } 904 905 @Override registerBrightnessModeObserver(ContentResolver resolver, ContentObserver settingsObserver)906 public void registerBrightnessModeObserver(ContentResolver resolver, 907 ContentObserver settingsObserver) { 908 mContentObserver = settingsObserver; 909 } 910 911 @Override unregisterBrightnessModeObserver(Context context, ContentObserver settingsObserver)912 public void unregisterBrightnessModeObserver(Context context, 913 ContentObserver settingsObserver) { 914 mContentObserver = null; 915 } 916 917 @Override registerReceiver(Context context, BroadcastReceiver shutdownReceiver, IntentFilter shutdownFilter)918 public void registerReceiver(Context context, 919 BroadcastReceiver shutdownReceiver, IntentFilter shutdownFilter) { 920 mBroadcastReceiver = shutdownReceiver; 921 } 922 923 @Override unregisterReceiver(Context context, BroadcastReceiver broadcastReceiver)924 public void unregisterReceiver(Context context, 925 BroadcastReceiver broadcastReceiver) { 926 assertEquals(mBroadcastReceiver, broadcastReceiver); 927 mBroadcastReceiver = null; 928 } 929 930 @Override getBackgroundHandler()931 public Handler getBackgroundHandler() { 932 return mHandler; 933 } 934 935 @Override isBrightnessModeAutomatic(ContentResolver resolver)936 public boolean isBrightnessModeAutomatic(ContentResolver resolver) { 937 return mIsBrightnessModeAutomatic; 938 } 939 940 @Override getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue, int userId)941 public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue, 942 int userId) { 943 Integer value = mSecureIntSettings.get(setting); 944 if (value == null) { 945 return defaultValue; 946 } else { 947 return value; 948 } 949 } 950 951 @Override getFile(String filename)952 public AtomicFile getFile(String filename) { 953 // Don't have the test write / read from anywhere. 954 return null; 955 } 956 957 @Override currentTimeMillis()958 public long currentTimeMillis() { 959 return mCurrentTimeMillis; 960 } 961 962 @Override elapsedRealtimeNanos()963 public long elapsedRealtimeNanos() { 964 return mElapsedRealtimeNanos; 965 } 966 967 @Override getUserSerialNumber(UserManager userManager, int userId)968 public int getUserSerialNumber(UserManager userManager, int userId) { 969 return userId + 10; 970 } 971 972 @Override getUserId(UserManager userManager, int userSerialNumber)973 public int getUserId(UserManager userManager, int userSerialNumber) { 974 return userSerialNumber - 10; 975 } 976 977 @Override getProfileIds(UserManager userManager, int userId)978 public int[] getProfileIds(UserManager userManager, int userId) { 979 if (mProfiles != null) { 980 return mProfiles; 981 } else { 982 return new int[]{userId}; 983 } 984 } 985 986 @Override getFocusedStack()987 public ActivityManager.StackInfo getFocusedStack() throws RemoteException { 988 ActivityManager.StackInfo focusedStack = new ActivityManager.StackInfo(); 989 focusedStack.userId = 0; 990 focusedStack.topActivity = new ComponentName("a.package", "a.class"); 991 return focusedStack; 992 } 993 994 @Override scheduleIdleJob(Context context)995 public void scheduleIdleJob(Context context) { 996 // Don't actually schedule jobs during unit tests. 997 mIdleScheduled = true; 998 } 999 1000 @Override cancelIdleJob(Context context)1001 public void cancelIdleJob(Context context) { 1002 mIdleScheduled = false; 1003 } 1004 1005 @Override isInteractive(Context context)1006 public boolean isInteractive(Context context) { 1007 return mInteractive; 1008 } 1009 1010 @Override getNightDisplayColorTemperature(Context context)1011 public int getNightDisplayColorTemperature(Context context) { 1012 return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 1013 mDefaultNightModeColorTemperature); 1014 } 1015 1016 @Override isNightDisplayActivated(Context context)1017 public boolean isNightDisplayActivated(Context context) { 1018 return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1019 0) == 1; 1020 } 1021 1022 @Override sampleColor(int noFramesToSample)1023 public DisplayedContentSample sampleColor(int noFramesToSample) { 1024 return new DisplayedContentSample(600L, 1025 null, 1026 null, 1027 new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, 1028 null); 1029 } 1030 1031 @Override getFrameRate(Context context)1032 public float getFrameRate(Context context) { 1033 return mFrameRate; 1034 } 1035 1036 @Override getSamplingAttributes()1037 public DisplayedContentSamplingAttributes getSamplingAttributes() { 1038 return mDefaultSamplingAttributes; 1039 } 1040 1041 @Override enableColorSampling(boolean enable, int noFrames)1042 public boolean enableColorSampling(boolean enable, int noFrames) { 1043 mColorSamplingEnabled = enable; 1044 mNoColorSamplingFrames = noFrames; 1045 return true; 1046 } 1047 1048 @Override registerDisplayListener(Context context, DisplayManager.DisplayListener listener, Handler handler)1049 public void registerDisplayListener(Context context, 1050 DisplayManager.DisplayListener listener, Handler handler) { 1051 mDisplayListener = listener; 1052 } 1053 1054 @Override unRegisterDisplayListener(Context context, DisplayManager.DisplayListener listener)1055 public void unRegisterDisplayListener(Context context, 1056 DisplayManager.DisplayListener listener) { 1057 mDisplayListener = null; 1058 } 1059 } 1060 } 1061