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