summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/NetworkStatsHistory.java51
-rw-r--r--core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java102
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java38
4 files changed, 117 insertions, 97 deletions
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index b0930b2..8bd1738 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -124,6 +124,22 @@ public class NetworkStatsHistory implements Parcelable {
return bucketDuration;
}
+ public long getStart() {
+ if (bucketCount > 0) {
+ return bucketStart[0];
+ } else {
+ return Long.MAX_VALUE;
+ }
+ }
+
+ public long getEnd() {
+ if (bucketCount > 0) {
+ return bucketStart[bucketCount - 1] + bucketDuration;
+ } else {
+ return Long.MIN_VALUE;
+ }
+ }
+
/**
* Return specific stats entry.
*/
@@ -253,9 +269,20 @@ public class NetworkStatsHistory implements Parcelable {
* Return interpolated data usage across the requested range. Interpolates
* across buckets, so values may be rounded slightly.
*/
- public long[] getTotalData(long start, long end, long[] outTotal) {
- long rx = 0;
- long tx = 0;
+ public Entry getValues(long start, long end, Entry recycle) {
+ return getValues(start, end, Long.MAX_VALUE, recycle);
+ }
+
+ /**
+ * Return interpolated data usage across the requested range. Interpolates
+ * across buckets, so values may be rounded slightly.
+ */
+ public Entry getValues(long start, long end, long now, Entry recycle) {
+ final Entry entry = recycle != null ? recycle : new Entry();
+ entry.bucketStart = start;
+ entry.bucketDuration = end - start;
+ entry.rxBytes = 0;
+ entry.txBytes = 0;
for (int i = bucketCount - 1; i >= 0; i--) {
final long curStart = bucketStart[i];
@@ -266,19 +293,19 @@ public class NetworkStatsHistory implements Parcelable {
// bucket is newer than record; keep looking
if (curStart > end) continue;
+ // include full value for active buckets, otherwise only fractional
+ final boolean activeBucket = curStart < now && curEnd > now;
final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
- if (overlap > 0) {
- rx += this.rxBytes[i] * overlap / bucketDuration;
- tx += this.txBytes[i] * overlap / bucketDuration;
+ if (activeBucket || overlap == bucketDuration) {
+ entry.rxBytes += rxBytes[i];
+ entry.txBytes += txBytes[i];
+ } else if (overlap > 0) {
+ entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
+ entry.txBytes += txBytes[i] * overlap / bucketDuration;
}
}
- if (outTotal == null || outTotal.length != 2) {
- outTotal = new long[2];
- }
- outTotal[0] = rx;
- outTotal[1] = tx;
- return outTotal;
+ return entry;
}
/**
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 16bb000..9403d95 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -55,7 +55,7 @@ public class NetworkStatsHistoryTest extends TestCase {
stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L);
assertEquals(1, stats.size());
- assertEntry(stats, 0, 1024L, 2048L);
+ assertValues(stats, 0, 1024L, 2048L);
}
public void testRecordEqualBuckets() throws Exception {
@@ -67,8 +67,8 @@ public class NetworkStatsHistoryTest extends TestCase {
stats.recordData(recordStart, recordStart + bucketDuration, 1024L, 128L);
assertEquals(2, stats.size());
- assertEntry(stats, 0, 512L, 64L);
- assertEntry(stats, 1, 512L, 64L);
+ assertValues(stats, 0, 512L, 64L);
+ assertValues(stats, 1, 512L, 64L);
}
public void testRecordTouchingBuckets() throws Exception {
@@ -83,11 +83,11 @@ public class NetworkStatsHistoryTest extends TestCase {
assertEquals(3, stats.size());
// first bucket should have (1/20 of value)
- assertEntry(stats, 0, 50L, 250L);
+ assertValues(stats, 0, 50L, 250L);
// second bucket should have (15/20 of value)
- assertEntry(stats, 1, 750L, 3750L);
+ assertValues(stats, 1, 750L, 3750L);
// final bucket should have (4/20 of value)
- assertEntry(stats, 2, 200L, 1000L);
+ assertValues(stats, 2, 200L, 1000L);
}
public void testRecordGapBuckets() throws Exception {
@@ -102,8 +102,8 @@ public class NetworkStatsHistoryTest extends TestCase {
// we should have two buckets, far apart from each other
assertEquals(2, stats.size());
- assertEntry(stats, 0, 128L, 256L);
- assertEntry(stats, 1, 64L, 512L);
+ assertValues(stats, 0, 128L, 256L);
+ assertValues(stats, 1, 64L, 512L);
// now record something in middle, spread across two buckets
final long middleStart = TEST_START + DAY_IN_MILLIS;
@@ -112,10 +112,10 @@ public class NetworkStatsHistoryTest extends TestCase {
// now should have four buckets, with new record in middle two buckets
assertEquals(4, stats.size());
- assertEntry(stats, 0, 128L, 256L);
- assertEntry(stats, 1, 1024L, 1024L);
- assertEntry(stats, 2, 1024L, 1024L);
- assertEntry(stats, 3, 64L, 512L);
+ assertValues(stats, 0, 128L, 256L);
+ assertValues(stats, 1, 1024L, 1024L);
+ assertValues(stats, 2, 1024L, 1024L);
+ assertValues(stats, 3, 64L, 512L);
}
public void testRecordOverlapBuckets() throws Exception {
@@ -129,13 +129,11 @@ public class NetworkStatsHistoryTest extends TestCase {
// should have two buckets, with some data mixed together
assertEquals(2, stats.size());
- assertEntry(stats, 0, 768L, 768L);
- assertEntry(stats, 1, 512L, 512L);
+ assertValues(stats, 0, 768L, 768L);
+ assertValues(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);
@@ -150,19 +148,16 @@ public class NetworkStatsHistoryTest extends TestCase {
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);
+ assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 3000L, 1500L);
// now inspect internal buckets
- assertEntry(stats, 0, 1000L, 500L);
- assertEntry(stats, 1, 1000L, 500L);
- assertEntry(stats, 2, 500L, 250L);
- assertEntry(stats, 3, 500L, 250L);
+ assertValues(stats, 0, 1000L, 500L);
+ assertValues(stats, 1, 1000L, 500L);
+ assertValues(stats, 2, 500L, 250L);
+ assertValues(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);
@@ -177,17 +172,16 @@ public class NetworkStatsHistoryTest extends TestCase {
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);
+ assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
// now inspect internal buckets
- assertEntry(stats, 0, 10L, 10L);
- assertEntry(stats, 1, 20L, 20L);
- assertEntry(stats, 2, 20L, 20L);
- assertEntry(stats, 3, 20L, 20L);
- assertEntry(stats, 4, 20L, 20L);
- assertEntry(stats, 5, 20L, 20L);
- assertEntry(stats, 6, 10L, 10L);
+ assertValues(stats, 0, 10L, 10L);
+ assertValues(stats, 1, 20L, 20L);
+ assertValues(stats, 2, 20L, 20L);
+ assertValues(stats, 3, 20L, 20L);
+ assertValues(stats, 4, 20L, 20L);
+ assertValues(stats, 5, 20L, 20L);
+ assertValues(stats, 6, 10L, 10L);
// now combine using 15min buckets
stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
@@ -195,14 +189,13 @@ public class NetworkStatsHistoryTest extends TestCase {
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);
+ assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
// and inspect buckets
- assertEntry(stats, 0, 200L, 200L);
- assertEntry(stats, 1, 150L, 150L);
- assertEntry(stats, 2, 150L, 150L);
- assertEntry(stats, 3, 150L, 150L);
+ assertValues(stats, 0, 200L, 200L);
+ assertValues(stats, 1, 150L, 150L);
+ assertValues(stats, 2, 150L, 150L);
+ assertValues(stats, 3, 150L, 150L);
}
public void testRemove() throws Exception {
@@ -241,27 +234,20 @@ public class NetworkStatsHistoryTest extends TestCase {
// 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);
+ assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, 0L, 0L);
// verify total in first hour
- stats.getTotalData(TEST_START, TEST_START + HOUR_IN_MILLIS, total);
- assertTotalEquals(total, 100, 200);
+ assertValues(stats, TEST_START, TEST_START + HOUR_IN_MILLIS, 100L, 200L);
// verify total across 1.5 hours
- stats.getTotalData(TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), total);
- assertTotalEquals(total, 150, 300);
+ assertValues(stats, TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), 150L, 300L);
// verify total beyond end
- stats.getTotalData(TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, total);
- assertTotalEquals(total, 100, 200);
+ assertValues(stats, TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, 100L, 200L);
// verify everything total
- stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
- assertTotalEquals(total, 2400, 4800);
+ assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 2400L, 4800L);
}
@@ -302,16 +288,18 @@ public class NetworkStatsHistoryTest extends TestCase {
}
}
- private static void assertTotalEquals(long[] total, long rxBytes, long txBytes) {
- assertEquals("unexpected rxBytes", rxBytes, total[0]);
- assertEquals("unexpected txBytes", txBytes, total[1]);
- }
-
- private static void assertEntry(
+ private static void assertValues(
NetworkStatsHistory stats, int index, long rxBytes, long txBytes) {
final NetworkStatsHistory.Entry entry = stats.getValues(index, null);
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
assertEquals("unexpected txBytes", txBytes, entry.txBytes);
}
+ private static void assertValues(
+ NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) {
+ final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
+ assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+ assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+ }
+
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 872438c..54e94db 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -313,21 +313,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) {
+ // use system clock to be externally consistent
+ final long now = System.currentTimeMillis();
+
final NetworkStats stats = new NetworkStats(end - start, 1);
final NetworkStats.Entry entry = new NetworkStats.Entry();
- long[] total = new long[2];
+ NetworkStatsHistory.Entry historyEntry = null;
// combine total from all interfaces that match template
for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
if (templateMatches(template, ident)) {
final NetworkStatsHistory history = mNetworkStats.get(ident);
- total = history.getTotalData(start, end, total);
+ historyEntry = history.getValues(start, end, now, historyEntry);
entry.iface = IFACE_ALL;
entry.uid = UID_ALL;
entry.tag = TAG_NONE;
- entry.rxBytes = total[0];
- entry.txBytes = total[1];
+ entry.rxBytes = historyEntry.rxBytes;
+ entry.txBytes = historyEntry.txBytes;
stats.combineValues(entry);
}
@@ -345,9 +348,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
synchronized (mStatsLock) {
ensureUidStatsLoadedLocked();
+ // use system clock to be externally consistent
+ final long now = System.currentTimeMillis();
+
final NetworkStats stats = new NetworkStats(end - start, 24);
final NetworkStats.Entry entry = new NetworkStats.Entry();
- long[] total = new long[2];
+ NetworkStatsHistory.Entry historyEntry = null;
for (NetworkIdentitySet ident : mUidStats.keySet()) {
if (templateMatches(template, ident)) {
@@ -361,13 +367,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// other tags when requested.
if (tag == TAG_NONE || includeTags) {
final NetworkStatsHistory history = uidStats.valueAt(i);
- total = history.getTotalData(start, end, total);
+ historyEntry = history.getValues(start, end, now, historyEntry);
entry.iface = IFACE_ALL;
entry.uid = uid;
entry.tag = tag;
- entry.rxBytes = total[0];
- entry.txBytes = total[1];
+ entry.rxBytes = historyEntry.rxBytes;
+ entry.txBytes = historyEntry.txBytes;
if (entry.rxBytes > 0 || entry.txBytes > 0) {
stats.combineValues(entry);
@@ -425,6 +431,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// broadcast.
final int uid = intent.getIntExtra(EXTRA_UID, 0);
synchronized (mStatsLock) {
+ // TODO: perform one last stats poll for UID
removeUidLocked(uid);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 36b3b82..ac74063 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -264,7 +264,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
public void testStatsBucketResize() throws Exception {
long elapsedRealtime = 0;
NetworkStatsHistory history = null;
- long[] total = null;
assertStatsFilesExist(false);
@@ -292,9 +291,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
// verify service recorded history
history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null));
- total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
- assertEquals(512L, total[0]);
- assertEquals(512L, total[1]);
+ assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
assertEquals(HOUR_IN_MILLIS, history.getBucketDuration());
assertEquals(2, history.size());
verifyAndReset();
@@ -311,9 +308,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
// verify identical stats, but spread across 4 buckets now
history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null));
- total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
- assertEquals(512L, total[0]);
- assertEquals(512L, total[1]);
+ assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration());
assertEquals(4, history.size());
verifyAndReset();
@@ -575,32 +570,28 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
NetworkStats stats = mService.getSummaryForAllUid(
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(3, stats.size());
- assertEntry(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L);
- assertEntry(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 1L, 10L, 1L);
- assertEntry(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L);
+ assertValues(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L);
+ assertValues(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 1L, 10L, 1L);
+ assertValues(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L);
// now verify that recent history only contains one uid
final long currentTime = TEST_START + elapsedRealtime;
stats = mService.getSummaryForAllUid(
sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
assertEquals(1, stats.size());
- assertEntry(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L);
+ assertValues(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L);
verifyAndReset();
}
- private void assertNetworkTotal(NetworkTemplate template, long rx, long tx) {
+ private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long txBytes) {
final NetworkStatsHistory history = mService.getHistoryForNetwork(template);
- final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
- assertEquals(rx, total[0]);
- assertEquals(tx, total[1]);
+ assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes);
}
- private void assertUidTotal(NetworkTemplate template, int uid, long rx, long tx) {
+ private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long txBytes) {
final NetworkStatsHistory history = mService.getHistoryForUid(template, uid, TAG_NONE);
- final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
- assertEquals(rx, total[0]);
- assertEquals(tx, total[1]);
+ assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes);
}
private void expectSystemReady() throws Exception {
@@ -660,7 +651,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
}
}
- private static void assertEntry(NetworkStats stats, int i, String iface, int uid, int tag,
+ private static void assertValues(NetworkStats stats, int i, String iface, int uid, int tag,
long rxBytes, long rxPackets, long txBytes, long txPackets) {
final NetworkStats.Entry entry = stats.getValues(i, null);
assertEquals(iface, entry.iface);
@@ -673,6 +664,13 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
// assertEquals(txPackets, entry.txPackets);
}
+ private static void assertValues(
+ NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) {
+ final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
+ assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+ assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+ }
+
private static NetworkState buildWifiState() {
final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);