From 63d27a9233fed934340231f438493746084a681d Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 3 Aug 2011 17:04:22 -0700 Subject: [PATCH] Network stats with varint, omit parcel fields. Persist NetworkStatsHistory using variable-length encoding; since most buckets have small numbers, we can encode them tighter. Initial test showed 44% space savings. Also persist packet and operation counters. Let NetworkStatsHistory consumers request which fields they actually need to reduce parcel overhead. Tests for verify varint and history field requests, also verify end- to-end by persisting history into byte[] and restoring. Expose bandwidth control enabled state. Extend random generation to create packet and operation counts. Moved operation counts to long. Fix bug that miscalculated bytes since last persist, which would cause partial stats loss when battery pulled. Bug: 4581977, 5023706, 5023635, 5096903 Change-Id: If61e89f681ffa11fe5711471fd9f7c238d3d37b0 --- .../android/net/INetworkStatsService.aidl | 4 +- core/java/android/net/NetworkStats.java | 23 +- .../java/android/net/NetworkStatsHistory.java | 296 +++++++++++------- .../android/os/INetworkManagementService.aidl | 5 + .../android/net/NetworkStatsHistoryTest.java | 176 +++++++++-- .../server/NetworkManagementService.java | 7 + .../server/net/NetworkStatsService.java | 59 ++-- .../server/NetworkStatsServiceTest.java | 10 +- 8 files changed, 409 insertions(+), 171 deletions(-) diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index c41d182cfa114..b65506c411e80 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -24,9 +24,9 @@ import android.net.NetworkTemplate; interface INetworkStatsService { /** Return historical network layer stats for traffic that matches template. */ - NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template); + NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields); /** Return historical network layer stats for specific UID traffic that matches template. */ - NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag); + NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag, int fields); /** Return network layer usage summary for traffic that matches template. */ NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end); diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 0e8e7fc5ecfea..f2fcb8fad852b 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -58,7 +58,7 @@ public class NetworkStats implements Parcelable { private long[] rxPackets; private long[] txBytes; private long[] txPackets; - private int[] operations; + private long[] operations; public static class Entry { public String iface; @@ -68,13 +68,18 @@ public class NetworkStats implements Parcelable { public long rxPackets; public long txBytes; public long txPackets; - public int operations; + public long operations; public Entry() { + this(IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L); + } + + public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { + this(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, operations); } public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, - long txPackets, int operations) { + long txPackets, long operations) { this.iface = iface; this.uid = uid; this.tag = tag; @@ -96,7 +101,7 @@ public class NetworkStats implements Parcelable { this.rxPackets = new long[initialSize]; this.txBytes = new long[initialSize]; this.txPackets = new long[initialSize]; - this.operations = new int[initialSize]; + this.operations = new long[initialSize]; } public NetworkStats(Parcel parcel) { @@ -109,7 +114,7 @@ public class NetworkStats implements Parcelable { rxPackets = parcel.createLongArray(); txBytes = parcel.createLongArray(); txPackets = parcel.createLongArray(); - operations = parcel.createIntArray(); + operations = parcel.createLongArray(); } /** {@inheritDoc} */ @@ -123,16 +128,16 @@ public class NetworkStats implements Parcelable { dest.writeLongArray(rxPackets); dest.writeLongArray(txBytes); dest.writeLongArray(txPackets); - dest.writeIntArray(operations); + dest.writeLongArray(operations); } public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { - return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0); + return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0L); } public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, - long txBytes, long txPackets, int operations) { + long txBytes, long txPackets, long operations) { return addValues( new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); } @@ -197,7 +202,7 @@ public class NetworkStats implements Parcelable { } public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets, - long txBytes, long txPackets, int operations) { + long txBytes, long txPackets, long operations) { return combineValues( new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); } diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index 7a4b811763aa9..c917af919e28b 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -19,11 +19,11 @@ package android.net; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStatsHistory.DataStreamUtils.readLongArray; -import static android.net.NetworkStatsHistory.DataStreamUtils.writeLongArray; -import static android.net.NetworkStatsHistory.ParcelUtils.readIntArray; +import static android.net.NetworkStatsHistory.DataStreamUtils.readFullLongArray; +import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLongArray; +import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray; +import static android.net.NetworkStatsHistory.Entry.UNKNOWN; import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray; -import static android.net.NetworkStatsHistory.ParcelUtils.writeIntArray; import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray; import android.os.Parcel; @@ -51,42 +51,53 @@ import java.util.Random; */ public class NetworkStatsHistory implements Parcelable { private static final int VERSION_INIT = 1; + private static final int VERSION_ADD_PACKETS = 2; - // TODO: teach about varint encoding to use less disk space - // TODO: teach about omitting entire fields to reduce parcel pressure - // TODO: persist/restore packet and operation counts + public static final int FIELD_RX_BYTES = 0x01; + public static final int FIELD_RX_PACKETS = 0x02; + public static final int FIELD_TX_BYTES = 0x04; + public static final int FIELD_TX_PACKETS = 0x08; + public static final int FIELD_OPERATIONS = 0x10; - private final long bucketDuration; + public static final int FIELD_ALL = 0xFFFFFFFF; + + private long bucketDuration; private int bucketCount; private long[] bucketStart; private long[] rxBytes; private long[] rxPackets; private long[] txBytes; private long[] txPackets; - private int[] operations; + private long[] operations; public static class Entry { + public static final long UNKNOWN = -1; + public long bucketStart; public long bucketDuration; public long rxBytes; public long rxPackets; public long txBytes; public long txPackets; - public int operations; + public long operations; } public NetworkStatsHistory(long bucketDuration) { - this(bucketDuration, 10); + this(bucketDuration, 10, FIELD_ALL); } public NetworkStatsHistory(long bucketDuration, int initialSize) { + this(bucketDuration, initialSize, FIELD_ALL); + } + + public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) { this.bucketDuration = bucketDuration; bucketStart = new long[initialSize]; - rxBytes = new long[initialSize]; - rxPackets = new long[initialSize]; - txBytes = new long[initialSize]; - txPackets = new long[initialSize]; - operations = new int[initialSize]; + if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize]; + if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize]; + if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize]; + if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize]; + if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize]; bucketCount = 0; } @@ -97,7 +108,7 @@ public class NetworkStatsHistory implements Parcelable { rxPackets = readLongArray(in); txBytes = readLongArray(in); txPackets = readLongArray(in); - operations = readIntArray(in); + operations = readLongArray(in); bucketCount = bucketStart.length; } @@ -109,21 +120,31 @@ public class NetworkStatsHistory implements Parcelable { writeLongArray(out, rxPackets, bucketCount); writeLongArray(out, txBytes, bucketCount); writeLongArray(out, txPackets, bucketCount); - writeIntArray(out, operations, bucketCount); + writeLongArray(out, operations, bucketCount); } public NetworkStatsHistory(DataInputStream in) throws IOException { - // TODO: read packet and operation counts final int version = in.readInt(); switch (version) { case VERSION_INIT: { bucketDuration = in.readLong(); - bucketStart = readLongArray(in); - rxBytes = readLongArray(in); + bucketStart = readFullLongArray(in); + rxBytes = readFullLongArray(in); rxPackets = new long[bucketStart.length]; - txBytes = readLongArray(in); + txBytes = readFullLongArray(in); txPackets = new long[bucketStart.length]; - operations = new int[bucketStart.length]; + operations = new long[bucketStart.length]; + bucketCount = bucketStart.length; + break; + } + case VERSION_ADD_PACKETS: { + bucketDuration = in.readLong(); + bucketStart = readVarLongArray(in); + rxBytes = readVarLongArray(in); + rxPackets = readVarLongArray(in); + txBytes = readVarLongArray(in); + txPackets = readVarLongArray(in); + operations = readVarLongArray(in); bucketCount = bucketStart.length; break; } @@ -134,12 +155,14 @@ public class NetworkStatsHistory implements Parcelable { } public void writeToStream(DataOutputStream out) throws IOException { - // TODO: write packet and operation counts - out.writeInt(VERSION_INIT); + out.writeInt(VERSION_ADD_PACKETS); out.writeLong(bucketDuration); - writeLongArray(out, bucketStart, bucketCount); - writeLongArray(out, rxBytes, bucketCount); - writeLongArray(out, txBytes, bucketCount); + writeVarLongArray(out, bucketStart, bucketCount); + writeVarLongArray(out, rxBytes, bucketCount); + writeVarLongArray(out, rxPackets, bucketCount); + writeVarLongArray(out, txBytes, bucketCount); + writeVarLongArray(out, txPackets, bucketCount); + writeVarLongArray(out, operations, bucketCount); } /** {@inheritDoc} */ @@ -178,11 +201,11 @@ public class NetworkStatsHistory implements Parcelable { final Entry entry = recycle != null ? recycle : new Entry(); entry.bucketStart = bucketStart[i]; entry.bucketDuration = bucketDuration; - entry.rxBytes = rxBytes[i]; - entry.rxPackets = rxPackets[i]; - entry.txBytes = txBytes[i]; - entry.txPackets = txPackets[i]; - entry.operations = operations[i]; + entry.rxBytes = getLong(rxBytes, i, UNKNOWN); + entry.rxPackets = getLong(rxPackets, i, UNKNOWN); + entry.txBytes = getLong(txBytes, i, UNKNOWN); + entry.txPackets = getLong(txPackets, i, UNKNOWN); + entry.operations = getLong(operations, i, UNKNOWN); return entry; } @@ -193,7 +216,7 @@ public class NetworkStatsHistory implements Parcelable { @Deprecated public void recordData(long start, long end, long rxBytes, long txBytes) { recordData(start, end, - new NetworkStats.Entry(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0)); + new NetworkStats.Entry(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L)); } /** @@ -230,11 +253,11 @@ public class NetworkStatsHistory implements Parcelable { final long fracTxPackets = entry.txPackets * overlap / duration; final int fracOperations = (int) (entry.operations * overlap / duration); - rxBytes[i] += fracRxBytes; entry.rxBytes -= fracRxBytes; - rxPackets[i] += fracRxPackets; entry.rxPackets -= fracRxPackets; - txBytes[i] += fracTxBytes; entry.txBytes -= fracTxBytes; - txPackets[i] += fracTxPackets; entry.txPackets -= fracTxPackets; - operations[i] += fracOperations; entry.operations -= fracOperations; + addLong(rxBytes, i, fracRxBytes); entry.rxBytes -= fracRxBytes; + addLong(rxPackets, i, fracRxPackets); entry.rxPackets -= fracRxPackets; + addLong(txBytes, i, fracTxBytes); entry.txBytes -= fracTxBytes; + addLong(txPackets, i, fracTxPackets); entry.txPackets -= fracTxPackets; + addLong(operations, i, fracOperations); entry.operations -= fracOperations; duration -= overlap; } @@ -246,16 +269,16 @@ public class NetworkStatsHistory implements Parcelable { */ public void recordEntireHistory(NetworkStatsHistory input) { final NetworkStats.Entry entry = new NetworkStats.Entry( - IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0); + IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L); for (int i = 0; i < input.bucketCount; i++) { final long start = input.bucketStart[i]; final long end = start + input.bucketDuration; - entry.rxBytes = input.rxBytes[i]; - entry.rxPackets = input.rxPackets[i]; - entry.txBytes = input.txBytes[i]; - entry.txPackets = input.txPackets[i]; - entry.operations = input.operations[i]; + entry.rxBytes = getLong(input.rxBytes, i, 0L); + entry.rxPackets = getLong(input.rxPackets, i, 0L); + entry.txBytes = getLong(input.txBytes, i, 0L); + entry.txPackets = getLong(input.txPackets, i, 0L); + entry.operations = getLong(input.operations, i, 0L); recordData(start, end, entry); } @@ -287,11 +310,11 @@ public class NetworkStatsHistory implements Parcelable { if (bucketCount >= bucketStart.length) { final int newLength = Math.max(bucketStart.length, 10) * 3 / 2; bucketStart = Arrays.copyOf(bucketStart, newLength); - rxBytes = Arrays.copyOf(rxBytes, newLength); - rxPackets = Arrays.copyOf(rxPackets, newLength); - txBytes = Arrays.copyOf(txBytes, newLength); - txPackets = Arrays.copyOf(txPackets, newLength); - operations = Arrays.copyOf(operations, newLength); + if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength); + if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength); + if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength); + if (txPackets != null) txPackets = Arrays.copyOf(txPackets, newLength); + if (operations != null) operations = Arrays.copyOf(operations, newLength); } // create gap when inserting bucket in middle @@ -300,19 +323,19 @@ public class NetworkStatsHistory implements Parcelable { final int length = bucketCount - index; System.arraycopy(bucketStart, index, bucketStart, dstPos, length); - System.arraycopy(rxBytes, index, rxBytes, dstPos, length); - System.arraycopy(rxPackets, index, rxPackets, dstPos, length); - System.arraycopy(txBytes, index, txBytes, dstPos, length); - System.arraycopy(txPackets, index, txPackets, dstPos, length); - System.arraycopy(operations, index, operations, dstPos, length); + if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length); + if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length); + if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length); + if (txPackets != null) System.arraycopy(txPackets, index, txPackets, dstPos, length); + if (operations != null) System.arraycopy(operations, index, operations, dstPos, length); } bucketStart[index] = start; - rxBytes[index] = 0; - rxPackets[index] = 0; - txBytes[index] = 0; - txPackets[index] = 0; - operations[index] = 0; + setLong(rxBytes, index, 0L); + setLong(rxPackets, index, 0L); + setLong(txBytes, index, 0L); + setLong(txPackets, index, 0L); + setLong(operations, index, 0L); bucketCount++; } @@ -333,11 +356,11 @@ public class NetworkStatsHistory implements Parcelable { if (i > 0) { final int length = bucketStart.length; bucketStart = Arrays.copyOfRange(bucketStart, i, length); - rxBytes = Arrays.copyOfRange(rxBytes, i, length); - rxPackets = Arrays.copyOfRange(rxPackets, i, length); - txBytes = Arrays.copyOfRange(txBytes, i, length); - txPackets = Arrays.copyOfRange(txPackets, i, length); - operations = Arrays.copyOfRange(operations, i, length); + if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length); + if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length); + if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length); + if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length); + if (operations != null) operations = Arrays.copyOfRange(operations, i, length); bucketCount -= i; } } @@ -358,11 +381,11 @@ public class NetworkStatsHistory implements Parcelable { final Entry entry = recycle != null ? recycle : new Entry(); entry.bucketStart = start; entry.bucketDuration = end - start; - entry.rxBytes = 0; - entry.rxPackets = 0; - entry.txBytes = 0; - entry.txPackets = 0; - entry.operations = 0; + entry.rxBytes = rxBytes != null ? 0 : UNKNOWN; + entry.rxPackets = rxPackets != null ? 0 : UNKNOWN; + entry.txBytes = txBytes != null ? 0 : UNKNOWN; + entry.txPackets = txPackets != null ? 0 : UNKNOWN; + entry.operations = operations != null ? 0 : UNKNOWN; for (int i = bucketCount - 1; i >= 0; i--) { final long curStart = bucketStart[i]; @@ -380,11 +403,11 @@ public class NetworkStatsHistory implements Parcelable { if (overlap <= 0) continue; // integer math each time is faster than floating point - entry.rxBytes += rxBytes[i] * overlap / bucketDuration; - entry.rxPackets += rxPackets[i] * overlap / bucketDuration; - entry.txBytes += txBytes[i] * overlap / bucketDuration; - entry.txPackets += txPackets[i] * overlap / bucketDuration; - entry.operations += operations[i] * overlap / bucketDuration; + if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration; + if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration; + if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration; + if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration; + if (operations != null) entry.operations += operations[i] * overlap / bucketDuration; } return entry; @@ -394,19 +417,29 @@ public class NetworkStatsHistory implements Parcelable { * @deprecated only for temporary testing */ @Deprecated - public void generateRandom(long start, long end, long rx, long tx) { + public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes, + long txPackets, long operations) { ensureBuckets(start, end); final NetworkStats.Entry entry = new NetworkStats.Entry( - IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0); + IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L); final Random r = new Random(); - while (rx > 1024 && tx > 1024) { + while (rxBytes > 1024 && rxPackets > 128 && txBytes > 1024 && txPackets > 128 + && operations > 32) { final long curStart = randomLong(r, start, end); final long curEnd = randomLong(r, curStart, end); - entry.rxBytes = randomLong(r, 0, rx); - entry.txBytes = randomLong(r, 0, tx); - rx -= entry.rxBytes; - tx -= entry.txBytes; + + entry.rxBytes = randomLong(r, 0, rxBytes); + entry.rxPackets = randomLong(r, 0, rxPackets); + entry.txBytes = randomLong(r, 0, txBytes); + entry.txPackets = randomLong(r, 0, txPackets); + entry.operations = randomLong(r, 0, operations); + + rxBytes -= entry.rxBytes; + rxPackets -= entry.rxPackets; + txBytes -= entry.txBytes; + txPackets -= entry.txPackets; + operations -= entry.operations; recordData(curStart, curEnd, entry); } @@ -429,11 +462,12 @@ public class NetworkStatsHistory implements Parcelable { for (int i = start; i < bucketCount; i++) { pw.print(prefix); pw.print(" bucketStart="); pw.print(bucketStart[i]); - pw.print(" rxBytes="); pw.print(rxBytes[i]); - pw.print(" rxPackets="); pw.print(rxPackets[i]); - pw.print(" txBytes="); pw.print(txBytes[i]); - pw.print(" txPackets="); pw.print(txPackets[i]); - pw.print(" operations="); pw.println(operations[i]); + if (rxBytes != null) pw.print(" rxBytes="); pw.print(rxBytes[i]); + if (rxPackets != null) pw.print(" rxPackets="); pw.print(rxPackets[i]); + if (txBytes != null) pw.print(" txBytes="); pw.print(txBytes[i]); + if (txPackets != null) pw.print(" txPackets="); pw.print(txPackets[i]); + if (operations != null) pw.print(" operations="); pw.print(operations[i]); + pw.println(); } } @@ -454,12 +488,25 @@ public class NetworkStatsHistory implements Parcelable { } }; + private static long getLong(long[] array, int i, long value) { + return array != null ? array[i] : value; + } + + private static void setLong(long[] array, int i, long value) { + if (array != null) array[i] = value; + } + + private static void addLong(long[] array, int i, long value) { + if (array != null) array[i] += value; + } + /** * Utility methods for interacting with {@link DataInputStream} and * {@link DataOutputStream}, mostly dealing with writing partial arrays. */ public static class DataStreamUtils { - public static long[] readLongArray(DataInputStream in) throws IOException { + @Deprecated + public static long[] readFullLongArray(DataInputStream in) throws IOException { final int size = in.readInt(); final long[] values = new long[size]; for (int i = 0; i < values.length; i++) { @@ -468,14 +515,59 @@ public class NetworkStatsHistory implements Parcelable { return values; } - public static void writeLongArray(DataOutputStream out, long[] values, int size) + /** + * Read variable-length {@link Long} using protobuf-style approach. + */ + public static long readVarLong(DataInputStream in) throws IOException { + int shift = 0; + long result = 0; + while (shift < 64) { + byte b = in.readByte(); + result |= (long) (b & 0x7F) << shift; + if ((b & 0x80) == 0) + return result; + shift += 7; + } + throw new ProtocolException("malformed long"); + } + + /** + * Write variable-length {@link Long} using protobuf-style approach. + */ + public static void writeVarLong(DataOutputStream out, long value) throws IOException { + while (true) { + if ((value & ~0x7FL) == 0) { + out.writeByte((int) value); + return; + } else { + out.writeByte(((int) value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + public static long[] readVarLongArray(DataInputStream in) throws IOException { + final int size = in.readInt(); + if (size == -1) return null; + final long[] values = new long[size]; + for (int i = 0; i < values.length; i++) { + values[i] = readVarLong(in); + } + return values; + } + + public static void writeVarLongArray(DataOutputStream out, long[] values, int size) throws IOException { + if (values == null) { + out.writeInt(-1); + return; + } if (size > values.length) { throw new IllegalArgumentException("size larger than length"); } out.writeInt(size); for (int i = 0; i < size; i++) { - out.writeLong(values[i]); + writeVarLong(out, values[i]); } } } @@ -487,6 +579,7 @@ public class NetworkStatsHistory implements Parcelable { public static class ParcelUtils { public static long[] readLongArray(Parcel in) { final int size = in.readInt(); + if (size == -1) return null; final long[] values = new long[size]; for (int i = 0; i < values.length; i++) { values[i] = in.readLong(); @@ -495,6 +588,10 @@ public class NetworkStatsHistory implements Parcelable { } public static void writeLongArray(Parcel out, long[] values, int size) { + if (values == null) { + out.writeInt(-1); + return; + } if (size > values.length) { throw new IllegalArgumentException("size larger than length"); } @@ -503,25 +600,6 @@ public class NetworkStatsHistory implements Parcelable { out.writeLong(values[i]); } } - - public static int[] readIntArray(Parcel in) { - final int size = in.readInt(); - final int[] values = new int[size]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readInt(); - } - return values; - } - - public static void writeIntArray(Parcel out, int[] values, int size) { - if (size > values.length) { - throw new IllegalArgumentException("size larger than length"); - } - out.writeInt(size); - for (int i = 0; i < size; i++) { - out.writeInt(values[i]); - } - } } } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 3704248e8076c..bc4e00cc4d21d 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -239,6 +239,11 @@ interface INetworkManagementService */ void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces); + /** + * Return status of bandwidth control module. + */ + boolean isBandwidthControlEnabled(); + /** * Configures bandwidth throttling on an interface. */ diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java index 242057cf963dd..4db4ea52d5272 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java @@ -16,6 +16,14 @@ package android.net; +import static android.net.NetworkStatsHistory.FIELD_ALL; +import static android.net.NetworkStatsHistory.FIELD_OPERATIONS; +import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; +import static android.net.NetworkStatsHistory.FIELD_RX_PACKETS; +import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; +import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong; +import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong; +import static android.net.NetworkStatsHistory.Entry.UNKNOWN; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -30,7 +38,10 @@ import android.util.Log; import com.android.frameworks.coretests.R; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; +import java.io.DataOutputStream; import java.util.Random; @SmallTest @@ -39,6 +50,10 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { private static final long TEST_START = 1194220800000L; + private static final long KB_IN_BYTES = 1024; + private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; + private static final long GB_IN_BYTES = MB_IN_BYTES * 1024; + private NetworkStatsHistory stats; @Override @@ -80,10 +95,11 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { stats = new NetworkStatsHistory(BUCKET_SIZE); // record data into narrow window to get single bucket - stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L); + stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); assertEquals(1, stats.size()); - assertValues(stats, 0, 1024L, 2048L); + assertValues(stats, 0, 1024L, 10L, 2048L, 20L, 2L); } public void testRecordEqualBuckets() throws Exception { @@ -92,11 +108,12 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // split equally across two buckets final long recordStart = TEST_START + (bucketDuration / 2); - stats.recordData(recordStart, recordStart + bucketDuration, 1024L, 128L); + stats.recordData(recordStart, recordStart + bucketDuration, + new NetworkStats.Entry(1024L, 10L, 128L, 2L, 2L)); assertEquals(2, stats.size()); - assertValues(stats, 0, 512L, 64L); - assertValues(stats, 1, 512L, 64L); + assertValues(stats, 0, 512L, 5L, 64L, 1L, 1L); + assertValues(stats, 1, 512L, 5L, 64L, 1L, 1L); } public void testRecordTouchingBuckets() throws Exception { @@ -107,15 +124,16 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // overlap into neighboring buckets. total record is 20 minutes. final long recordStart = (TEST_START + BUCKET_SIZE) - MINUTE_IN_MILLIS; final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4); - stats.recordData(recordStart, recordEnd, 1000L, 5000L); + stats.recordData(recordStart, recordEnd, + new NetworkStats.Entry(1000L, 2000L, 5000L, 10000L, 100L)); assertEquals(3, stats.size()); // first bucket should have (1/20 of value) - assertValues(stats, 0, 50L, 250L); + assertValues(stats, 0, 50L, 100L, 250L, 500L, 5L); // second bucket should have (15/20 of value) - assertValues(stats, 1, 750L, 3750L); + assertValues(stats, 1, 750L, 1500L, 3750L, 7500L, 75L); // final bucket should have (4/20 of value) - assertValues(stats, 2, 200L, 1000L); + assertValues(stats, 2, 200L, 400L, 1000L, 2000L, 20L); } public void testRecordGapBuckets() throws Exception { @@ -125,25 +143,28 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // record some data today and next week with large gap final long firstStart = TEST_START; final long lastStart = TEST_START + WEEK_IN_MILLIS; - stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS, 128L, 256L); - stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, 64L, 512L); + stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS, + new NetworkStats.Entry(128L, 2L, 256L, 4L, 1L)); + stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, + new NetworkStats.Entry(64L, 1L, 512L, 8L, 2L)); // we should have two buckets, far apart from each other assertEquals(2, stats.size()); - assertValues(stats, 0, 128L, 256L); - assertValues(stats, 1, 64L, 512L); + assertValues(stats, 0, 128L, 2L, 256L, 4L, 1L); + assertValues(stats, 1, 64L, 1L, 512L, 8L, 2L); // now record something in middle, spread across two buckets final long middleStart = TEST_START + DAY_IN_MILLIS; final long middleEnd = middleStart + (HOUR_IN_MILLIS * 2); - stats.recordData(middleStart, middleEnd, 2048L, 2048L); + stats.recordData(middleStart, middleEnd, + new NetworkStats.Entry(2048L, 4L, 2048L, 4L, 2L)); // now should have four buckets, with new record in middle two buckets assertEquals(4, stats.size()); - assertValues(stats, 0, 128L, 256L); - assertValues(stats, 1, 1024L, 1024L); - assertValues(stats, 2, 1024L, 1024L); - assertValues(stats, 3, 64L, 512L); + assertValues(stats, 0, 128L, 2L, 256L, 4L, 1L); + assertValues(stats, 1, 1024L, 2L, 1024L, 2L, 1L); + assertValues(stats, 2, 1024L, 2L, 1024L, 2L, 1L); + assertValues(stats, 3, 64L, 1L, 512L, 8L, 2L); } public void testRecordOverlapBuckets() throws Exception { @@ -151,14 +172,16 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { stats = new NetworkStatsHistory(BUCKET_SIZE); // record some data in one bucket, and another overlapping buckets - stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L); + stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, + new NetworkStats.Entry(256L, 2L, 256L, 2L, 1L)); final long midStart = TEST_START + (HOUR_IN_MILLIS / 2); - stats.recordData(midStart, midStart + HOUR_IN_MILLIS, 1024L, 1024L); + stats.recordData(midStart, midStart + HOUR_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 1024L, 10L, 10L)); // should have two buckets, with some data mixed together assertEquals(2, stats.size()); - assertValues(stats, 0, 768L, 768L); - assertValues(stats, 1, 512L, 512L); + assertValues(stats, 0, 768L, 7L, 768L, 7L, 6L); + assertValues(stats, 1, 512L, 5L, 512L, 5L, 5L); } public void testRecordEntireGapIdentical() throws Exception { @@ -283,6 +306,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { public void testFuzzing() throws Exception { try { // fuzzing with random events, looking for crashes + final NetworkStats.Entry entry = new NetworkStats.Entry(); final Random r = new Random(); for (int i = 0; i < 500; i++) { stats = new NetworkStatsHistory(r.nextLong()); @@ -291,7 +315,12 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // add range final long start = r.nextLong(); final long end = start + r.nextInt(); - stats.recordData(start, end, r.nextLong(), r.nextLong()); + entry.rxBytes = nextPositiveLong(r); + entry.rxPackets = nextPositiveLong(r); + entry.txBytes = nextPositiveLong(r); + entry.txPackets = nextPositiveLong(r); + entry.operations = nextPositiveLong(r); + stats.recordData(start, end, entry); } else { // trim something stats.removeBucketsBefore(r.nextLong()); @@ -305,6 +334,88 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { } } + private static long nextPositiveLong(Random r) { + final long value = r.nextLong(); + return value < 0 ? -value : value; + } + + public void testIgnoreFields() throws Exception { + final NetworkStatsHistory history = new NetworkStatsHistory( + MINUTE_IN_MILLIS, 0, FIELD_RX_BYTES | FIELD_TX_BYTES); + + history.recordData(0, MINUTE_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + history.recordData(0, MINUTE_IN_MILLIS * 2, + new NetworkStats.Entry(2L, 2L, 2L, 2L, 2L)); + + assertValues( + history, Long.MIN_VALUE, Long.MAX_VALUE, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN); + } + + public void testIgnoreFieldsRecordIn() throws Exception { + final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); + final NetworkStatsHistory partial = new NetworkStatsHistory( + MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS); + + full.recordData(0, MINUTE_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + partial.recordEntireHistory(full); + + assertValues(partial, Long.MIN_VALUE, Long.MAX_VALUE, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L); + } + + public void testIgnoreFieldsRecordOut() throws Exception { + final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); + final NetworkStatsHistory partial = new NetworkStatsHistory( + MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS); + + partial.recordData(0, MINUTE_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + full.recordEntireHistory(partial); + + assertValues(full, Long.MIN_VALUE, Long.MAX_VALUE, 0L, 10L, 0L, 0L, 4L); + } + + public void testSerialize() throws Exception { + final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL); + before.recordData(0, MINUTE_IN_MILLIS * 4, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + before.recordData(DAY_IN_MILLIS, DAY_IN_MILLIS + MINUTE_IN_MILLIS, + new NetworkStats.Entry(10L, 20L, 30L, 40L, 50L)); + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + before.writeToStream(new DataOutputStream(out)); + out.close(); + + final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + final NetworkStatsHistory after = new NetworkStatsHistory(new DataInputStream(in)); + + // must have identical totals before and after + assertValues(before, Long.MIN_VALUE, Long.MAX_VALUE, 1034L, 30L, 2078L, 60L, 54L); + assertValues(after, Long.MIN_VALUE, Long.MAX_VALUE, 1034L, 30L, 2078L, 60L, 54L); + } + + public void testVarLong() throws Exception { + assertEquals(0L, performVarLong(0L)); + assertEquals(-1L, performVarLong(-1L)); + assertEquals(1024L, performVarLong(1024L)); + assertEquals(-1024L, performVarLong(-1024L)); + assertEquals(40 * MB_IN_BYTES, performVarLong(40 * MB_IN_BYTES)); + assertEquals(512 * GB_IN_BYTES, performVarLong(512 * GB_IN_BYTES)); + assertEquals(Long.MIN_VALUE, performVarLong(Long.MIN_VALUE)); + assertEquals(Long.MAX_VALUE, performVarLong(Long.MAX_VALUE)); + assertEquals(Long.MIN_VALUE + 40, performVarLong(Long.MIN_VALUE + 40)); + assertEquals(Long.MAX_VALUE - 40, performVarLong(Long.MAX_VALUE - 40)); + } + + private static long performVarLong(long before) throws Exception { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeVarLong(new DataOutputStream(out), before); + + final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + return readVarLong(new DataInputStream(in)); + } + private static void assertConsistent(NetworkStatsHistory stats) { // verify timestamps are monotonic long lastStart = Long.MIN_VALUE; @@ -330,4 +441,23 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertEquals("unexpected txBytes", txBytes, entry.txBytes); } + private static void assertValues(NetworkStatsHistory stats, int index, long rxBytes, + long rxPackets, long txBytes, long txPackets, long operations) { + final NetworkStatsHistory.Entry entry = stats.getValues(index, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + assertEquals("unexpected operations", operations, entry.operations); + } + + private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes, + long rxPackets, long txBytes, long txPackets, long operations) { + final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + assertEquals("unexpected operations", operations, entry.operations); + } } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index a59b6c07f356a..30de385f305a7 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -1231,6 +1231,13 @@ class NetworkManagementService extends INetworkManagementService.Stub { } } + @Override + public boolean isBandwidthControlEnabled() { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + return mBandwidthControlEnabled; + } + + @Override public NetworkStats getNetworkStatsUidDetail(int uid) { if (Binder.getCallingUid() != uid) { mContext.enforceCallingOrSelfPermission( diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 24188cab76b6a..deca7a91cfa49 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -16,10 +16,10 @@ package com.android.server.net; -import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.ACCESS_NETWORK_STATE; -import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING; +import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; @@ -68,7 +68,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; import android.telephony.TelephonyManager; -import android.util.Log; import android.util.LongSparseArray; import android.util.NtpTrustedTime; import android.util.Slog; @@ -282,13 +281,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template) { + public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); synchronized (mStatsLock) { // combine all interfaces that match template final NetworkStatsHistory combined = new NetworkStatsHistory( - mSettings.getNetworkBucketDuration(), estimateNetworkBuckets()); + mSettings.getNetworkBucketDuration(), estimateNetworkBuckets(), fields); for (NetworkIdentitySet ident : mNetworkStats.keySet()) { if (templateMatches(template, ident)) { final NetworkStatsHistory history = mNetworkStats.get(ident); @@ -302,7 +301,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public NetworkStatsHistory getHistoryForUid(NetworkTemplate template, int uid, int tag) { + public NetworkStatsHistory getHistoryForUid( + NetworkTemplate template, int uid, int tag, int fields) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); synchronized (mStatsLock) { @@ -311,7 +311,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // combine all interfaces that match template final NetworkStatsHistory combined = new NetworkStatsHistory( - mSettings.getUidBucketDuration(), estimateUidBuckets()); + mSettings.getUidBucketDuration(), estimateUidBuckets(), fields); for (NetworkIdentitySet ident : mUidStats.keySet()) { if (templateMatches(template, ident)) { final NetworkStatsHistory history = mUidStats.get(ident).get(packed); @@ -596,7 +596,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // decide if enough has changed to trigger persist final NetworkStats persistDelta = computeStatsDelta( - mLastPersistNetworkSnapshot, networkSnapshot); + mLastPersistNetworkSnapshot, networkSnapshot, true); final long persistThreshold = mSettings.getPersistThreshold(); NetworkStats.Entry entry = null; @@ -626,7 +626,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void performNetworkPollLocked(NetworkStats networkSnapshot, long currentTime) { final HashSet unknownIface = Sets.newHashSet(); - final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot); + final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot, false); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -661,9 +661,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) { ensureUidStatsLoadedLocked(); - final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot); + final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot, false); final NetworkStats operationsDelta = computeStatsDelta( - mLastOperationsSnapshot, mOperations); + mLastOperationsSnapshot, mOperations, false); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; @@ -932,6 +932,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { out.flush(); mNetworkFile.finishWrite(fos); } catch (IOException e) { + Slog.w(TAG, "problem writing stats: ", e); if (fos != null) { mNetworkFile.failWrite(fos); } @@ -978,6 +979,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { out.flush(); mUidFile.finishWrite(fos); } catch (IOException e) { + Slog.w(TAG, "problem writing stats: ", e); if (fos != null) { mUidFile.failWrite(fos); } @@ -1052,15 +1054,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ @Deprecated private void generateRandomLocked() { - long networkEnd = System.currentTimeMillis(); - long networkStart = networkEnd - mSettings.getNetworkMaxHistory(); - long networkRx = 3 * GB_IN_BYTES; - long networkTx = 2 * GB_IN_BYTES; + final long NET_END = System.currentTimeMillis(); + final long NET_START = NET_END - mSettings.getNetworkMaxHistory(); + final long NET_RX_BYTES = 3 * GB_IN_BYTES; + final long NET_RX_PACKETS = NET_RX_BYTES / 1024; + final long NET_TX_BYTES = 2 * GB_IN_BYTES; + final long NET_TX_PACKETS = NET_TX_BYTES / 1024; - long uidEnd = System.currentTimeMillis(); - long uidStart = uidEnd - mSettings.getUidMaxHistory(); - long uidRx = 500 * MB_IN_BYTES; - long uidTx = 100 * MB_IN_BYTES; + final long UID_END = System.currentTimeMillis(); + final long UID_START = UID_END - mSettings.getUidMaxHistory(); + final long UID_RX_BYTES = 500 * MB_IN_BYTES; + final long UID_RX_PACKETS = UID_RX_BYTES / 1024; + final long UID_TX_BYTES = 100 * MB_IN_BYTES; + final long UID_TX_PACKETS = UID_TX_BYTES / 1024; + final long UID_OPERATIONS = UID_RX_BYTES / 2048; final List installedApps = mContext .getPackageManager().getInstalledApplications(0); @@ -1068,13 +1075,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mNetworkStats.clear(); mUidStats.clear(); for (NetworkIdentitySet ident : mActiveIfaces.values()) { - findOrCreateNetworkStatsLocked(ident).generateRandom( - networkStart, networkEnd, networkRx, networkTx); + findOrCreateNetworkStatsLocked(ident).generateRandom(NET_START, NET_END, NET_RX_BYTES, + NET_RX_PACKETS, NET_TX_BYTES, NET_TX_PACKETS, 0L); for (ApplicationInfo info : installedApps) { final int uid = info.uid; - findOrCreateUidStatsLocked(ident, uid, TAG_NONE).generateRandom( - uidStart, uidEnd, uidRx, uidTx); + findOrCreateUidStatsLocked(ident, uid, TAG_NONE).generateRandom(UID_START, UID_END, + UID_RX_BYTES, UID_RX_PACKETS, UID_TX_BYTES, UID_TX_PACKETS, UID_OPERATIONS); } } } @@ -1083,9 +1090,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Return the delta between two {@link NetworkStats} snapshots, where {@code * before} can be {@code null}. */ - private static NetworkStats computeStatsDelta(NetworkStats before, NetworkStats current) { + private static NetworkStats computeStatsDelta( + NetworkStats before, NetworkStats current, boolean collectStale) { if (before != null) { return current.subtractClamped(before); + } else if (collectStale) { + // caller is okay collecting stale stats for first call. + return current; } else { // this is first snapshot; to prevent from double-counting we only // observe traffic occuring between known snapshots. diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index cf69fd546552a..8eb9cc305397d 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStatsHistory.FIELD_ALL; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.TrafficStats.UID_REMOVED; @@ -302,7 +303,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify service recorded history - history = mService.getHistoryForNetwork(sTemplateWifi); + history = mService.getHistoryForNetwork(sTemplateWifi, FIELD_ALL); assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0); assertEquals(HOUR_IN_MILLIS, history.getBucketDuration()); assertEquals(2, history.size()); @@ -319,7 +320,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // verify identical stats, but spread across 4 buckets now - history = mService.getHistoryForNetwork(sTemplateWifi); + history = mService.getHistoryForNetwork(sTemplateWifi, FIELD_ALL); assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0); assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration()); assertEquals(4, history.size()); @@ -631,14 +632,15 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) { - final NetworkStatsHistory history = mService.getHistoryForNetwork(template); + final NetworkStatsHistory history = mService.getHistoryForNetwork(template, FIELD_ALL); assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes, txPackets, operations); } private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) { - final NetworkStatsHistory history = mService.getHistoryForUid(template, uid, TAG_NONE); + final NetworkStatsHistory history = mService.getHistoryForUid( + template, uid, TAG_NONE, FIELD_ALL); assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes, txPackets, operations); }