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