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