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