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