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