1 /*
2  * Copyright (C) 2011 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 android.net;
18 
19 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
20 import static android.net.NetworkStats.IFACE_ALL;
21 import static android.net.NetworkStats.METERED_NO;
22 import static android.net.NetworkStats.ROAMING_NO;
23 import static android.net.NetworkStats.SET_DEFAULT;
24 import static android.net.NetworkStats.TAG_NONE;
25 import static android.net.NetworkStats.UID_ALL;
26 import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong;
27 import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong;
28 import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
29 import static android.net.NetworkStatsHistory.FIELD_ALL;
30 import static android.net.NetworkStatsHistory.FIELD_OPERATIONS;
31 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
32 import static android.net.NetworkStatsHistory.FIELD_RX_PACKETS;
33 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
34 import static android.net.TrafficStats.GB_IN_BYTES;
35 import static android.net.TrafficStats.MB_IN_BYTES;
36 import static android.text.format.DateUtils.DAY_IN_MILLIS;
37 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
38 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
39 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
40 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
41 import static android.text.format.DateUtils.YEAR_IN_MILLIS;
42 
43 import static org.junit.Assert.assertEquals;
44 import static org.junit.Assert.assertFalse;
45 import static org.junit.Assert.assertTrue;
46 
47 import android.content.Context;
48 import android.os.Build;
49 import android.util.Log;
50 
51 import androidx.test.InstrumentationRegistry;
52 import androidx.test.filters.SmallTest;
53 
54 import com.android.frameworks.tests.net.R;
55 import com.android.testutils.DevSdkIgnoreRule;
56 import com.android.testutils.DevSdkIgnoreRunner;
57 import com.android.testutils.SkipPresubmit;
58 
59 import org.junit.After;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 
63 import java.io.ByteArrayInputStream;
64 import java.io.ByteArrayOutputStream;
65 import java.io.DataInputStream;
66 import java.io.DataOutputStream;
67 import java.util.Random;
68 
69 @RunWith(DevSdkIgnoreRunner.class)
70 @SmallTest
71 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
72 public class NetworkStatsHistoryTest {
73     private static final String TAG = "NetworkStatsHistoryTest";
74 
75     private static final long TEST_START = 1194220800000L;
76 
77     private NetworkStatsHistory stats;
78 
79     @After
tearDown()80     public void tearDown() throws Exception {
81         if (stats != null) {
82             assertConsistent(stats);
83         }
84     }
85 
86     @Test
testReadOriginalVersion()87     public void testReadOriginalVersion() throws Exception {
88         final Context context = InstrumentationRegistry.getContext();
89         final DataInputStream in =
90                 new DataInputStream(context.getResources().openRawResource(R.raw.history_v1));
91 
92         NetworkStatsHistory.Entry entry = null;
93         try {
94             final NetworkStatsHistory history = new NetworkStatsHistory(in);
95             assertEquals(15 * SECOND_IN_MILLIS, history.getBucketDuration());
96 
97             entry = history.getValues(0, entry);
98             assertEquals(29143L, entry.rxBytes);
99             assertEquals(6223L, entry.txBytes);
100 
101             entry = history.getValues(history.size() - 1, entry);
102             assertEquals(1476L, entry.rxBytes);
103             assertEquals(838L, entry.txBytes);
104 
105             entry = history.getValues(Long.MIN_VALUE, Long.MAX_VALUE, entry);
106             assertEquals(332401L, entry.rxBytes);
107             assertEquals(64314L, entry.txBytes);
108 
109         } finally {
110             in.close();
111         }
112     }
113 
114     @Test
testRecordSingleBucket()115     public void testRecordSingleBucket() throws Exception {
116         final long BUCKET_SIZE = HOUR_IN_MILLIS;
117         stats = new NetworkStatsHistory(BUCKET_SIZE);
118 
119         // record data into narrow window to get single bucket
120         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS,
121                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
122                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
123 
124         assertEquals(1, stats.size());
125         assertValues(stats, 0, SECOND_IN_MILLIS, 1024L, 10L, 2048L, 20L, 2L);
126     }
127 
128     @Test
testRecordEqualBuckets()129     public void testRecordEqualBuckets() throws Exception {
130         final long bucketDuration = HOUR_IN_MILLIS;
131         stats = new NetworkStatsHistory(bucketDuration);
132 
133         // split equally across two buckets
134         final long recordStart = TEST_START + (bucketDuration / 2);
135         stats.recordData(recordStart, recordStart + bucketDuration,
136                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
137                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 128L, 2L, 2L));
138 
139         assertEquals(2, stats.size());
140         assertValues(stats, 0, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L);
141         assertValues(stats, 1, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L);
142     }
143 
144     @Test
testRecordTouchingBuckets()145     public void testRecordTouchingBuckets() throws Exception {
146         final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS;
147         stats = new NetworkStatsHistory(BUCKET_SIZE);
148 
149         // split almost completely into middle bucket, but with a few minutes
150         // overlap into neighboring buckets. total record is 20 minutes.
151         final long recordStart = (TEST_START + BUCKET_SIZE) - MINUTE_IN_MILLIS;
152         final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4);
153         stats.recordData(recordStart, recordEnd,
154                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
155                 ROAMING_NO, DEFAULT_NETWORK_NO, 1000L, 2000L, 5000L, 10000L, 100L));
156 
157         assertEquals(3, stats.size());
158         // first bucket should have (1/20 of value)
159         assertValues(stats, 0, MINUTE_IN_MILLIS, 50L, 100L, 250L, 500L, 5L);
160         // second bucket should have (15/20 of value)
161         assertValues(stats, 1, 15 * MINUTE_IN_MILLIS, 750L, 1500L, 3750L, 7500L, 75L);
162         // final bucket should have (4/20 of value)
163         assertValues(stats, 2, 4 * MINUTE_IN_MILLIS, 200L, 400L, 1000L, 2000L, 20L);
164     }
165 
166     @Test
testRecordGapBuckets()167     public void testRecordGapBuckets() throws Exception {
168         final long BUCKET_SIZE = HOUR_IN_MILLIS;
169         stats = new NetworkStatsHistory(BUCKET_SIZE);
170 
171         // record some data today and next week with large gap
172         final long firstStart = TEST_START;
173         final long lastStart = TEST_START + WEEK_IN_MILLIS;
174         stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS,
175                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
176                 ROAMING_NO, DEFAULT_NETWORK_NO, 128L, 2L, 256L, 4L, 1L));
177         stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS,
178                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
179                 ROAMING_NO, DEFAULT_NETWORK_NO, 64L, 1L, 512L, 8L, 2L));
180 
181         // we should have two buckets, far apart from each other
182         assertEquals(2, stats.size());
183         assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L);
184         assertValues(stats, 1, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L);
185 
186         // now record something in middle, spread across two buckets
187         final long middleStart = TEST_START + DAY_IN_MILLIS;
188         final long middleEnd = middleStart + (HOUR_IN_MILLIS * 2);
189         stats.recordData(middleStart, middleEnd,
190                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
191                 ROAMING_NO, DEFAULT_NETWORK_NO, 2048L, 4L, 2048L, 4L, 2L));
192 
193         // now should have four buckets, with new record in middle two buckets
194         assertEquals(4, stats.size());
195         assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L);
196         assertValues(stats, 1, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L);
197         assertValues(stats, 2, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L);
198         assertValues(stats, 3, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L);
199     }
200 
201     @Test
testRecordOverlapBuckets()202     public void testRecordOverlapBuckets() throws Exception {
203         final long BUCKET_SIZE = HOUR_IN_MILLIS;
204         stats = new NetworkStatsHistory(BUCKET_SIZE);
205 
206         // record some data in one bucket, and another overlapping buckets
207         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS,
208                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
209                 ROAMING_NO, DEFAULT_NETWORK_NO, 256L, 2L, 256L, 2L, 1L));
210         final long midStart = TEST_START + (HOUR_IN_MILLIS / 2);
211         stats.recordData(midStart, midStart + HOUR_IN_MILLIS,
212                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
213                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 1024L, 10L, 10L));
214 
215         // should have two buckets, with some data mixed together
216         assertEquals(2, stats.size());
217         assertValues(stats, 0, SECOND_IN_MILLIS + (HOUR_IN_MILLIS / 2), 768L, 7L, 768L, 7L, 6L);
218         assertValues(stats, 1, (HOUR_IN_MILLIS / 2), 512L, 5L, 512L, 5L, 5L);
219     }
220 
221     @Test
testRecordEntireGapIdentical()222     public void testRecordEntireGapIdentical() throws Exception {
223         // first, create two separate histories far apart
224         final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
225         stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L);
226 
227         final long TEST_START_2 = TEST_START + DAY_IN_MILLIS;
228         final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS);
229         stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L);
230 
231         // combine together with identical bucket size
232         stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
233         stats.recordEntireHistory(stats1);
234         stats.recordEntireHistory(stats2);
235 
236         // first verify that totals match up
237         assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 3000L, 1500L);
238 
239         // now inspect internal buckets
240         assertValues(stats, 0, 1000L, 500L);
241         assertValues(stats, 1, 1000L, 500L);
242         assertValues(stats, 2, 500L, 250L);
243         assertValues(stats, 3, 500L, 250L);
244     }
245 
246     @Test
testRecordEntireOverlapVaryingBuckets()247     public void testRecordEntireOverlapVaryingBuckets() throws Exception {
248         // create history just over hour bucket boundary
249         final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
250         stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L);
251 
252         final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS;
253         final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS);
254         stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L);
255 
256         // combine together with minute bucket size
257         stats = new NetworkStatsHistory(MINUTE_IN_MILLIS);
258         stats.recordEntireHistory(stats1);
259         stats.recordEntireHistory(stats2);
260 
261         // first verify that totals match up
262         assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
263 
264         // now inspect internal buckets
265         assertValues(stats, 0, 10L, 10L);
266         assertValues(stats, 1, 20L, 20L);
267         assertValues(stats, 2, 20L, 20L);
268         assertValues(stats, 3, 20L, 20L);
269         assertValues(stats, 4, 20L, 20L);
270         assertValues(stats, 5, 20L, 20L);
271         assertValues(stats, 6, 10L, 10L);
272 
273         // now combine using 15min buckets
274         stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
275         stats.recordEntireHistory(stats1);
276         stats.recordEntireHistory(stats2);
277 
278         // first verify that totals match up
279         assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
280 
281         // and inspect buckets
282         assertValues(stats, 0, 200L, 200L);
283         assertValues(stats, 1, 150L, 150L);
284         assertValues(stats, 2, 150L, 150L);
285         assertValues(stats, 3, 150L, 150L);
286     }
287 
288     @Test
testRemoveStartingBefore()289     public void testRemoveStartingBefore() throws Exception {
290         stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
291 
292         // record some data across 24 buckets
293         stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
294         assertEquals(24, stats.size());
295 
296         // try removing invalid data; should be no change
297         stats.removeBucketsStartingBefore(0 - DAY_IN_MILLIS);
298         assertEquals(24, stats.size());
299 
300         // try removing far before buckets; should be no change
301         stats.removeBucketsStartingBefore(TEST_START - YEAR_IN_MILLIS);
302         assertEquals(24, stats.size());
303 
304         // try removing just moments into first bucket; should be no change
305         // since that bucket doesn't contain data starts before the cutoff
306         stats.removeBucketsStartingBefore(TEST_START);
307         assertEquals(24, stats.size());
308 
309         // try removing single bucket
310         stats.removeBucketsStartingBefore(TEST_START + HOUR_IN_MILLIS);
311         assertEquals(23, stats.size());
312 
313         // try removing multiple buckets
314         stats.removeBucketsStartingBefore(TEST_START + (4 * HOUR_IN_MILLIS));
315         assertEquals(20, stats.size());
316 
317         // try removing all buckets
318         stats.removeBucketsStartingBefore(TEST_START + YEAR_IN_MILLIS);
319         assertEquals(0, stats.size());
320     }
321 
322     @Test
testTotalData()323     public void testTotalData() throws Exception {
324         final long BUCKET_SIZE = HOUR_IN_MILLIS;
325         stats = new NetworkStatsHistory(BUCKET_SIZE);
326 
327         // record uniform data across day
328         stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L);
329 
330         // verify that total outside range is 0
331         assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, 0L, 0L);
332 
333         // verify total in first hour
334         assertValues(stats, TEST_START, TEST_START + HOUR_IN_MILLIS, 100L, 200L);
335 
336         // verify total across 1.5 hours
337         assertValues(stats, TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), 150L, 300L);
338 
339         // verify total beyond end
340         assertValues(stats, TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, 100L, 200L);
341 
342         // verify everything total
343         assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 2400L, 4800L);
344 
345     }
346 
347     @SkipPresubmit(reason = "Flaky: b/302325928; add to presubmit after fixing")
348     @Test
testFuzzing()349     public void testFuzzing() throws Exception {
350         try {
351             // fuzzing with random events, looking for crashes
352             final NetworkStats.Entry entry = new NetworkStats.Entry();
353             final Random r = new Random();
354             for (int i = 0; i < 500; i++) {
355                 stats = new NetworkStatsHistory(r.nextLong());
356                 for (int j = 0; j < 10000; j++) {
357                     if (r.nextBoolean()) {
358                         // add range
359                         final long start = r.nextLong();
360                         final long end = start + r.nextInt();
361                         entry.rxBytes = nextPositiveLong(r);
362                         entry.rxPackets = nextPositiveLong(r);
363                         entry.txBytes = nextPositiveLong(r);
364                         entry.txPackets = nextPositiveLong(r);
365                         entry.operations = nextPositiveLong(r);
366                         stats.recordData(start, end, entry);
367                     } else {
368                         // trim something
369                         stats.removeBucketsStartingBefore(r.nextLong());
370                     }
371                 }
372                 assertConsistent(stats);
373             }
374         } catch (Throwable e) {
375             Log.e(TAG, String.valueOf(stats));
376             throw new RuntimeException(e);
377         }
378     }
379 
nextPositiveLong(Random r)380     private static long nextPositiveLong(Random r) {
381         final long value = r.nextLong();
382         return value < 0 ? -value : value;
383     }
384 
385     @Test
testIgnoreFields()386     public void testIgnoreFields() throws Exception {
387         final NetworkStatsHistory history = new NetworkStatsHistory(
388                 MINUTE_IN_MILLIS, 0, FIELD_RX_BYTES | FIELD_TX_BYTES);
389 
390         history.recordData(0, MINUTE_IN_MILLIS,
391                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
392                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
393         history.recordData(0, 2 * MINUTE_IN_MILLIS,
394                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
395                 ROAMING_NO, DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 2L));
396 
397         assertFullValues(history, UNKNOWN, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN);
398     }
399 
400     @Test
testIgnoreFieldsRecordIn()401     public void testIgnoreFieldsRecordIn() throws Exception {
402         final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL);
403         final NetworkStatsHistory partial = new NetworkStatsHistory(
404                 MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS);
405 
406         full.recordData(0, MINUTE_IN_MILLIS,
407                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
408                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
409         partial.recordEntireHistory(full);
410 
411         assertFullValues(partial, UNKNOWN, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L);
412     }
413 
414     @Test
testIgnoreFieldsRecordOut()415     public void testIgnoreFieldsRecordOut() throws Exception {
416         final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL);
417         final NetworkStatsHistory partial = new NetworkStatsHistory(
418                 MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS);
419 
420         partial.recordData(0, MINUTE_IN_MILLIS,
421                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
422                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
423         full.recordEntireHistory(partial);
424 
425         assertFullValues(full, MINUTE_IN_MILLIS, 0L, 10L, 0L, 0L, 4L);
426     }
427 
428     @Test
testSerialize()429     public void testSerialize() throws Exception {
430         final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL);
431         before.recordData(0, 4 * MINUTE_IN_MILLIS,
432                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
433                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 4L));
434         before.recordData(DAY_IN_MILLIS, DAY_IN_MILLIS + MINUTE_IN_MILLIS,
435                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
436                 ROAMING_NO, DEFAULT_NETWORK_NO, 10L, 20L, 30L, 40L, 50L));
437 
438         final ByteArrayOutputStream out = new ByteArrayOutputStream();
439         before.writeToStream(new DataOutputStream(out));
440         out.close();
441 
442         final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
443         final NetworkStatsHistory after = new NetworkStatsHistory(new DataInputStream(in));
444 
445         // must have identical totals before and after
446         assertFullValues(before, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L);
447         assertFullValues(after, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L);
448     }
449 
450     @Test
testVarLong()451     public void testVarLong() throws Exception {
452         assertEquals(0L, performVarLong(0L));
453         assertEquals(-1L, performVarLong(-1L));
454         assertEquals(1024L, performVarLong(1024L));
455         assertEquals(-1024L, performVarLong(-1024L));
456         assertEquals(40 * MB_IN_BYTES, performVarLong(40 * MB_IN_BYTES));
457         assertEquals(512 * GB_IN_BYTES, performVarLong(512 * GB_IN_BYTES));
458         assertEquals(Long.MIN_VALUE, performVarLong(Long.MIN_VALUE));
459         assertEquals(Long.MAX_VALUE, performVarLong(Long.MAX_VALUE));
460         assertEquals(Long.MIN_VALUE + 40, performVarLong(Long.MIN_VALUE + 40));
461         assertEquals(Long.MAX_VALUE - 40, performVarLong(Long.MAX_VALUE - 40));
462     }
463 
464     @Test
testIndexBeforeAfter()465     public void testIndexBeforeAfter() throws Exception {
466         final long BUCKET_SIZE = HOUR_IN_MILLIS;
467         stats = new NetworkStatsHistory(BUCKET_SIZE);
468 
469         final long FIRST_START = TEST_START;
470         final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS);
471         final long SECOND_START = TEST_START + WEEK_IN_MILLIS;
472         final long SECOND_END = SECOND_START + HOUR_IN_MILLIS;
473         final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS);
474         final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS);
475 
476         stats.recordData(FIRST_START, FIRST_END,
477                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
478                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
479         stats.recordData(SECOND_START, SECOND_END,
480                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
481                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
482         stats.recordData(THIRD_START, THIRD_END,
483                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
484                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
485 
486         // should have buckets: 2+1+2
487         assertEquals(5, stats.size());
488 
489         assertIndexBeforeAfter(stats, 0, 0, Long.MIN_VALUE);
490         assertIndexBeforeAfter(stats, 0, 1, FIRST_START);
491         assertIndexBeforeAfter(stats, 0, 1, FIRST_START + MINUTE_IN_MILLIS);
492         assertIndexBeforeAfter(stats, 0, 2, FIRST_START + HOUR_IN_MILLIS);
493         assertIndexBeforeAfter(stats, 1, 2, FIRST_START + HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
494         assertIndexBeforeAfter(stats, 1, 2, FIRST_END - MINUTE_IN_MILLIS);
495         assertIndexBeforeAfter(stats, 1, 2, FIRST_END);
496         assertIndexBeforeAfter(stats, 1, 2, FIRST_END + MINUTE_IN_MILLIS);
497         assertIndexBeforeAfter(stats, 1, 2, SECOND_START - MINUTE_IN_MILLIS);
498         assertIndexBeforeAfter(stats, 1, 3, SECOND_START);
499         assertIndexBeforeAfter(stats, 2, 3, SECOND_END);
500         assertIndexBeforeAfter(stats, 2, 3, SECOND_END + MINUTE_IN_MILLIS);
501         assertIndexBeforeAfter(stats, 2, 3, THIRD_START - MINUTE_IN_MILLIS);
502         assertIndexBeforeAfter(stats, 2, 4, THIRD_START);
503         assertIndexBeforeAfter(stats, 3, 4, THIRD_START + MINUTE_IN_MILLIS);
504         assertIndexBeforeAfter(stats, 3, 4, THIRD_START + HOUR_IN_MILLIS);
505         assertIndexBeforeAfter(stats, 4, 4, THIRD_END);
506         assertIndexBeforeAfter(stats, 4, 4, THIRD_END + MINUTE_IN_MILLIS);
507         assertIndexBeforeAfter(stats, 4, 4, Long.MAX_VALUE);
508     }
509 
510     @Test
testIntersects()511     public void testIntersects() throws Exception {
512         final long BUCKET_SIZE = HOUR_IN_MILLIS;
513         stats = new NetworkStatsHistory(BUCKET_SIZE);
514 
515         final long FIRST_START = TEST_START;
516         final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS);
517         final long SECOND_START = TEST_START + WEEK_IN_MILLIS;
518         final long SECOND_END = SECOND_START + HOUR_IN_MILLIS;
519         final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS);
520         final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS);
521 
522         stats.recordData(FIRST_START, FIRST_END,
523                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
524                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
525         stats.recordData(SECOND_START, SECOND_END,
526                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
527                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
528         stats.recordData(THIRD_START, THIRD_END,
529                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
530                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
531 
532         assertFalse(stats.intersects(10, 20));
533         assertFalse(stats.intersects(TEST_START + YEAR_IN_MILLIS, TEST_START + YEAR_IN_MILLIS + 1));
534         assertFalse(stats.intersects(Long.MAX_VALUE, Long.MIN_VALUE));
535 
536         assertTrue(stats.intersects(Long.MIN_VALUE, Long.MAX_VALUE));
537         assertTrue(stats.intersects(10, TEST_START + YEAR_IN_MILLIS));
538         assertTrue(stats.intersects(TEST_START, TEST_START));
539         assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, TEST_START + DAY_IN_MILLIS + 1));
540         assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, Long.MAX_VALUE));
541         assertTrue(stats.intersects(TEST_START + 1, Long.MAX_VALUE));
542 
543         assertFalse(stats.intersects(Long.MIN_VALUE, TEST_START - 1));
544         assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START));
545         assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START + 1));
546     }
547 
548     @Test
testSetValues()549     public void testSetValues() throws Exception {
550         stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
551         stats.recordData(TEST_START, TEST_START + 1,
552                 new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO,
553                 ROAMING_NO, DEFAULT_NETWORK_NO, 1024L, 10L, 2048L, 20L, 2L));
554 
555         assertEquals(1024L + 2048L, stats.getTotalBytes());
556 
557         final NetworkStatsHistory.Entry entry = stats.getValues(0, null);
558         entry.rxBytes /= 2;
559         entry.txBytes *= 2;
560         stats.setValues(0, entry);
561 
562         assertEquals(512L + 4096L, stats.getTotalBytes());
563     }
564 
assertIndexBeforeAfter( NetworkStatsHistory stats, int before, int after, long time)565     private static void assertIndexBeforeAfter(
566             NetworkStatsHistory stats, int before, int after, long time) {
567         assertEquals("unexpected before", before, stats.getIndexBefore(time));
568         assertEquals("unexpected after", after, stats.getIndexAfter(time));
569     }
570 
performVarLong(long before)571     private static long performVarLong(long before) throws Exception {
572         final ByteArrayOutputStream out = new ByteArrayOutputStream();
573         writeVarLong(new DataOutputStream(out), before);
574 
575         final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
576         return readVarLong(new DataInputStream(in));
577     }
578 
assertConsistent(NetworkStatsHistory stats)579     private static void assertConsistent(NetworkStatsHistory stats) {
580         // verify timestamps are monotonic
581         long lastStart = Long.MIN_VALUE;
582         NetworkStatsHistory.Entry entry = null;
583         for (int i = 0; i < stats.size(); i++) {
584             entry = stats.getValues(i, entry);
585             assertTrue(lastStart < entry.bucketStart);
586             lastStart = entry.bucketStart;
587         }
588     }
589 
590     private static void assertValues(
591             NetworkStatsHistory stats, int index, long rxBytes, long txBytes) {
592         final NetworkStatsHistory.Entry entry = stats.getValues(index, null);
593         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
594         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
595     }
596 
597     private static void assertValues(
598             NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) {
599         final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
600         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
601         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
602     }
603 
604     private static void assertValues(NetworkStatsHistory stats, int index, long activeTime,
605             long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
606         final NetworkStatsHistory.Entry entry = stats.getValues(index, null);
607         assertEquals("unexpected activeTime", activeTime, entry.activeTime);
608         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
609         assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
610         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
611         assertEquals("unexpected txPackets", txPackets, entry.txPackets);
612         assertEquals("unexpected operations", operations, entry.operations);
613     }
614 
615     private static void assertFullValues(NetworkStatsHistory stats, long activeTime, long rxBytes,
616             long rxPackets, long txBytes, long txPackets, long operations) {
617         assertValues(stats, Long.MIN_VALUE, Long.MAX_VALUE, activeTime, rxBytes, rxPackets, txBytes,
618                 txPackets, operations);
619     }
620 
621     private static void assertValues(NetworkStatsHistory stats, long start, long end,
622             long activeTime, long rxBytes, long rxPackets, long txBytes, long txPackets,
623             long operations) {
624         final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
625         assertEquals("unexpected activeTime", activeTime, entry.activeTime);
626         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
627         assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
628         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
629         assertEquals("unexpected txPackets", txPackets, entry.txPackets);
630         assertEquals("unexpected operations", operations, entry.operations);
631     }
632 }
633