1 /* 2 * Copyright (C) 2021 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.ConnectivityManager.TYPE_MOBILE; 20 import static android.net.NetworkIdentity.OEM_NONE; 21 import static android.net.NetworkStats.SET_ALL; 22 import static android.net.NetworkStats.SET_DEFAULT; 23 import static android.net.NetworkStats.TAG_NONE; 24 import static android.net.NetworkStats.UID_ALL; 25 import static android.net.NetworkStatsHistory.FIELD_ALL; 26 import static android.net.NetworkTemplate.buildTemplateMobileAll; 27 import static android.os.Process.myUid; 28 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 29 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 30 31 import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational; 32 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2; 33 import static com.android.testutils.MiscAsserts.assertThrows; 34 35 import static org.junit.Assert.assertArrayEquals; 36 import static org.junit.Assert.assertEquals; 37 import static org.junit.Assert.assertNotNull; 38 import static org.junit.Assert.fail; 39 40 import android.annotation.NonNull; 41 import android.content.res.Resources; 42 import android.net.NetworkStatsCollection.Key; 43 import android.os.Process; 44 import android.os.UserHandle; 45 import android.telephony.SubscriptionPlan; 46 import android.telephony.TelephonyManager; 47 import android.text.format.DateUtils; 48 import android.util.ArrayMap; 49 import android.util.RecurrenceRule; 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 58 import libcore.io.IoUtils; 59 import libcore.io.Streams; 60 61 import org.junit.After; 62 import org.junit.Before; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 import org.mockito.Mockito; 66 67 import java.io.ByteArrayInputStream; 68 import java.io.ByteArrayOutputStream; 69 import java.io.File; 70 import java.io.FileOutputStream; 71 import java.io.InputStream; 72 import java.io.OutputStream; 73 import java.time.Clock; 74 import java.time.Instant; 75 import java.time.ZoneId; 76 import java.time.ZonedDateTime; 77 import java.util.ArrayList; 78 import java.util.List; 79 import java.util.Map; 80 import java.util.Set; 81 82 /** 83 * Tests for {@link NetworkStatsCollection}. 84 */ 85 @RunWith(DevSdkIgnoreRunner.class) 86 @SmallTest 87 @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available 88 public class NetworkStatsCollectionTest { 89 90 private static final String TEST_FILE = "test.bin"; 91 private static final String TEST_IMSI = "310260000000000"; 92 private static final int TEST_SUBID = 1; 93 94 private static final long TIME_A = 1326088800000L; // UTC: Monday 9th January 2012 06:00:00 AM 95 private static final long TIME_B = 1326110400000L; // UTC: Monday 9th January 2012 12:00:00 PM 96 private static final long TIME_C = 1326132000000L; // UTC: Monday 9th January 2012 06:00:00 PM 97 98 private static Clock sOriginalClock; 99 100 @Before setUp()101 public void setUp() throws Exception { 102 sOriginalClock = RecurrenceRule.sClock; 103 } 104 105 @After tearDown()106 public void tearDown() throws Exception { 107 RecurrenceRule.sClock = sOriginalClock; 108 } 109 setClock(Instant instant)110 private void setClock(Instant instant) { 111 RecurrenceRule.sClock = Clock.fixed(instant, ZoneId.systemDefault()); 112 } 113 114 @Test testReadLegacyNetwork()115 public void testReadLegacyNetwork() throws Exception { 116 final File testFile = 117 new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); 118 stageFile(R.raw.netstats_v1, testFile); 119 120 final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); 121 collection.readLegacyNetwork(testFile); 122 123 // verify that history read correctly 124 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 125 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE); 126 127 // now export into a unified format 128 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 129 collection.write(bos); 130 131 // clear structure completely 132 collection.reset(); 133 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 134 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE); 135 136 // and read back into structure, verifying that totals are same 137 collection.read(new ByteArrayInputStream(bos.toByteArray())); 138 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 139 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE); 140 } 141 142 @Test testReadLegacyUid()143 public void testReadLegacyUid() throws Exception { 144 final File testFile = 145 new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); 146 stageFile(R.raw.netstats_uid_v4, testFile); 147 148 final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); 149 collection.readLegacyUid(testFile, false); 150 151 // verify that history read correctly 152 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 153 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE); 154 155 // now export into a unified format 156 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 157 collection.write(bos); 158 159 // clear structure completely 160 collection.reset(); 161 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 162 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE); 163 164 // and read back into structure, verifying that totals are same 165 collection.read(new ByteArrayInputStream(bos.toByteArray())); 166 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 167 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE); 168 } 169 170 @Test testReadLegacyUidTags()171 public void testReadLegacyUidTags() throws Exception { 172 final File testFile = 173 new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); 174 stageFile(R.raw.netstats_uid_v4, testFile); 175 176 final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); 177 collection.readLegacyUid(testFile, true); 178 179 // verify that history read correctly 180 assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), 181 77017831L, 100995L, 35436758L, 92344L); 182 183 // now export into a unified format 184 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 185 collection.write(bos); 186 187 // clear structure completely 188 collection.reset(); 189 assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), 190 0L, 0L, 0L, 0L); 191 192 // and read back into structure, verifying that totals are same 193 collection.read(new ByteArrayInputStream(bos.toByteArray())); 194 assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), 195 77017831L, 100995L, 35436758L, 92344L); 196 } 197 198 @Test testStartEndAtomicBuckets()199 public void testStartEndAtomicBuckets() throws Exception { 200 final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); 201 202 // record empty data straddling between buckets 203 final NetworkStats.Entry entry = new NetworkStats.Entry(); 204 entry.rxBytes = 32; 205 collection.recordData(Mockito.mock(NetworkIdentitySet.class), UID_ALL, SET_DEFAULT, 206 TAG_NONE, 30 * MINUTE_IN_MILLIS, 90 * MINUTE_IN_MILLIS, entry); 207 208 // assert that we report boundary in atomic buckets 209 assertEquals(0, collection.getStartMillis()); 210 assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis()); 211 } 212 213 @Test testAccessLevels()214 public void testAccessLevels() throws Exception { 215 final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); 216 final NetworkStats.Entry entry = new NetworkStats.Entry(); 217 final NetworkIdentitySet identSet = new NetworkIdentitySet(); 218 identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, 219 TEST_IMSI, null, false, true, true, OEM_NONE, TEST_SUBID)); 220 221 int myUid = Process.myUid(); 222 int otherUidInSameUser = Process.myUid() + 1; 223 int uidInDifferentUser = Process.myUid() + UserHandle.PER_USER_RANGE; 224 225 // Record one entry for the current UID. 226 entry.rxBytes = 32; 227 collection.recordData(identSet, myUid, SET_DEFAULT, TAG_NONE, 0, 60 * MINUTE_IN_MILLIS, 228 entry); 229 230 // Record one entry for another UID in this user. 231 entry.rxBytes = 64; 232 collection.recordData(identSet, otherUidInSameUser, SET_DEFAULT, TAG_NONE, 0, 233 60 * MINUTE_IN_MILLIS, entry); 234 235 // Record one entry for the system UID. 236 entry.rxBytes = 128; 237 collection.recordData(identSet, Process.SYSTEM_UID, SET_DEFAULT, TAG_NONE, 0, 238 60 * MINUTE_IN_MILLIS, entry); 239 240 // Record one entry for a UID in a different user. 241 entry.rxBytes = 256; 242 collection.recordData(identSet, uidInDifferentUser, SET_DEFAULT, TAG_NONE, 0, 243 60 * MINUTE_IN_MILLIS, entry); 244 245 // Verify the set of relevant UIDs for each access level. 246 assertArrayEquals(new int[] { myUid }, 247 collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT)); 248 assertArrayEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser }, 249 collection.getRelevantUids(NetworkStatsAccess.Level.USER)); 250 assertArrayEquals( 251 new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser }, 252 collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE)); 253 254 // Verify security check in getHistory. 255 assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, 256 myUid, SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid)); 257 try { 258 collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, otherUidInSameUser, 259 SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid); 260 fail("Should have thrown SecurityException for accessing different UID"); 261 } catch (SecurityException e) { 262 // expected 263 } 264 265 // Verify appropriate aggregation in getSummary. 266 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32, 0, 0, 0, 267 NetworkStatsAccess.Level.DEFAULT); 268 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128, 0, 0, 0, 269 NetworkStatsAccess.Level.USER); 270 assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128 + 256, 0, 0, 271 0, NetworkStatsAccess.Level.DEVICE); 272 } 273 274 @Test testAugmentPlan()275 public void testAugmentPlan() throws Exception { 276 final File testFile = 277 new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); 278 stageFile(R.raw.netstats_v1, testFile); 279 280 final NetworkStatsCollection emptyCollection = 281 new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); 282 final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); 283 collection.readLegacyNetwork(testFile); 284 285 // We're in the future, but not that far off 286 setClock(Instant.parse("2012-06-01T00:00:00.00Z")); 287 288 // Test a bunch of plans that should result in no augmentation 289 final List<SubscriptionPlan> plans = new ArrayList<>(); 290 291 // No plan 292 plans.add(null); 293 // No usage anchor 294 plans.add(SubscriptionPlan.Builder 295 .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")).build()); 296 // Usage anchor far in past 297 plans.add(SubscriptionPlan.Builder 298 .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")) 299 .setDataUsage(1000L, TIME_A - DateUtils.YEAR_IN_MILLIS).build()); 300 // Usage anchor far in future 301 plans.add(SubscriptionPlan.Builder 302 .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")) 303 .setDataUsage(1000L, TIME_A + DateUtils.YEAR_IN_MILLIS).build()); 304 // Usage anchor near but outside cycle 305 plans.add(SubscriptionPlan.Builder 306 .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), 307 ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) 308 .setDataUsage(1000L, TIME_C).build()); 309 310 for (SubscriptionPlan plan : plans) { 311 int i; 312 NetworkStatsHistory history; 313 314 // Empty collection should be untouched 315 history = getHistory(emptyCollection, plan, TIME_A, TIME_C); 316 assertEquals(0L, history.getTotalBytes()); 317 318 // Normal collection should be untouched 319 history = getHistory(collection, plan, TIME_A, TIME_C); 320 i = 0; 321 assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); 322 assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); 323 assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); 324 assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); 325 assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); 326 assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); 327 assertEntry(10747, 50, 16839, 55, history.getValues(i++, null)); 328 assertEntry(10747, 49, 16837, 54, history.getValues(i++, null)); 329 assertEntry(89191, 151, 18021, 140, history.getValues(i++, null)); 330 assertEntry(89190, 150, 18020, 139, history.getValues(i++, null)); 331 assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); 332 assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); 333 assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); 334 assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); 335 assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); 336 assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); 337 assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); 338 assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); 339 assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); 340 assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); 341 assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); 342 assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); 343 assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); 344 assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); 345 assertEquals(history.size(), i); 346 347 // Slice from middle should be untouched 348 history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, 349 TIME_B + HOUR_IN_MILLIS); 350 i = 0; 351 assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); 352 assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); 353 assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); 354 assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); 355 assertEquals(history.size(), i); 356 } 357 358 // Lower anchor in the middle of plan 359 { 360 int i; 361 NetworkStatsHistory history; 362 363 final SubscriptionPlan plan = SubscriptionPlan.Builder 364 .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), 365 ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) 366 .setDataUsage(200000L, TIME_B).build(); 367 368 // Empty collection should be augmented 369 history = getHistory(emptyCollection, plan, TIME_A, TIME_C); 370 assertEquals(200000L, history.getTotalBytes()); 371 372 // Normal collection should be augmented 373 history = getHistory(collection, plan, TIME_A, TIME_C); 374 i = 0; 375 assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); 376 assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); 377 assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); 378 assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); 379 assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); 380 assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); 381 // Cycle point; start data normalization 382 assertEntry(7507, 0, 11763, 0, history.getValues(i++, null)); 383 assertEntry(7507, 0, 11762, 0, history.getValues(i++, null)); 384 assertEntry(62309, 0, 12589, 0, history.getValues(i++, null)); 385 assertEntry(62309, 0, 12588, 0, history.getValues(i++, null)); 386 assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); 387 assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); 388 // Anchor point; end data normalization 389 assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); 390 assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); 391 assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); 392 assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); 393 assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); 394 assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); 395 // Cycle point 396 assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); 397 assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); 398 assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); 399 assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); 400 assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); 401 assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); 402 assertEquals(history.size(), i); 403 404 // Slice from middle should be augmented 405 history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, 406 TIME_B + HOUR_IN_MILLIS); 407 i = 0; 408 assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); 409 assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); 410 assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); 411 assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); 412 assertEquals(history.size(), i); 413 } 414 415 // Higher anchor in the middle of plan 416 { 417 int i; 418 NetworkStatsHistory history; 419 420 final SubscriptionPlan plan = SubscriptionPlan.Builder 421 .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), 422 ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) 423 .setDataUsage(400000L, TIME_B + MINUTE_IN_MILLIS).build(); 424 425 // Empty collection should be augmented 426 history = getHistory(emptyCollection, plan, TIME_A, TIME_C); 427 assertEquals(400000L, history.getTotalBytes()); 428 429 // Normal collection should be augmented 430 history = getHistory(collection, plan, TIME_A, TIME_C); 431 i = 0; 432 assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); 433 assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); 434 assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); 435 assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); 436 assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); 437 assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); 438 // Cycle point; start data normalization 439 assertEntry(15015, 0, 23527, 0, history.getValues(i++, null)); 440 assertEntry(15015, 0, 23524, 0, history.getValues(i++, null)); 441 assertEntry(124619, 0, 25179, 0, history.getValues(i++, null)); 442 assertEntry(124618, 0, 25177, 0, history.getValues(i++, null)); 443 assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); 444 assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); 445 // Anchor point; end data normalization 446 assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); 447 assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); 448 assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); 449 assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); 450 assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); 451 assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); 452 // Cycle point 453 assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); 454 assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); 455 assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); 456 assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); 457 assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); 458 assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); 459 460 // Slice from middle should be augmented 461 history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, 462 TIME_B + HOUR_IN_MILLIS); 463 i = 0; 464 assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); 465 assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); 466 assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); 467 assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); 468 assertEquals(history.size(), i); 469 } 470 } 471 472 @Test testAugmentPlanGigantic()473 public void testAugmentPlanGigantic() throws Exception { 474 // We're in the future, but not that far off 475 setClock(Instant.parse("2012-06-01T00:00:00.00Z")); 476 477 // Create a simple history with a ton of measured usage 478 final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS); 479 final NetworkIdentitySet ident = new NetworkIdentitySet(); 480 ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null, 481 false, true, true, OEM_NONE, TEST_SUBID)); 482 large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B, 483 new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0)); 484 485 // Verify untouched total 486 assertEquals(12_730_893_164L, getHistory(large, null, TIME_A, TIME_C).getTotalBytes()); 487 488 // Verify anchor that might cause overflows 489 final SubscriptionPlan plan = SubscriptionPlan.Builder 490 .createRecurringMonthly(ZonedDateTime.parse("2012-01-09T00:00:00.00Z")) 491 .setDataUsage(4_939_212_390L, TIME_B).build(); 492 assertEquals(4_939_212_386L, getHistory(large, plan, TIME_A, TIME_C).getTotalBytes()); 493 } 494 495 @Test testRounding()496 public void testRounding() throws Exception { 497 final NetworkStatsCollection coll = new NetworkStatsCollection(HOUR_IN_MILLIS); 498 499 // Special values should remain unchanged 500 for (long time : new long[] { 501 Long.MIN_VALUE, Long.MAX_VALUE, SubscriptionPlan.TIME_UNKNOWN 502 }) { 503 assertEquals(time, coll.roundUp(time)); 504 assertEquals(time, coll.roundDown(time)); 505 } 506 507 assertEquals(TIME_A, coll.roundUp(TIME_A)); 508 assertEquals(TIME_A, coll.roundDown(TIME_A)); 509 510 assertEquals(TIME_A + HOUR_IN_MILLIS, coll.roundUp(TIME_A + 1)); 511 assertEquals(TIME_A, coll.roundDown(TIME_A + 1)); 512 513 assertEquals(TIME_A, coll.roundUp(TIME_A - 1)); 514 assertEquals(TIME_A - HOUR_IN_MILLIS, coll.roundDown(TIME_A - 1)); 515 } 516 517 @Test testMultiplySafeRational()518 public void testMultiplySafeRational() { 519 assertEquals(25, multiplySafeByRational(50, 1, 2)); 520 assertEquals(100, multiplySafeByRational(50, 2, 1)); 521 522 assertEquals(-10, multiplySafeByRational(30, -1, 3)); 523 assertEquals(0, multiplySafeByRational(30, 0, 3)); 524 assertEquals(10, multiplySafeByRational(30, 1, 3)); 525 assertEquals(20, multiplySafeByRational(30, 2, 3)); 526 assertEquals(30, multiplySafeByRational(30, 3, 3)); 527 assertEquals(40, multiplySafeByRational(30, 4, 3)); 528 529 assertEquals(100_000_000_000L, 530 multiplySafeByRational(300_000_000_000L, 10_000_000_000L, 30_000_000_000L)); 531 assertEquals(100_000_000_010L, 532 multiplySafeByRational(300_000_000_000L, 10_000_000_001L, 30_000_000_000L)); 533 assertEquals(823_202_048L, 534 multiplySafeByRational(4_939_212_288L, 2_121_815_528L, 12_730_893_165L)); 535 536 assertThrows(ArithmeticException.class, () -> multiplySafeByRational(30, 3, 0)); 537 } 538 assertCollectionEntries( @onNull Map<Key, NetworkStatsHistory> expectedEntries, @NonNull NetworkStatsCollection collection)539 private static void assertCollectionEntries( 540 @NonNull Map<Key, NetworkStatsHistory> expectedEntries, 541 @NonNull NetworkStatsCollection collection) { 542 final Map<Key, NetworkStatsHistory> actualEntries = collection.getEntries(); 543 assertEquals(expectedEntries.size(), actualEntries.size()); 544 for (Key expectedKey : expectedEntries.keySet()) { 545 final NetworkStatsHistory expectedHistory = expectedEntries.get(expectedKey); 546 final NetworkStatsHistory actualHistory = actualEntries.get(expectedKey); 547 assertNotNull(actualHistory); 548 assertEquals(expectedHistory.getEntries(), actualHistory.getEntries()); 549 actualEntries.remove(expectedKey); 550 } 551 assertEquals(0, actualEntries.size()); 552 } 553 554 @Test testRemoveHistoryBefore()555 public void testRemoveHistoryBefore() { 556 final NetworkIdentity testIdent = new NetworkIdentity.Builder() 557 .setSubscriberId(TEST_IMSI).build(); 558 final Key key1 = new Key(Set.of(testIdent), 0, 0, 0); 559 final Key key2 = new Key(Set.of(testIdent), 1, 0, 0); 560 final long bucketDuration = 10; 561 562 // Prepare entries for testing, with different bucket start timestamps. 563 final NetworkStatsHistory.Entry entry1 = new NetworkStatsHistory.Entry(10, 10, 40, 564 4, 50, 5, 60); 565 final NetworkStatsHistory.Entry entry2 = new NetworkStatsHistory.Entry(20, 10, 3, 566 41, 7, 1, 0); 567 final NetworkStatsHistory.Entry entry3 = new NetworkStatsHistory.Entry(30, 10, 1, 568 21, 70, 4, 1); 569 570 NetworkStatsHistory history1 = new NetworkStatsHistory.Builder(10, 5) 571 .addEntry(entry1) 572 .addEntry(entry2) 573 .build(); 574 NetworkStatsHistory history2 = new NetworkStatsHistory.Builder(10, 5) 575 .addEntry(entry2) 576 .addEntry(entry3) 577 .build(); 578 NetworkStatsCollection collection = new NetworkStatsCollection.Builder(bucketDuration) 579 .addEntry(key1, history1) 580 .addEntry(key2, history2) 581 .build(); 582 583 // Verify nothing is removed if the cutoff time is equal to bucketStart. 584 collection.removeHistoryBefore(10); 585 final Map<Key, NetworkStatsHistory> expectedEntries = new ArrayMap<>(); 586 expectedEntries.put(key1, history1); 587 expectedEntries.put(key2, history2); 588 assertCollectionEntries(expectedEntries, collection); 589 590 // Verify entry1 will be removed if its bucket start before to cutoff timestamp. 591 collection.removeHistoryBefore(11); 592 history1 = new NetworkStatsHistory.Builder(10, 5) 593 .addEntry(entry2) 594 .build(); 595 history2 = new NetworkStatsHistory.Builder(10, 5) 596 .addEntry(entry2) 597 .addEntry(entry3) 598 .build(); 599 final Map<Key, NetworkStatsHistory> cutoff1Entries1 = new ArrayMap<>(); 600 cutoff1Entries1.put(key1, history1); 601 cutoff1Entries1.put(key2, history2); 602 assertCollectionEntries(cutoff1Entries1, collection); 603 604 // Verify entry2 will be removed if its bucket start covers by cutoff timestamp. 605 collection.removeHistoryBefore(22); 606 history2 = new NetworkStatsHistory.Builder(10, 5) 607 .addEntry(entry3) 608 .build(); 609 final Map<Key, NetworkStatsHistory> cutoffEntries2 = new ArrayMap<>(); 610 // History1 is not expected since the collection will omit empty entries. 611 cutoffEntries2.put(key2, history2); 612 assertCollectionEntries(cutoffEntries2, collection); 613 614 // Verify all entries will be removed if cutoff timestamp covers all. 615 collection.removeHistoryBefore(Long.MAX_VALUE); 616 assertEquals(0, collection.getEntries().size()); 617 } 618 619 /** 620 * Copy a {@link Resources#openRawResource(int)} into {@link File} for 621 * testing purposes. 622 */ stageFile(int rawId, File file)623 private void stageFile(int rawId, File file) throws Exception { 624 new File(file.getParent()).mkdirs(); 625 InputStream in = null; 626 OutputStream out = null; 627 try { 628 in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId); 629 out = new FileOutputStream(file); 630 Streams.copy(in, out); 631 } finally { 632 IoUtils.closeQuietly(in); 633 IoUtils.closeQuietly(out); 634 } 635 } 636 getHistory(NetworkStatsCollection collection, SubscriptionPlan augmentPlan, long start, long end)637 private static NetworkStatsHistory getHistory(NetworkStatsCollection collection, 638 SubscriptionPlan augmentPlan, long start, long end) { 639 return collection.getHistory(buildTemplateMobileAll(TEST_IMSI), augmentPlan, UID_ALL, 640 SET_ALL, TAG_NONE, FIELD_ALL, start, end, NetworkStatsAccess.Level.DEVICE, myUid()); 641 } 642 assertSummaryTotal(NetworkStatsCollection collection, NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets, @NetworkStatsAccess.Level int accessLevel)643 private static void assertSummaryTotal(NetworkStatsCollection collection, 644 NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets, 645 @NetworkStatsAccess.Level int accessLevel) { 646 final NetworkStats.Entry actual = collection.getSummary( 647 template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel, myUid()) 648 .getTotal(null); 649 assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual); 650 } 651 assertSummaryTotalIncludingTags(NetworkStatsCollection collection, NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets)652 private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection, 653 NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) { 654 final NetworkStats.Entry actual = collection.getSummary( 655 template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE, myUid()) 656 .getTotalIncludingTags(null); 657 assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual); 658 } 659 assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, NetworkStats.Entry actual)660 private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, 661 NetworkStats.Entry actual) { 662 assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual); 663 } 664 assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, NetworkStatsHistory.Entry actual)665 private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, 666 NetworkStatsHistory.Entry actual) { 667 assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual); 668 } 669 assertEntry(NetworkStats.Entry expected, NetworkStatsHistory.Entry actual)670 private static void assertEntry(NetworkStats.Entry expected, 671 NetworkStatsHistory.Entry actual) { 672 assertEntry(expected, new NetworkStats.Entry(actual.rxBytes, actual.rxPackets, 673 actual.txBytes, actual.txPackets, 0L)); 674 } 675 assertEntry(NetworkStatsHistory.Entry expected, NetworkStatsHistory.Entry actual)676 private static void assertEntry(NetworkStatsHistory.Entry expected, 677 NetworkStatsHistory.Entry actual) { 678 assertEntry(new NetworkStats.Entry(actual.rxBytes, actual.rxPackets, 679 actual.txBytes, actual.txPackets, 0L), 680 new NetworkStats.Entry(actual.rxBytes, actual.rxPackets, 681 actual.txBytes, actual.txPackets, 0L)); 682 } 683 assertEntry(NetworkStats.Entry expected, NetworkStats.Entry actual)684 private static void assertEntry(NetworkStats.Entry expected, 685 NetworkStats.Entry actual) { 686 assertEquals("unexpected rxBytes", expected.rxBytes, actual.rxBytes); 687 assertEquals("unexpected rxPackets", expected.rxPackets, actual.rxPackets); 688 assertEquals("unexpected txBytes", expected.txBytes, actual.txBytes); 689 assertEquals("unexpected txPackets", expected.txPackets, actual.txPackets); 690 } 691 } 692