summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/NetworkStatsHistory.java40
-rw-r--r--core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java111
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java19
3 files changed, 165 insertions, 5 deletions
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 45d8e27..5edbf58 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -134,6 +134,18 @@ public class NetworkStatsHistory implements Parcelable {
}
/**
+ * Record an entire {@link NetworkStatsHistory} into this history. Usually
+ * for combining together stats for external reporting.
+ */
+ public void recordEntireHistory(NetworkStatsHistory input) {
+ for (int i = 0; i < input.bucketCount; i++) {
+ final long start = input.bucketStart[i];
+ final long end = start + input.bucketDuration;
+ recordData(start, end, input.rx[i], input.tx[i]);
+ }
+ }
+
+ /**
* Ensure that buckets exist for given time range, creating as needed.
*/
private void ensureBuckets(long start, long end) {
@@ -203,6 +215,34 @@ public class NetworkStatsHistory implements Parcelable {
}
/**
+ * Return interpolated data usage across the requested range. Interpolates
+ * across buckets, so values may be rounded slightly.
+ */
+ public void getTotalData(long start, long end, long[] outTotal) {
+ long rx = 0;
+ long tx = 0;
+
+ for (int i = bucketCount - 1; i >= 0; i--) {
+ final long curStart = bucketStart[i];
+ final long curEnd = curStart + bucketDuration;
+
+ // bucket is older than record; we're finished
+ if (curEnd < start) break;
+ // bucket is newer than record; keep looking
+ if (curStart > end) continue;
+
+ final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
+ if (overlap > 0) {
+ rx += this.rx[i] * overlap / bucketDuration;
+ tx += this.tx[i] * overlap / bucketDuration;
+ }
+ }
+
+ outTotal[0] = rx;
+ outTotal[1] = tx;
+ }
+
+ /**
* @deprecated only for temporary testing
*/
@Deprecated
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 69bbcd7..0b72c3c 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -133,9 +133,80 @@ public class NetworkStatsHistoryTest extends TestCase {
assertBucket(stats, 1, 512L, 512L);
}
+ public void testRecordEntireGapIdentical() throws Exception {
+ final long[] total = new long[2];
+
+ // first, create two separate histories far apart
+ final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L);
+
+ final long TEST_START_2 = TEST_START + DAY_IN_MILLIS;
+ final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L);
+
+ // combine together with identical bucket size
+ stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats.recordEntireHistory(stats1);
+ stats.recordEntireHistory(stats2);
+
+ // first verify that totals match up
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 3000L, 1500L);
+
+ // now inspect internal buckets
+ assertBucket(stats, 0, 1000L, 500L);
+ assertBucket(stats, 1, 1000L, 500L);
+ assertBucket(stats, 2, 500L, 250L);
+ assertBucket(stats, 3, 500L, 250L);
+ }
+
+ public void testRecordEntireOverlapVaryingBuckets() throws Exception {
+ final long[] total = new long[2];
+
+ // create history just over hour bucket boundary
+ final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L);
+
+ final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS;
+ final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+ stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L);
+
+ // combine together with minute bucket size
+ stats = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+ stats.recordEntireHistory(stats1);
+ stats.recordEntireHistory(stats2);
+
+ // first verify that totals match up
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 650L, 650L);
+
+ // now inspect internal buckets
+ assertBucket(stats, 0, 10L, 10L);
+ assertBucket(stats, 1, 20L, 20L);
+ assertBucket(stats, 2, 20L, 20L);
+ assertBucket(stats, 3, 20L, 20L);
+ assertBucket(stats, 4, 20L, 20L);
+ assertBucket(stats, 5, 20L, 20L);
+ assertBucket(stats, 6, 10L, 10L);
+
+ // now combine using 15min buckets
+ stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
+ stats.recordEntireHistory(stats1);
+ stats.recordEntireHistory(stats2);
+
+ // first verify that totals match up
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 650L, 650L);
+
+ // and inspect buckets
+ assertBucket(stats, 0, 200L, 200L);
+ assertBucket(stats, 1, 150L, 150L);
+ assertBucket(stats, 2, 150L, 150L);
+ assertBucket(stats, 3, 150L, 150L);
+ }
+
public void testRemove() throws Exception {
- final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = new NetworkStatsHistory(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
// record some data across 24 buckets
stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
@@ -163,6 +234,37 @@ public class NetworkStatsHistoryTest extends TestCase {
assertEquals(0, stats.bucketCount);
}
+ public void testTotalData() throws Exception {
+ final long BUCKET_SIZE = HOUR_IN_MILLIS;
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
+
+ // record uniform data across day
+ stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L);
+
+ final long[] total = new long[2];
+
+ // verify that total outside range is 0
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, total);
+ assertTotalEquals(total, 0, 0);
+
+ // verify total in first hour
+ stats.getTotalData(TEST_START, TEST_START + HOUR_IN_MILLIS, total);
+ assertTotalEquals(total, 100, 200);
+
+ // verify total across 1.5 hours
+ stats.getTotalData(TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), total);
+ assertTotalEquals(total, 150, 300);
+
+ // verify total beyond end
+ stats.getTotalData(TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 100, 200);
+
+ // verify everything total
+ stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+ assertTotalEquals(total, 2400, 4800);
+
+ }
+
@Suppress
public void testFuzzing() throws Exception {
try {
@@ -196,6 +298,11 @@ public class NetworkStatsHistoryTest extends TestCase {
}
}
+ private static void assertTotalEquals(long[] total, long rx, long tx) {
+ assertEquals("unexpected rx", rx, total[0]);
+ assertEquals("unexpected tx", tx, total[1]);
+ }
+
private static void assertBucket(NetworkStatsHistory stats, int index, long rx, long tx) {
assertEquals("unexpected rx", rx, stats.rx[index]);
assertEquals("unexpected tx", tx, stats.tx[index]);
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 967e491..3892de8 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -210,7 +210,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
for (InterfaceIdentity ident : mSummaryStats.keySet()) {
final NetworkStatsHistory history = mSummaryStats.get(ident);
if (ident.matchesTemplate(networkTemplate, subscriberId)) {
- // TODO: combine all matching history data into a single history
+ combined.recordEntireHistory(history);
}
}
return combined;
@@ -231,8 +231,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: create relaxed permission for reading stats
mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
- // TODO: total UID-granularity usage between time range
- return null;
+ // TODO: apply networktemplate once granular uid stats are stored.
+
+ synchronized (mStatsLock) {
+ final int size = mDetailStats.size();
+ final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size);
+
+ final long[] total = new long[2];
+ for (int i = 0; i < size; i++) {
+ final int uid = mDetailStats.keyAt(i);
+ final NetworkStatsHistory history = mDetailStats.valueAt(i);
+ history.getTotalData(start, end, total);
+ stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
+ }
+ return stats.build();
+ }
}
/**