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