1 /*
2  * Copyright 2018 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.assertNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.hardware.display.AmbientBrightnessDayStats;
26 import android.os.SystemClock;
27 import android.os.UserManager;
28 
29 import androidx.test.InstrumentationRegistry;
30 import androidx.test.filters.SmallTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 import java.io.ByteArrayInputStream;
38 import java.io.ByteArrayOutputStream;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.nio.charset.StandardCharsets;
42 import java.time.LocalDate;
43 import java.util.ArrayList;
44 
45 @SmallTest
46 @RunWith(AndroidJUnit4.class)
47 public class AmbientBrightnessStatsTrackerTest {
48 
49     private TestInjector mTestInjector;
50 
51     @Before
setUp()52     public void setUp() {
53         mTestInjector = new TestInjector();
54     }
55 
56     @Test
testBrightnessStatsTrackerOverSingleDay()57     public void testBrightnessStatsTrackerOverSingleDay() {
58         AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
59         ArrayList<AmbientBrightnessDayStats> userStats;
60         float[] expectedStats;
61         // Test case where no user data
62         userStats = statsTracker.getUserStats(0);
63         assertNull(userStats);
64         // Test after adding some user data
65         statsTracker.start();
66         statsTracker.add(0, 0);
67         mTestInjector.incrementTime(1000);
68         statsTracker.stop();
69         userStats = statsTracker.getUserStats(0);
70         assertEquals(1, userStats.size());
71         assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
72         expectedStats = getEmptyStatsArray();
73         expectedStats[0] = 1;
74         assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
75         // Test after adding some more user data
76         statsTracker.start();
77         statsTracker.add(0, 0.05f);
78         mTestInjector.incrementTime(1000);
79         statsTracker.add(0, 0.2f);
80         mTestInjector.incrementTime(1500);
81         statsTracker.add(0, 50000);
82         mTestInjector.incrementTime(2500);
83         statsTracker.stop();
84         userStats = statsTracker.getUserStats(0);
85         assertEquals(1, userStats.size());
86         assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
87         expectedStats = getEmptyStatsArray();
88         expectedStats[0] = 2;
89         expectedStats[1] = 1.5f;
90         expectedStats[11] = 2.5f;
91         assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
92     }
93 
94     @Test
testBrightnessStatsTrackerOverMultipleDays()95     public void testBrightnessStatsTrackerOverMultipleDays() {
96         AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
97         ArrayList<AmbientBrightnessDayStats> userStats;
98         float[] expectedStats;
99         // Add data for day 1
100         statsTracker.start();
101         statsTracker.add(0, 0.05f);
102         mTestInjector.incrementTime(1000);
103         statsTracker.add(0, 0.2f);
104         mTestInjector.incrementTime(1500);
105         statsTracker.add(0, 1);
106         mTestInjector.incrementTime(2500);
107         statsTracker.stop();
108         // Add data for day 2
109         mTestInjector.incrementDate(1);
110         statsTracker.start();
111         statsTracker.add(0, 0);
112         mTestInjector.incrementTime(3500);
113         statsTracker.add(0, 5);
114         mTestInjector.incrementTime(5000);
115         statsTracker.stop();
116         // Test that the data is tracked as expected
117         userStats = statsTracker.getUserStats(0);
118         assertEquals(2, userStats.size());
119         assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
120         expectedStats = getEmptyStatsArray();
121         expectedStats[0] = 1;
122         expectedStats[1] = 1.5f;
123         expectedStats[3] = 2.5f;
124         assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
125         assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
126         expectedStats = getEmptyStatsArray();
127         expectedStats[0] = 3.5f;
128         expectedStats[4] = 5;
129         assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
130     }
131 
132     @Test
testBrightnessStatsTrackerOverMultipleUsers()133     public void testBrightnessStatsTrackerOverMultipleUsers() {
134         AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
135         ArrayList<AmbientBrightnessDayStats> userStats;
136         float[] expectedStats;
137         // Add data for user 1
138         statsTracker.start();
139         statsTracker.add(0, 0.05f);
140         mTestInjector.incrementTime(1000);
141         statsTracker.add(0, 0.2f);
142         mTestInjector.incrementTime(1500);
143         statsTracker.add(0, 1);
144         mTestInjector.incrementTime(2500);
145         statsTracker.stop();
146         // Add data for user 2
147         mTestInjector.incrementDate(1);
148         statsTracker.start();
149         statsTracker.add(1, 0);
150         mTestInjector.incrementTime(3500);
151         statsTracker.add(1, 5);
152         mTestInjector.incrementTime(5000);
153         statsTracker.stop();
154         // Test that the data is tracked as expected
155         userStats = statsTracker.getUserStats(0);
156         assertEquals(1, userStats.size());
157         assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
158         expectedStats = getEmptyStatsArray();
159         expectedStats[0] = 1;
160         expectedStats[1] = 1.5f;
161         expectedStats[3] = 2.5f;
162         assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
163         userStats = statsTracker.getUserStats(1);
164         assertEquals(1, userStats.size());
165         assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
166         expectedStats = getEmptyStatsArray();
167         expectedStats[0] = 3.5f;
168         expectedStats[4] = 5;
169         assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
170     }
171 
172     @Test
testBrightnessStatsTrackerOverMaxDays()173     public void testBrightnessStatsTrackerOverMaxDays() {
174         AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
175         ArrayList<AmbientBrightnessDayStats> userStats;
176         // Add 10 extra days of data over the buffer limit
177         for (int i = 0; i < AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK + 10; i++) {
178             mTestInjector.incrementDate(1);
179             statsTracker.start();
180             statsTracker.add(0, 10);
181             mTestInjector.incrementTime(1000);
182             statsTracker.add(0, 20);
183             mTestInjector.incrementTime(1000);
184             statsTracker.stop();
185         }
186         // Assert that we are only tracking last "MAX_DAYS_TO_TRACK"
187         userStats = statsTracker.getUserStats(0);
188         assertEquals(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK, userStats.size());
189         LocalDate runningDate = mTestInjector.getLocalDate();
190         for (int i = AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1; i >= 0; i--) {
191             assertEquals(runningDate, userStats.get(i).getLocalDate());
192             runningDate = runningDate.minusDays(1);
193         }
194     }
195 
196     @Test
testReadAmbientBrightnessStats()197     public void testReadAmbientBrightnessStats() throws IOException {
198         AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
199         LocalDate date = mTestInjector.getLocalDate();
200         ArrayList<AmbientBrightnessDayStats> userStats;
201         String statsFile =
202                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
203                         + "<ambient-brightness-stats>\r\n"
204                         // Old stats that shouldn't be read
205                         + "<ambient-brightness-day-stats user=\"10\" local-date=\""
206                         + date.minusDays(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK)
207                         + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
208                         + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
209                         + "0.0,0.0,0.0\" />\r\n"
210                         // Valid stats that should get read
211                         + "<ambient-brightness-day-stats user=\"10\" local-date=\""
212                         + date.minusDays(1)
213                         + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
214                         + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
215                         + "0.0,0.0,0.0\" />\r\n"
216                         // Valid stats that should get read
217                         + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
218                         + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
219                         + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
220                         + "0.0\" />\r\n"
221                         + "</ambient-brightness-stats>";
222         statsTracker.readStats(getInputStream(statsFile));
223         userStats = statsTracker.getUserStats(0);
224         assertEquals(2, userStats.size());
225         assertEquals(new AmbientBrightnessDayStats(date.minusDays(1),
226                 new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
227                 new float[]{1.088f, 0, 0.726f, 0, 25.868f, 0, 0, 0, 0, 0}), userStats.get(0));
228         assertEquals(new AmbientBrightnessDayStats(date,
229                 new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
230                 new float[]{0, 0, 0, 0, 4.482f, 0, 0, 0, 0, 0}), userStats.get(1));
231     }
232 
233     @Test
testFailedReadAmbientBrightnessStatsWithException()234     public void testFailedReadAmbientBrightnessStatsWithException() {
235         AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
236         LocalDate date = mTestInjector.getLocalDate();
237         String statsFile;
238         // Test with parse error
239         statsFile =
240                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
241                         + "<ambient-brightness-stats>\r\n"
242                         // Incorrect since bucket boundaries not parsable
243                         + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
244                         + "\" bucket-boundaries=\"asdf,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
245                         + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
246                         + "0.0,0.0,0.0\" />\r\n"
247                         + "</ambient-brightness-stats>";
248         try {
249             statsTracker.readStats(getInputStream(statsFile));
250         } catch (IOException e) {
251             // Expected
252         }
253         assertNull(statsTracker.getUserStats(0));
254         // Test with incorrect data (bucket boundaries length not equal to stats length)
255         statsFile =
256                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
257                         + "<ambient-brightness-stats>\r\n"
258                         // Correct data
259                         + "<ambient-brightness-day-stats user=\"10\" local-date=\""
260                         + date.minusDays(1)
261                         + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
262                         + "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
263                         + "0.0\" />\r\n"
264                         // Incorrect data
265                         + "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
266                         + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,1000.0,"
267                         + "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
268                         + "0.0,0.0,0.0\" />\r\n"
269                         + "</ambient-brightness-stats>";
270         try {
271             statsTracker.readStats(getInputStream(statsFile));
272         } catch (Exception e) {
273             // Expected
274         }
275         assertNull(statsTracker.getUserStats(0));
276         // Test with missing attribute
277         statsFile =
278                 "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
279                         + "<ambient-brightness-stats>\r\n"
280                         + "<ambientBrightnessDayStats user=\"10\" local-date=\"" + date
281                         + "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
282                         + "3000.0,10000.0\" />\r\n"
283                         + "</ambient-brightness-stats>";
284         try {
285             statsTracker.readStats(getInputStream(statsFile));
286         } catch (Exception e) {
287             // Expected
288         }
289         assertNull(statsTracker.getUserStats(0));
290     }
291 
292     @Test
testWriteThenReadAmbientBrightnessStats()293     public void testWriteThenReadAmbientBrightnessStats() throws IOException {
294         AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
295         ArrayList<AmbientBrightnessDayStats> userStats;
296         float[] expectedStats;
297         // Generate some placeholder data
298         // Data: very old which should not be read
299         statsTracker.start();
300         statsTracker.add(0, 0.05f);
301         mTestInjector.incrementTime(1000);
302         statsTracker.add(0, 0.2f);
303         mTestInjector.incrementTime(1500);
304         statsTracker.add(0, 1);
305         mTestInjector.incrementTime(2500);
306         statsTracker.stop();
307         // Data: day 1 user 1
308         mTestInjector.incrementDate(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1);
309         statsTracker.start();
310         statsTracker.add(0, 0.05f);
311         mTestInjector.incrementTime(1000);
312         statsTracker.add(0, 0.2f);
313         mTestInjector.incrementTime(1500);
314         statsTracker.add(0, 1);
315         mTestInjector.incrementTime(2500);
316         statsTracker.stop();
317         // Data: day 1 user 2
318         statsTracker.start();
319         statsTracker.add(1, 0);
320         mTestInjector.incrementTime(3500);
321         statsTracker.add(1, 5);
322         mTestInjector.incrementTime(5000);
323         statsTracker.stop();
324         // Data: day 2 user 1
325         mTestInjector.incrementDate(1);
326         statsTracker.start();
327         statsTracker.add(0, 0);
328         mTestInjector.incrementTime(3500);
329         statsTracker.add(0, 50000);
330         mTestInjector.incrementTime(5000);
331         statsTracker.stop();
332         // Write them
333         ByteArrayOutputStream baos = new ByteArrayOutputStream();
334         statsTracker.writeStats(baos);
335         baos.flush();
336         // Read them back and assert that it's the same
337         ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
338         AmbientBrightnessStatsTracker newStatsTracker = getTestStatsTracker();
339         newStatsTracker.readStats(input);
340         userStats = newStatsTracker.getUserStats(0);
341         assertEquals(2, userStats.size());
342         // Check day 1 user 1
343         assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
344         expectedStats = getEmptyStatsArray();
345         expectedStats[0] = 1;
346         expectedStats[1] = 1.5f;
347         expectedStats[3] = 2.5f;
348         assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
349         // Check day 2 user 1
350         assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
351         expectedStats = getEmptyStatsArray();
352         expectedStats[0] = 3.5f;
353         expectedStats[11] = 5;
354         assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
355         userStats = newStatsTracker.getUserStats(1);
356         assertEquals(1, userStats.size());
357         // Check day 1 user 2
358         assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
359         expectedStats = getEmptyStatsArray();
360         expectedStats[0] = 3.5f;
361         expectedStats[4] = 5;
362         assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
363     }
364 
365     @Test
testTimer()366     public void testTimer() {
367         AmbientBrightnessStatsTracker.Timer timer = new AmbientBrightnessStatsTracker.Timer(
368                 () -> mTestInjector.elapsedRealtimeMillis());
369         assertEquals(0, timer.totalDurationSec(), 0);
370         mTestInjector.incrementTime(1000);
371         assertEquals(0, timer.totalDurationSec(), 0);
372         assertFalse(timer.isRunning());
373         // Start timer
374         timer.start();
375         assertTrue(timer.isRunning());
376         assertEquals(0, timer.totalDurationSec(), 0);
377         mTestInjector.incrementTime(1000);
378         assertTrue(timer.isRunning());
379         assertEquals(1, timer.totalDurationSec(), 0);
380         // Reset timer
381         timer.reset();
382         assertEquals(0, timer.totalDurationSec(), 0);
383         assertFalse(timer.isRunning());
384         // Start again
385         timer.start();
386         assertTrue(timer.isRunning());
387         assertEquals(0, timer.totalDurationSec(), 0);
388         mTestInjector.incrementTime(2000);
389         assertTrue(timer.isRunning());
390         assertEquals(2, timer.totalDurationSec(), 0);
391         // Reset again
392         timer.reset();
393         assertEquals(0, timer.totalDurationSec(), 0);
394         assertFalse(timer.isRunning());
395     }
396 
397     private class TestInjector extends AmbientBrightnessStatsTracker.Injector {
398 
399         private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
400         private LocalDate mLocalDate = LocalDate.now();
401 
incrementTime(long timeMillis)402         public void incrementTime(long timeMillis) {
403             mElapsedRealtimeMillis += timeMillis;
404         }
405 
incrementDate(int numDays)406         public void incrementDate(int numDays) {
407             mLocalDate = mLocalDate.plusDays(numDays);
408         }
409 
410         @Override
elapsedRealtimeMillis()411         public long elapsedRealtimeMillis() {
412             return mElapsedRealtimeMillis;
413         }
414 
415         @Override
getUserSerialNumber(UserManager userManager, int userId)416         public int getUserSerialNumber(UserManager userManager, int userId) {
417             return userId + 10;
418         }
419 
420         @Override
getUserId(UserManager userManager, int userSerialNumber)421         public int getUserId(UserManager userManager, int userSerialNumber) {
422             return userSerialNumber - 10;
423         }
424 
425         @Override
getLocalDate()426         public LocalDate getLocalDate() {
427             return mLocalDate;
428         }
429     }
430 
getTestStatsTracker()431     private AmbientBrightnessStatsTracker getTestStatsTracker() {
432         return new AmbientBrightnessStatsTracker(
433                 InstrumentationRegistry.getContext().getSystemService(UserManager.class),
434                 mTestInjector);
435     }
436 
getEmptyStatsArray()437     private float[] getEmptyStatsArray() {
438         return new float[AmbientBrightnessStatsTracker.BUCKET_BOUNDARIES_FOR_NEW_STATS.length];
439     }
440 
getInputStream(String data)441     private InputStream getInputStream(String data) {
442         return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
443     }
444 }
445