From c76817d37071a38354ccc77f691c293009ea7c52 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 +++++++++++------- .../server/net/NetworkStatsService.java | 59 ++-- 4 files changed, 238 insertions(+), 144 deletions(-) diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index c41d182cfa..b65506c411 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 0e8e7fc5ec..f2fcb8fad8 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 7a4b811763..c917af919e 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/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 24188cab76..deca7a91cf 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.