Loading core/java/android/net/NetworkStats.java +61 −56 Original line number Diff line number Diff line Loading @@ -43,31 +43,19 @@ public class NetworkStats implements Parcelable { /** {@link #tag} value for without tag. */ public static final int TAG_NONE = 0; // TODO: move public fields to Entry accessors, then undeprecate // TODO: refactor rx/tx to rxBytes/txBytes /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. */ @Deprecated public final long elapsedRealtime; @Deprecated public int size; @Deprecated public String[] iface; @Deprecated public int[] uid; @Deprecated public int[] tag; @Deprecated public long[] rx; @Deprecated public long[] rxPackets; @Deprecated public long[] tx; @Deprecated public long[] txPackets; private final long elapsedRealtime; private int size; private String[] iface; private int[] uid; private int[] tag; private long[] rxBytes; private long[] rxPackets; private long[] txBytes; private long[] txPackets; public static class Entry { public String iface; Loading @@ -77,6 +65,20 @@ public class NetworkStats implements Parcelable { public long rxPackets; public long txBytes; public long txPackets; public Entry() { } public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { this.iface = iface; this.uid = uid; this.tag = tag; this.rxBytes = rxBytes; this.rxPackets = rxPackets; this.txBytes = txBytes; this.txPackets = txPackets; } } public NetworkStats(long elapsedRealtime, int initialSize) { Loading @@ -85,9 +87,9 @@ public class NetworkStats implements Parcelable { this.iface = new String[initialSize]; this.uid = new int[initialSize]; this.tag = new int[initialSize]; this.rx = new long[initialSize]; this.rxBytes = new long[initialSize]; this.rxPackets = new long[initialSize]; this.tx = new long[initialSize]; this.txBytes = new long[initialSize]; this.txPackets = new long[initialSize]; } Loading @@ -97,23 +99,15 @@ public class NetworkStats implements Parcelable { iface = parcel.createStringArray(); uid = parcel.createIntArray(); tag = parcel.createIntArray(); rx = parcel.createLongArray(); rxBytes = parcel.createLongArray(); rxPackets = parcel.createLongArray(); tx = parcel.createLongArray(); txBytes = parcel.createLongArray(); txPackets = parcel.createLongArray(); } /** * Add new stats entry with given values. */ public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { final Entry entry = new Entry(); entry.iface = iface; entry.uid = uid; entry.tag = tag; entry.rxBytes = rx; entry.txBytes = tx; return addValues(entry); public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { return addValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets)); } /** Loading @@ -126,18 +120,18 @@ public class NetworkStats implements Parcelable { iface = Arrays.copyOf(iface, newLength); uid = Arrays.copyOf(uid, newLength); tag = Arrays.copyOf(tag, newLength); rx = Arrays.copyOf(rx, newLength); rxBytes = Arrays.copyOf(rxBytes, newLength); rxPackets = Arrays.copyOf(rxPackets, newLength); tx = Arrays.copyOf(tx, newLength); txBytes = Arrays.copyOf(txBytes, newLength); txPackets = Arrays.copyOf(txPackets, newLength); } iface[size] = entry.iface; uid[size] = entry.uid; tag[size] = entry.tag; rx[size] = entry.rxBytes; rxBytes[size] = entry.rxBytes; rxPackets[size] = entry.rxPackets; tx[size] = entry.txBytes; txBytes[size] = entry.txBytes; txPackets[size] = entry.txPackets; size++; Loading @@ -152,9 +146,9 @@ public class NetworkStats implements Parcelable { entry.iface = iface[i]; entry.uid = uid[i]; entry.tag = tag[i]; entry.rxBytes = rx[i]; entry.rxBytes = rxBytes[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txBytes = txBytes[i]; entry.txPackets = txPackets[i]; return entry; } Loading @@ -167,20 +161,31 @@ public class NetworkStats implements Parcelable { return size; } // @VisibleForTesting public int internalSize() { return iface.length; } public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { return combineValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets)); } /** * Combine given values with an existing row, or create a new row if * {@link #findIndex(String, int, int)} is unable to find match. Can also be * used to subtract values from existing rows. */ public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) { // TODO: extent to accept rxPackets/txPackets final int i = findIndex(iface, uid, tag); public NetworkStats combineValues(Entry entry) { final int i = findIndex(entry.iface, entry.uid, entry.tag); if (i == -1) { // only create new entry when positive contribution addEntry(iface, uid, tag, rx, tx); addValues(entry); } else { this.rx[i] += rx; this.tx[i] += tx; rxBytes[i] += entry.rxBytes; rxPackets[i] += entry.rxPackets; txBytes[i] += entry.txBytes; txPackets[i] += entry.txPackets; } return this; } Loading Loading @@ -280,15 +285,15 @@ public class NetworkStats implements Parcelable { final int j = value.findIndex(entry.iface, entry.uid, entry.tag); if (j == -1) { // newly appearing row, return entire value entry.rxBytes = rx[i]; entry.rxBytes = rxBytes[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txBytes = txBytes[i]; entry.txPackets = txPackets[i]; } else { // existing row, subtract remote value entry.rxBytes = rx[i] - value.rx[j]; entry.rxBytes = rxBytes[i] - value.rxBytes[j]; entry.rxPackets = rxPackets[i] - value.rxPackets[j]; entry.txBytes = tx[i] - value.tx[j]; entry.txBytes = txBytes[i] - value.txBytes[j]; entry.txPackets = txPackets[i] - value.txPackets[j]; if (enforceMonotonic && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 Loading Loading @@ -321,9 +326,9 @@ public class NetworkStats implements Parcelable { pw.print(" iface="); pw.print(iface[i]); pw.print(" uid="); pw.print(uid[i]); pw.print(" tag="); pw.print(tag[i]); pw.print(" rxBytes="); pw.print(rx[i]); pw.print(" rxBytes="); pw.print(rxBytes[i]); pw.print(" rxPackets="); pw.print(rxPackets[i]); pw.print(" txBytes="); pw.print(tx[i]); pw.print(" txBytes="); pw.print(txBytes[i]); pw.print(" txPackets="); pw.println(txPackets[i]); } } Loading @@ -347,9 +352,9 @@ public class NetworkStats implements Parcelable { dest.writeStringArray(iface); dest.writeIntArray(uid); dest.writeIntArray(tag); dest.writeLongArray(rx); dest.writeLongArray(rxBytes); dest.writeLongArray(rxPackets); dest.writeLongArray(tx); dest.writeLongArray(txBytes); dest.writeLongArray(txPackets); } Loading core/java/android/net/NetworkStatsHistory.java +59 −32 Original line number Diff line number Diff line Loading @@ -43,13 +43,20 @@ public class NetworkStatsHistory implements Parcelable { private static final int VERSION_INIT = 1; // TODO: teach about varint encoding to use less disk space // TODO: extend to record rxPackets/txPackets public final long bucketDuration; private final long bucketDuration; private int bucketCount; private long[] bucketStart; private long[] rxBytes; private long[] txBytes; public int bucketCount; public long[] bucketStart; public long[] rx; public long[] tx; public static class Entry { public long bucketStart; public long bucketDuration; public long rxBytes; public long txBytes; } public NetworkStatsHistory(long bucketDuration) { this(bucketDuration, 10); Loading @@ -58,16 +65,16 @@ public class NetworkStatsHistory implements Parcelable { public NetworkStatsHistory(long bucketDuration, int initialSize) { this.bucketDuration = bucketDuration; bucketStart = new long[initialSize]; rx = new long[initialSize]; tx = new long[initialSize]; rxBytes = new long[initialSize]; txBytes = new long[initialSize]; bucketCount = 0; } public NetworkStatsHistory(Parcel in) { bucketDuration = in.readLong(); bucketStart = readLongArray(in); rx = in.createLongArray(); tx = in.createLongArray(); rxBytes = in.createLongArray(); txBytes = in.createLongArray(); bucketCount = bucketStart.length; } Loading @@ -75,8 +82,8 @@ public class NetworkStatsHistory implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rx, bucketCount); writeLongArray(out, tx, bucketCount); writeLongArray(out, rxBytes, bucketCount); writeLongArray(out, txBytes, bucketCount); } public NetworkStatsHistory(DataInputStream in) throws IOException { Loading @@ -85,8 +92,8 @@ public class NetworkStatsHistory implements Parcelable { case VERSION_INIT: { bucketDuration = in.readLong(); bucketStart = readLongArray(in); rx = readLongArray(in); tx = readLongArray(in); rxBytes = readLongArray(in); txBytes = readLongArray(in); bucketCount = bucketStart.length; break; } Loading @@ -100,8 +107,8 @@ public class NetworkStatsHistory implements Parcelable { out.writeInt(VERSION_INIT); out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rx, bucketCount); writeLongArray(out, tx, bucketCount); writeLongArray(out, rxBytes, bucketCount); writeLongArray(out, txBytes, bucketCount); } /** {@inheritDoc} */ Loading @@ -109,6 +116,26 @@ public class NetworkStatsHistory implements Parcelable { return 0; } public int size() { return bucketCount; } public long getBucketDuration() { return bucketDuration; } /** * Return specific stats entry. */ public Entry getValues(int i, Entry recycle) { final Entry entry = recycle != null ? recycle : new Entry(); entry.bucketStart = bucketStart[i]; entry.bucketDuration = bucketDuration; entry.rxBytes = rxBytes[i]; entry.txBytes = txBytes[i]; return entry; } /** * Record that data traffic occurred in the given time range. Will * distribute across internal buckets, creating new buckets as needed. Loading @@ -135,8 +162,8 @@ public class NetworkStatsHistory implements Parcelable { final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); if (overlap > 0) { this.rx[i] += rx * overlap / duration; this.tx[i] += tx * overlap / duration; this.rxBytes[i] += rx * overlap / duration; this.txBytes[i] += tx * overlap / duration; } } } Loading @@ -149,7 +176,7 @@ public class NetworkStatsHistory implements Parcelable { 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]); recordData(start, end, input.rxBytes[i], input.txBytes[i]); } } Loading Loading @@ -179,8 +206,8 @@ public class NetworkStatsHistory implements Parcelable { if (bucketCount >= bucketStart.length) { final int newLength = Math.max(bucketStart.length, 10) * 3 / 2; bucketStart = Arrays.copyOf(bucketStart, newLength); rx = Arrays.copyOf(rx, newLength); tx = Arrays.copyOf(tx, newLength); rxBytes = Arrays.copyOf(rxBytes, newLength); txBytes = Arrays.copyOf(txBytes, newLength); } // create gap when inserting bucket in middle Loading @@ -189,13 +216,13 @@ public class NetworkStatsHistory implements Parcelable { final int length = bucketCount - index; System.arraycopy(bucketStart, index, bucketStart, dstPos, length); System.arraycopy(rx, index, rx, dstPos, length); System.arraycopy(tx, index, tx, dstPos, length); System.arraycopy(rxBytes, index, rxBytes, dstPos, length); System.arraycopy(txBytes, index, txBytes, dstPos, length); } bucketStart[index] = start; rx[index] = 0; tx[index] = 0; rxBytes[index] = 0; txBytes[index] = 0; bucketCount++; } Loading @@ -216,8 +243,8 @@ public class NetworkStatsHistory implements Parcelable { if (i > 0) { final int length = bucketStart.length; bucketStart = Arrays.copyOfRange(bucketStart, i, length); rx = Arrays.copyOfRange(rx, i, length); tx = Arrays.copyOfRange(tx, i, length); rxBytes = Arrays.copyOfRange(rxBytes, i, length); txBytes = Arrays.copyOfRange(txBytes, i, length); bucketCount -= i; } } Loading @@ -241,8 +268,8 @@ public class NetworkStatsHistory implements Parcelable { 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; rx += this.rxBytes[i] * overlap / bucketDuration; tx += this.txBytes[i] * overlap / bucketDuration; } } Loading Loading @@ -292,8 +319,8 @@ public class NetworkStatsHistory implements Parcelable { for (int i = start; i < bucketCount; i++) { pw.print(prefix); pw.print(" bucketStart="); pw.print(bucketStart[i]); pw.print(" rx="); pw.print(rx[i]); pw.print(" tx="); pw.println(tx[i]); pw.print(" rxBytes="); pw.print(rxBytes[i]); pw.print(" txBytes="); pw.println(txBytes[i]); } } Loading core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java +55 −49 Original line number Diff line number Diff line Loading @@ -54,8 +54,8 @@ public class NetworkStatsHistoryTest extends TestCase { // record data into narrow window to get single bucket stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L); assertEquals(1, stats.bucketCount); assertBucket(stats, 0, 1024L, 2048L); assertEquals(1, stats.size()); assertEntry(stats, 0, 1024L, 2048L); } public void testRecordEqualBuckets() throws Exception { Loading @@ -66,9 +66,9 @@ public class NetworkStatsHistoryTest extends TestCase { final long recordStart = TEST_START + (bucketDuration / 2); stats.recordData(recordStart, recordStart + bucketDuration, 1024L, 128L); assertEquals(2, stats.bucketCount); assertBucket(stats, 0, 512L, 64L); assertBucket(stats, 1, 512L, 64L); assertEquals(2, stats.size()); assertEntry(stats, 0, 512L, 64L); assertEntry(stats, 1, 512L, 64L); } public void testRecordTouchingBuckets() throws Exception { Loading @@ -81,13 +81,13 @@ public class NetworkStatsHistoryTest extends TestCase { final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4); stats.recordData(recordStart, recordEnd, 1000L, 5000L); assertEquals(3, stats.bucketCount); assertEquals(3, stats.size()); // first bucket should have (1/20 of value) assertBucket(stats, 0, 50L, 250L); assertEntry(stats, 0, 50L, 250L); // second bucket should have (15/20 of value) assertBucket(stats, 1, 750L, 3750L); assertEntry(stats, 1, 750L, 3750L); // final bucket should have (4/20 of value) assertBucket(stats, 2, 200L, 1000L); assertEntry(stats, 2, 200L, 1000L); } public void testRecordGapBuckets() throws Exception { Loading @@ -101,9 +101,9 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, 64L, 512L); // we should have two buckets, far apart from each other assertEquals(2, stats.bucketCount); assertBucket(stats, 0, 128L, 256L); assertBucket(stats, 1, 64L, 512L); assertEquals(2, stats.size()); assertEntry(stats, 0, 128L, 256L); assertEntry(stats, 1, 64L, 512L); // now record something in middle, spread across two buckets final long middleStart = TEST_START + DAY_IN_MILLIS; Loading @@ -111,11 +111,11 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(middleStart, middleEnd, 2048L, 2048L); // now should have four buckets, with new record in middle two buckets assertEquals(4, stats.bucketCount); assertBucket(stats, 0, 128L, 256L); assertBucket(stats, 1, 1024L, 1024L); assertBucket(stats, 2, 1024L, 1024L); assertBucket(stats, 3, 64L, 512L); assertEquals(4, stats.size()); assertEntry(stats, 0, 128L, 256L); assertEntry(stats, 1, 1024L, 1024L); assertEntry(stats, 2, 1024L, 1024L); assertEntry(stats, 3, 64L, 512L); } public void testRecordOverlapBuckets() throws Exception { Loading @@ -128,9 +128,9 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(midStart, midStart + HOUR_IN_MILLIS, 1024L, 1024L); // should have two buckets, with some data mixed together assertEquals(2, stats.bucketCount); assertBucket(stats, 0, 768L, 768L); assertBucket(stats, 1, 512L, 512L); assertEquals(2, stats.size()); assertEntry(stats, 0, 768L, 768L); assertEntry(stats, 1, 512L, 512L); } public void testRecordEntireGapIdentical() throws Exception { Loading @@ -154,10 +154,10 @@ public class NetworkStatsHistoryTest extends TestCase { 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); assertEntry(stats, 0, 1000L, 500L); assertEntry(stats, 1, 1000L, 500L); assertEntry(stats, 2, 500L, 250L); assertEntry(stats, 3, 500L, 250L); } public void testRecordEntireOverlapVaryingBuckets() throws Exception { Loading @@ -181,13 +181,13 @@ public class NetworkStatsHistoryTest extends TestCase { 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); 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); // now combine using 15min buckets stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4); Loading @@ -199,10 +199,10 @@ public class NetworkStatsHistoryTest extends TestCase { 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); assertEntry(stats, 0, 200L, 200L); assertEntry(stats, 1, 150L, 150L); assertEntry(stats, 2, 150L, 150L); assertEntry(stats, 3, 150L, 150L); } public void testRemove() throws Exception { Loading @@ -210,28 +210,28 @@ public class NetworkStatsHistoryTest extends TestCase { // record some data across 24 buckets stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L); assertEquals(24, stats.bucketCount); assertEquals(24, stats.size()); // try removing far before buckets; should be no change stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS); assertEquals(24, stats.bucketCount); assertEquals(24, stats.size()); // try removing just moments into first bucket; should be no change // since that bucket contains data beyond the cutoff stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS); assertEquals(24, stats.bucketCount); assertEquals(24, stats.size()); // try removing single bucket stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS); assertEquals(23, stats.bucketCount); assertEquals(23, stats.size()); // try removing multiple buckets stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS)); assertEquals(20, stats.bucketCount); assertEquals(20, stats.size()); // try removing all buckets stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS); assertEquals(0, stats.bucketCount); assertEquals(0, stats.size()); } public void testTotalData() throws Exception { Loading Loading @@ -293,19 +293,25 @@ public class NetworkStatsHistoryTest extends TestCase { private static void assertConsistent(NetworkStatsHistory stats) { // verify timestamps are monotonic for (int i = 1; i < stats.bucketCount; i++) { assertTrue(stats.bucketStart[i - 1] < stats.bucketStart[i]); long lastStart = Long.MIN_VALUE; NetworkStatsHistory.Entry entry = null; for (int i = 0; i < stats.size(); i++) { entry = stats.getValues(i, entry); assertTrue(lastStart < entry.bucketStart); lastStart = entry.bucketStart; } } 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 assertTotalEquals(long[] total, long rxBytes, long txBytes) { assertEquals("unexpected rxBytes", rxBytes, total[0]); assertEquals("unexpected txBytes", txBytes, 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]); private static void assertEntry( 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); } } core/tests/coretests/src/android/net/NetworkStatsTest.java +55 −59 File changed.Preview size limit exceeded, changes collapsed. Show changes services/java/com/android/server/ThrottleService.java +3 −2 Original line number Diff line number Diff line Loading @@ -515,8 +515,9 @@ public class ThrottleService extends IThrottleManager.Stub { mIface, NetworkStats.UID_ALL, NetworkStats.TAG_NONE); if (index != -1) { incRead = stats.rx[index] - mLastRead; incWrite = stats.tx[index] - mLastWrite; final NetworkStats.Entry entry = stats.getValues(index, null); incRead = entry.rxBytes - mLastRead; incWrite = entry.txBytes - mLastWrite; } else { // missing iface, assume stats are 0 Slog.w(TAG, "unable to find stats for iface " + mIface); Loading Loading
core/java/android/net/NetworkStats.java +61 −56 Original line number Diff line number Diff line Loading @@ -43,31 +43,19 @@ public class NetworkStats implements Parcelable { /** {@link #tag} value for without tag. */ public static final int TAG_NONE = 0; // TODO: move public fields to Entry accessors, then undeprecate // TODO: refactor rx/tx to rxBytes/txBytes /** * {@link SystemClock#elapsedRealtime()} timestamp when this data was * generated. */ @Deprecated public final long elapsedRealtime; @Deprecated public int size; @Deprecated public String[] iface; @Deprecated public int[] uid; @Deprecated public int[] tag; @Deprecated public long[] rx; @Deprecated public long[] rxPackets; @Deprecated public long[] tx; @Deprecated public long[] txPackets; private final long elapsedRealtime; private int size; private String[] iface; private int[] uid; private int[] tag; private long[] rxBytes; private long[] rxPackets; private long[] txBytes; private long[] txPackets; public static class Entry { public String iface; Loading @@ -77,6 +65,20 @@ public class NetworkStats implements Parcelable { public long rxPackets; public long txBytes; public long txPackets; public Entry() { } public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { this.iface = iface; this.uid = uid; this.tag = tag; this.rxBytes = rxBytes; this.rxPackets = rxPackets; this.txBytes = txBytes; this.txPackets = txPackets; } } public NetworkStats(long elapsedRealtime, int initialSize) { Loading @@ -85,9 +87,9 @@ public class NetworkStats implements Parcelable { this.iface = new String[initialSize]; this.uid = new int[initialSize]; this.tag = new int[initialSize]; this.rx = new long[initialSize]; this.rxBytes = new long[initialSize]; this.rxPackets = new long[initialSize]; this.tx = new long[initialSize]; this.txBytes = new long[initialSize]; this.txPackets = new long[initialSize]; } Loading @@ -97,23 +99,15 @@ public class NetworkStats implements Parcelable { iface = parcel.createStringArray(); uid = parcel.createIntArray(); tag = parcel.createIntArray(); rx = parcel.createLongArray(); rxBytes = parcel.createLongArray(); rxPackets = parcel.createLongArray(); tx = parcel.createLongArray(); txBytes = parcel.createLongArray(); txPackets = parcel.createLongArray(); } /** * Add new stats entry with given values. */ public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) { final Entry entry = new Entry(); entry.iface = iface; entry.uid = uid; entry.tag = tag; entry.rxBytes = rx; entry.txBytes = tx; return addValues(entry); public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { return addValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets)); } /** Loading @@ -126,18 +120,18 @@ public class NetworkStats implements Parcelable { iface = Arrays.copyOf(iface, newLength); uid = Arrays.copyOf(uid, newLength); tag = Arrays.copyOf(tag, newLength); rx = Arrays.copyOf(rx, newLength); rxBytes = Arrays.copyOf(rxBytes, newLength); rxPackets = Arrays.copyOf(rxPackets, newLength); tx = Arrays.copyOf(tx, newLength); txBytes = Arrays.copyOf(txBytes, newLength); txPackets = Arrays.copyOf(txPackets, newLength); } iface[size] = entry.iface; uid[size] = entry.uid; tag[size] = entry.tag; rx[size] = entry.rxBytes; rxBytes[size] = entry.rxBytes; rxPackets[size] = entry.rxPackets; tx[size] = entry.txBytes; txBytes[size] = entry.txBytes; txPackets[size] = entry.txPackets; size++; Loading @@ -152,9 +146,9 @@ public class NetworkStats implements Parcelable { entry.iface = iface[i]; entry.uid = uid[i]; entry.tag = tag[i]; entry.rxBytes = rx[i]; entry.rxBytes = rxBytes[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txBytes = txBytes[i]; entry.txPackets = txPackets[i]; return entry; } Loading @@ -167,20 +161,31 @@ public class NetworkStats implements Parcelable { return size; } // @VisibleForTesting public int internalSize() { return iface.length; } public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { return combineValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets)); } /** * Combine given values with an existing row, or create a new row if * {@link #findIndex(String, int, int)} is unable to find match. Can also be * used to subtract values from existing rows. */ public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) { // TODO: extent to accept rxPackets/txPackets final int i = findIndex(iface, uid, tag); public NetworkStats combineValues(Entry entry) { final int i = findIndex(entry.iface, entry.uid, entry.tag); if (i == -1) { // only create new entry when positive contribution addEntry(iface, uid, tag, rx, tx); addValues(entry); } else { this.rx[i] += rx; this.tx[i] += tx; rxBytes[i] += entry.rxBytes; rxPackets[i] += entry.rxPackets; txBytes[i] += entry.txBytes; txPackets[i] += entry.txPackets; } return this; } Loading Loading @@ -280,15 +285,15 @@ public class NetworkStats implements Parcelable { final int j = value.findIndex(entry.iface, entry.uid, entry.tag); if (j == -1) { // newly appearing row, return entire value entry.rxBytes = rx[i]; entry.rxBytes = rxBytes[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = tx[i]; entry.txBytes = txBytes[i]; entry.txPackets = txPackets[i]; } else { // existing row, subtract remote value entry.rxBytes = rx[i] - value.rx[j]; entry.rxBytes = rxBytes[i] - value.rxBytes[j]; entry.rxPackets = rxPackets[i] - value.rxPackets[j]; entry.txBytes = tx[i] - value.tx[j]; entry.txBytes = txBytes[i] - value.txBytes[j]; entry.txPackets = txPackets[i] - value.txPackets[j]; if (enforceMonotonic && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 Loading Loading @@ -321,9 +326,9 @@ public class NetworkStats implements Parcelable { pw.print(" iface="); pw.print(iface[i]); pw.print(" uid="); pw.print(uid[i]); pw.print(" tag="); pw.print(tag[i]); pw.print(" rxBytes="); pw.print(rx[i]); pw.print(" rxBytes="); pw.print(rxBytes[i]); pw.print(" rxPackets="); pw.print(rxPackets[i]); pw.print(" txBytes="); pw.print(tx[i]); pw.print(" txBytes="); pw.print(txBytes[i]); pw.print(" txPackets="); pw.println(txPackets[i]); } } Loading @@ -347,9 +352,9 @@ public class NetworkStats implements Parcelable { dest.writeStringArray(iface); dest.writeIntArray(uid); dest.writeIntArray(tag); dest.writeLongArray(rx); dest.writeLongArray(rxBytes); dest.writeLongArray(rxPackets); dest.writeLongArray(tx); dest.writeLongArray(txBytes); dest.writeLongArray(txPackets); } Loading
core/java/android/net/NetworkStatsHistory.java +59 −32 Original line number Diff line number Diff line Loading @@ -43,13 +43,20 @@ public class NetworkStatsHistory implements Parcelable { private static final int VERSION_INIT = 1; // TODO: teach about varint encoding to use less disk space // TODO: extend to record rxPackets/txPackets public final long bucketDuration; private final long bucketDuration; private int bucketCount; private long[] bucketStart; private long[] rxBytes; private long[] txBytes; public int bucketCount; public long[] bucketStart; public long[] rx; public long[] tx; public static class Entry { public long bucketStart; public long bucketDuration; public long rxBytes; public long txBytes; } public NetworkStatsHistory(long bucketDuration) { this(bucketDuration, 10); Loading @@ -58,16 +65,16 @@ public class NetworkStatsHistory implements Parcelable { public NetworkStatsHistory(long bucketDuration, int initialSize) { this.bucketDuration = bucketDuration; bucketStart = new long[initialSize]; rx = new long[initialSize]; tx = new long[initialSize]; rxBytes = new long[initialSize]; txBytes = new long[initialSize]; bucketCount = 0; } public NetworkStatsHistory(Parcel in) { bucketDuration = in.readLong(); bucketStart = readLongArray(in); rx = in.createLongArray(); tx = in.createLongArray(); rxBytes = in.createLongArray(); txBytes = in.createLongArray(); bucketCount = bucketStart.length; } Loading @@ -75,8 +82,8 @@ public class NetworkStatsHistory implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rx, bucketCount); writeLongArray(out, tx, bucketCount); writeLongArray(out, rxBytes, bucketCount); writeLongArray(out, txBytes, bucketCount); } public NetworkStatsHistory(DataInputStream in) throws IOException { Loading @@ -85,8 +92,8 @@ public class NetworkStatsHistory implements Parcelable { case VERSION_INIT: { bucketDuration = in.readLong(); bucketStart = readLongArray(in); rx = readLongArray(in); tx = readLongArray(in); rxBytes = readLongArray(in); txBytes = readLongArray(in); bucketCount = bucketStart.length; break; } Loading @@ -100,8 +107,8 @@ public class NetworkStatsHistory implements Parcelable { out.writeInt(VERSION_INIT); out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rx, bucketCount); writeLongArray(out, tx, bucketCount); writeLongArray(out, rxBytes, bucketCount); writeLongArray(out, txBytes, bucketCount); } /** {@inheritDoc} */ Loading @@ -109,6 +116,26 @@ public class NetworkStatsHistory implements Parcelable { return 0; } public int size() { return bucketCount; } public long getBucketDuration() { return bucketDuration; } /** * Return specific stats entry. */ public Entry getValues(int i, Entry recycle) { final Entry entry = recycle != null ? recycle : new Entry(); entry.bucketStart = bucketStart[i]; entry.bucketDuration = bucketDuration; entry.rxBytes = rxBytes[i]; entry.txBytes = txBytes[i]; return entry; } /** * Record that data traffic occurred in the given time range. Will * distribute across internal buckets, creating new buckets as needed. Loading @@ -135,8 +162,8 @@ public class NetworkStatsHistory implements Parcelable { final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); if (overlap > 0) { this.rx[i] += rx * overlap / duration; this.tx[i] += tx * overlap / duration; this.rxBytes[i] += rx * overlap / duration; this.txBytes[i] += tx * overlap / duration; } } } Loading @@ -149,7 +176,7 @@ public class NetworkStatsHistory implements Parcelable { 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]); recordData(start, end, input.rxBytes[i], input.txBytes[i]); } } Loading Loading @@ -179,8 +206,8 @@ public class NetworkStatsHistory implements Parcelable { if (bucketCount >= bucketStart.length) { final int newLength = Math.max(bucketStart.length, 10) * 3 / 2; bucketStart = Arrays.copyOf(bucketStart, newLength); rx = Arrays.copyOf(rx, newLength); tx = Arrays.copyOf(tx, newLength); rxBytes = Arrays.copyOf(rxBytes, newLength); txBytes = Arrays.copyOf(txBytes, newLength); } // create gap when inserting bucket in middle Loading @@ -189,13 +216,13 @@ public class NetworkStatsHistory implements Parcelable { final int length = bucketCount - index; System.arraycopy(bucketStart, index, bucketStart, dstPos, length); System.arraycopy(rx, index, rx, dstPos, length); System.arraycopy(tx, index, tx, dstPos, length); System.arraycopy(rxBytes, index, rxBytes, dstPos, length); System.arraycopy(txBytes, index, txBytes, dstPos, length); } bucketStart[index] = start; rx[index] = 0; tx[index] = 0; rxBytes[index] = 0; txBytes[index] = 0; bucketCount++; } Loading @@ -216,8 +243,8 @@ public class NetworkStatsHistory implements Parcelable { if (i > 0) { final int length = bucketStart.length; bucketStart = Arrays.copyOfRange(bucketStart, i, length); rx = Arrays.copyOfRange(rx, i, length); tx = Arrays.copyOfRange(tx, i, length); rxBytes = Arrays.copyOfRange(rxBytes, i, length); txBytes = Arrays.copyOfRange(txBytes, i, length); bucketCount -= i; } } Loading @@ -241,8 +268,8 @@ public class NetworkStatsHistory implements Parcelable { 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; rx += this.rxBytes[i] * overlap / bucketDuration; tx += this.txBytes[i] * overlap / bucketDuration; } } Loading Loading @@ -292,8 +319,8 @@ public class NetworkStatsHistory implements Parcelable { for (int i = start; i < bucketCount; i++) { pw.print(prefix); pw.print(" bucketStart="); pw.print(bucketStart[i]); pw.print(" rx="); pw.print(rx[i]); pw.print(" tx="); pw.println(tx[i]); pw.print(" rxBytes="); pw.print(rxBytes[i]); pw.print(" txBytes="); pw.println(txBytes[i]); } } Loading
core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java +55 −49 Original line number Diff line number Diff line Loading @@ -54,8 +54,8 @@ public class NetworkStatsHistoryTest extends TestCase { // record data into narrow window to get single bucket stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L); assertEquals(1, stats.bucketCount); assertBucket(stats, 0, 1024L, 2048L); assertEquals(1, stats.size()); assertEntry(stats, 0, 1024L, 2048L); } public void testRecordEqualBuckets() throws Exception { Loading @@ -66,9 +66,9 @@ public class NetworkStatsHistoryTest extends TestCase { final long recordStart = TEST_START + (bucketDuration / 2); stats.recordData(recordStart, recordStart + bucketDuration, 1024L, 128L); assertEquals(2, stats.bucketCount); assertBucket(stats, 0, 512L, 64L); assertBucket(stats, 1, 512L, 64L); assertEquals(2, stats.size()); assertEntry(stats, 0, 512L, 64L); assertEntry(stats, 1, 512L, 64L); } public void testRecordTouchingBuckets() throws Exception { Loading @@ -81,13 +81,13 @@ public class NetworkStatsHistoryTest extends TestCase { final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4); stats.recordData(recordStart, recordEnd, 1000L, 5000L); assertEquals(3, stats.bucketCount); assertEquals(3, stats.size()); // first bucket should have (1/20 of value) assertBucket(stats, 0, 50L, 250L); assertEntry(stats, 0, 50L, 250L); // second bucket should have (15/20 of value) assertBucket(stats, 1, 750L, 3750L); assertEntry(stats, 1, 750L, 3750L); // final bucket should have (4/20 of value) assertBucket(stats, 2, 200L, 1000L); assertEntry(stats, 2, 200L, 1000L); } public void testRecordGapBuckets() throws Exception { Loading @@ -101,9 +101,9 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, 64L, 512L); // we should have two buckets, far apart from each other assertEquals(2, stats.bucketCount); assertBucket(stats, 0, 128L, 256L); assertBucket(stats, 1, 64L, 512L); assertEquals(2, stats.size()); assertEntry(stats, 0, 128L, 256L); assertEntry(stats, 1, 64L, 512L); // now record something in middle, spread across two buckets final long middleStart = TEST_START + DAY_IN_MILLIS; Loading @@ -111,11 +111,11 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(middleStart, middleEnd, 2048L, 2048L); // now should have four buckets, with new record in middle two buckets assertEquals(4, stats.bucketCount); assertBucket(stats, 0, 128L, 256L); assertBucket(stats, 1, 1024L, 1024L); assertBucket(stats, 2, 1024L, 1024L); assertBucket(stats, 3, 64L, 512L); assertEquals(4, stats.size()); assertEntry(stats, 0, 128L, 256L); assertEntry(stats, 1, 1024L, 1024L); assertEntry(stats, 2, 1024L, 1024L); assertEntry(stats, 3, 64L, 512L); } public void testRecordOverlapBuckets() throws Exception { Loading @@ -128,9 +128,9 @@ public class NetworkStatsHistoryTest extends TestCase { stats.recordData(midStart, midStart + HOUR_IN_MILLIS, 1024L, 1024L); // should have two buckets, with some data mixed together assertEquals(2, stats.bucketCount); assertBucket(stats, 0, 768L, 768L); assertBucket(stats, 1, 512L, 512L); assertEquals(2, stats.size()); assertEntry(stats, 0, 768L, 768L); assertEntry(stats, 1, 512L, 512L); } public void testRecordEntireGapIdentical() throws Exception { Loading @@ -154,10 +154,10 @@ public class NetworkStatsHistoryTest extends TestCase { 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); assertEntry(stats, 0, 1000L, 500L); assertEntry(stats, 1, 1000L, 500L); assertEntry(stats, 2, 500L, 250L); assertEntry(stats, 3, 500L, 250L); } public void testRecordEntireOverlapVaryingBuckets() throws Exception { Loading @@ -181,13 +181,13 @@ public class NetworkStatsHistoryTest extends TestCase { 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); 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); // now combine using 15min buckets stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4); Loading @@ -199,10 +199,10 @@ public class NetworkStatsHistoryTest extends TestCase { 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); assertEntry(stats, 0, 200L, 200L); assertEntry(stats, 1, 150L, 150L); assertEntry(stats, 2, 150L, 150L); assertEntry(stats, 3, 150L, 150L); } public void testRemove() throws Exception { Loading @@ -210,28 +210,28 @@ public class NetworkStatsHistoryTest extends TestCase { // record some data across 24 buckets stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L); assertEquals(24, stats.bucketCount); assertEquals(24, stats.size()); // try removing far before buckets; should be no change stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS); assertEquals(24, stats.bucketCount); assertEquals(24, stats.size()); // try removing just moments into first bucket; should be no change // since that bucket contains data beyond the cutoff stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS); assertEquals(24, stats.bucketCount); assertEquals(24, stats.size()); // try removing single bucket stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS); assertEquals(23, stats.bucketCount); assertEquals(23, stats.size()); // try removing multiple buckets stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS)); assertEquals(20, stats.bucketCount); assertEquals(20, stats.size()); // try removing all buckets stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS); assertEquals(0, stats.bucketCount); assertEquals(0, stats.size()); } public void testTotalData() throws Exception { Loading Loading @@ -293,19 +293,25 @@ public class NetworkStatsHistoryTest extends TestCase { private static void assertConsistent(NetworkStatsHistory stats) { // verify timestamps are monotonic for (int i = 1; i < stats.bucketCount; i++) { assertTrue(stats.bucketStart[i - 1] < stats.bucketStart[i]); long lastStart = Long.MIN_VALUE; NetworkStatsHistory.Entry entry = null; for (int i = 0; i < stats.size(); i++) { entry = stats.getValues(i, entry); assertTrue(lastStart < entry.bucketStart); lastStart = entry.bucketStart; } } 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 assertTotalEquals(long[] total, long rxBytes, long txBytes) { assertEquals("unexpected rxBytes", rxBytes, total[0]); assertEquals("unexpected txBytes", txBytes, 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]); private static void assertEntry( 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); } }
core/tests/coretests/src/android/net/NetworkStatsTest.java +55 −59 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/java/com/android/server/ThrottleService.java +3 −2 Original line number Diff line number Diff line Loading @@ -515,8 +515,9 @@ public class ThrottleService extends IThrottleManager.Stub { mIface, NetworkStats.UID_ALL, NetworkStats.TAG_NONE); if (index != -1) { incRead = stats.rx[index] - mLastRead; incWrite = stats.tx[index] - mLastWrite; final NetworkStats.Entry entry = stats.getValues(index, null); incRead = entry.rxBytes - mLastRead; incWrite = entry.txBytes - mLastWrite; } else { // missing iface, assume stats are 0 Slog.w(TAG, "unable to find stats for iface " + mIface); Loading