diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 054825024b..c41d182cfa 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -23,16 +23,21 @@ import android.net.NetworkTemplate; /** {@hide} */ interface INetworkStatsService { - /** Return historical stats for traffic that matches template. */ + /** Return historical network layer stats for traffic that matches template. */ NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template); - /** Return historical stats for specific UID traffic that matches template. */ + /** Return historical network layer stats for specific UID traffic that matches template. */ NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag); - /** Return usage summary for traffic that matches template. */ + /** Return network layer usage summary for traffic that matches template. */ NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end); - /** Return usage summary per UID for traffic that matches template. */ + /** Return network layer usage summary per UID for traffic that matches template. */ NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags); + /** Return data layer snapshot of UID network usage. */ + NetworkStats getDataLayerSnapshotForUid(int uid); + /** Increment data layer count of operations performed for UID and tag. */ + void incrementOperationCount(int uid, int tag, int operationCount); + /** Force update of statistics. */ void forceUpdate(); diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index fbff7d89fc..0e8e7fc5ec 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -21,6 +21,8 @@ import android.os.Parcelable; import android.os.SystemClock; import android.util.SparseBooleanArray; +import com.android.internal.util.Objects; + import java.io.CharArrayWriter; import java.io.PrintWriter; import java.util.Arrays; @@ -56,6 +58,7 @@ public class NetworkStats implements Parcelable { private long[] rxPackets; private long[] txBytes; private long[] txPackets; + private int[] operations; public static class Entry { public String iface; @@ -65,12 +68,13 @@ public class NetworkStats implements Parcelable { public long rxPackets; public long txBytes; public long txPackets; + public int operations; public Entry() { } public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes, - long txPackets) { + long txPackets, int operations) { this.iface = iface; this.uid = uid; this.tag = tag; @@ -78,6 +82,7 @@ public class NetworkStats implements Parcelable { this.rxPackets = rxPackets; this.txBytes = txBytes; this.txPackets = txPackets; + this.operations = operations; } } @@ -91,6 +96,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]; } public NetworkStats(Parcel parcel) { @@ -103,11 +109,32 @@ public class NetworkStats implements Parcelable { rxPackets = parcel.createLongArray(); txBytes = parcel.createLongArray(); txPackets = parcel.createLongArray(); + operations = parcel.createIntArray(); + } + + /** {@inheritDoc} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(elapsedRealtime); + dest.writeInt(size); + dest.writeStringArray(iface); + dest.writeIntArray(uid); + dest.writeIntArray(tag); + dest.writeLongArray(rxBytes); + dest.writeLongArray(rxPackets); + dest.writeLongArray(txBytes); + dest.writeLongArray(txPackets); + dest.writeIntArray(operations); } 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)); + return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0); + } + + public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets, + long txBytes, long txPackets, int operations) { + return addValues( + new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); } /** @@ -124,6 +151,7 @@ public class NetworkStats implements Parcelable { rxPackets = Arrays.copyOf(rxPackets, newLength); txBytes = Arrays.copyOf(txBytes, newLength); txPackets = Arrays.copyOf(txPackets, newLength); + operations = Arrays.copyOf(operations, newLength); } iface[size] = entry.iface; @@ -133,6 +161,7 @@ public class NetworkStats implements Parcelable { rxPackets[size] = entry.rxPackets; txBytes[size] = entry.txBytes; txPackets[size] = entry.txPackets; + operations[size] = entry.operations; size++; return this; @@ -150,6 +179,7 @@ public class NetworkStats implements Parcelable { entry.rxPackets = rxPackets[i]; entry.txBytes = txBytes[i]; entry.txPackets = txPackets[i]; + entry.operations = operations[i]; return entry; } @@ -167,8 +197,9 @@ public class NetworkStats implements Parcelable { } 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)); + long txBytes, long txPackets, int operations) { + return combineValues( + new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations)); } /** @@ -186,6 +217,7 @@ public class NetworkStats implements Parcelable { rxPackets[i] += entry.rxPackets; txBytes[i] += entry.txBytes; txPackets[i] += entry.txPackets; + operations[i] += entry.operations; } return this; } @@ -195,13 +227,29 @@ public class NetworkStats implements Parcelable { */ public int findIndex(String iface, int uid, int tag) { for (int i = 0; i < size; i++) { - if (equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) { + if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) { return i; } } return -1; } + /** + * Splice in {@link #operations} from the given {@link NetworkStats} based + * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface}, + * since operation counts are at data layer. + */ + public void spliceOperationsFrom(NetworkStats stats) { + for (int i = 0; i < size; i++) { + final int j = stats.findIndex(IFACE_ALL, uid[i], tag[i]); + if (j == -1) { + operations[i] = 0; + } else { + operations[i] = stats.operations[j]; + } + } + } + /** * Return list of unique interfaces known by this data structure. */ @@ -289,15 +337,17 @@ public class NetworkStats implements Parcelable { entry.rxPackets = rxPackets[i]; entry.txBytes = txBytes[i]; entry.txPackets = txPackets[i]; + entry.operations = operations[i]; } else { // existing row, subtract remote value entry.rxBytes = rxBytes[i] - value.rxBytes[j]; entry.rxPackets = rxPackets[i] - value.rxPackets[j]; entry.txBytes = txBytes[i] - value.txBytes[j]; entry.txPackets = txPackets[i] - value.txPackets[j]; + entry.operations = operations[i] - value.operations[j]; if (enforceMonotonic && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 - || entry.txPackets < 0)) { + || entry.txPackets < 0 || entry.operations < 0)) { throw new IllegalArgumentException("found non-monotonic values"); } if (clampNegative) { @@ -305,6 +355,7 @@ public class NetworkStats implements Parcelable { entry.rxPackets = Math.max(0, entry.rxPackets); entry.txBytes = Math.max(0, entry.txBytes); entry.txPackets = Math.max(0, entry.txPackets); + entry.operations = Math.max(0, entry.operations); } } @@ -314,10 +365,6 @@ public class NetworkStats implements Parcelable { return result; } - private static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } - public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); @@ -325,11 +372,12 @@ public class NetworkStats implements Parcelable { pw.print(prefix); pw.print(" iface="); pw.print(iface[i]); pw.print(" uid="); pw.print(uid[i]); - pw.print(" tag="); pw.print(tag[i]); + pw.print(" tag=0x"); pw.print(Integer.toHexString(tag[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.println(txPackets[i]); + pw.print(" txPackets="); pw.print(txPackets[i]); + pw.print(" operations="); pw.println(operations[i]); } } @@ -345,19 +393,6 @@ public class NetworkStats implements Parcelable { return 0; } - /** {@inheritDoc} */ - public void writeToParcel(Parcel dest, int flags) { - dest.writeLong(elapsedRealtime); - dest.writeInt(size); - dest.writeStringArray(iface); - dest.writeIntArray(uid); - dest.writeIntArray(tag); - dest.writeLongArray(rxBytes); - dest.writeLongArray(rxPackets); - dest.writeLongArray(txBytes); - dest.writeLongArray(txPackets); - } - public static final Creator CREATOR = new Creator() { public NetworkStats createFromParcel(Parcel in) { return new NetworkStats(in); diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index 8bd1738937..4ffabb15b6 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -16,6 +16,16 @@ 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.ParcelUtils.readLongArray; +import static android.net.NetworkStatsHistory.ParcelUtils.writeIntArray; +import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray; + import android.os.Parcel; import android.os.Parcelable; @@ -43,19 +53,26 @@ 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 + // TODO: teach about omitting entire fields to reduce parcel pressure + // TODO: persist/restore packet and operation counts private final long bucketDuration; private int bucketCount; private long[] bucketStart; private long[] rxBytes; + private long[] rxPackets; private long[] txBytes; + private long[] txPackets; + private int[] operations; public static class Entry { public long bucketStart; public long bucketDuration; public long rxBytes; + public long rxPackets; public long txBytes; + public long txPackets; + public int operations; } public NetworkStatsHistory(long bucketDuration) { @@ -66,15 +83,21 @@ public class NetworkStatsHistory implements Parcelable { 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]; bucketCount = 0; } public NetworkStatsHistory(Parcel in) { bucketDuration = in.readLong(); bucketStart = readLongArray(in); - rxBytes = in.createLongArray(); - txBytes = in.createLongArray(); + rxBytes = readLongArray(in); + rxPackets = readLongArray(in); + txBytes = readLongArray(in); + txPackets = readLongArray(in); + operations = readIntArray(in); bucketCount = bucketStart.length; } @@ -83,17 +106,24 @@ public class NetworkStatsHistory implements Parcelable { out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rxBytes, bucketCount); + writeLongArray(out, rxPackets, bucketCount); writeLongArray(out, txBytes, bucketCount); + writeLongArray(out, txPackets, bucketCount); + writeIntArray(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); + rxPackets = new long[bucketStart.length]; txBytes = readLongArray(in); + txPackets = new long[bucketStart.length]; + operations = new int[bucketStart.length]; bucketCount = bucketStart.length; break; } @@ -104,6 +134,7 @@ public class NetworkStatsHistory implements Parcelable { } public void writeToStream(DataOutputStream out) throws IOException { + // TODO: write packet and operation counts out.writeInt(VERSION_INIT); out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); @@ -148,7 +179,10 @@ public class NetworkStatsHistory implements Parcelable { 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]; return entry; } @@ -156,17 +190,27 @@ public class NetworkStatsHistory implements Parcelable { * Record that data traffic occurred in the given time range. Will * distribute across internal buckets, creating new buckets as needed. */ - public void recordData(long start, long end, long rx, long tx) { - if (rx < 0 || tx < 0) { - throw new IllegalArgumentException( - "tried recording negative data: rx=" + rx + ", tx=" + tx); + @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)); + } + + /** + * Record that data traffic occurred in the given time range. Will + * distribute across internal buckets, creating new buckets as needed. + */ + public void recordData(long start, long end, NetworkStats.Entry entry) { + if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0 + || entry.operations < 0) { + throw new IllegalArgumentException("tried recording negative data"); } // create any buckets needed by this range ensureBuckets(start, end); // distribute data usage into buckets - final long duration = end - start; + long duration = end - start; for (int i = bucketCount - 1; i >= 0; i--) { final long curStart = bucketStart[i]; final long curEnd = curStart + bucketDuration; @@ -177,10 +221,22 @@ public class NetworkStatsHistory implements Parcelable { if (curStart > end) continue; final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); - if (overlap > 0) { - this.rxBytes[i] += rx * overlap / duration; - this.txBytes[i] += tx * overlap / duration; - } + if (overlap <= 0) continue; + + // integer math each time is faster than floating point + final long fracRxBytes = entry.rxBytes * overlap / duration; + final long fracRxPackets = entry.rxPackets * overlap / duration; + final long fracTxBytes = entry.txBytes * overlap / duration; + 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; + + duration -= overlap; } } @@ -189,10 +245,19 @@ public class NetworkStatsHistory implements Parcelable { * for combining together stats for external reporting. */ public void recordEntireHistory(NetworkStatsHistory input) { + final NetworkStats.Entry entry = new NetworkStats.Entry( + IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0); for (int i = 0; i < input.bucketCount; i++) { final long start = input.bucketStart[i]; final long end = start + input.bucketDuration; - recordData(start, end, input.rxBytes[i], input.txBytes[i]); + + 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]; + + recordData(start, end, entry); } } @@ -223,7 +288,10 @@ public class NetworkStatsHistory implements Parcelable { 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); } // create gap when inserting bucket in middle @@ -233,12 +301,18 @@ public class NetworkStatsHistory implements Parcelable { 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); } bucketStart[index] = start; rxBytes[index] = 0; + rxPackets[index] = 0; txBytes[index] = 0; + txPackets[index] = 0; + operations[index] = 0; bucketCount++; } @@ -260,7 +334,10 @@ public class NetworkStatsHistory implements Parcelable { 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); bucketCount -= i; } } @@ -282,7 +359,10 @@ public class NetworkStatsHistory implements Parcelable { entry.bucketStart = start; entry.bucketDuration = end - start; entry.rxBytes = 0; + entry.rxPackets = 0; entry.txBytes = 0; + entry.txPackets = 0; + entry.operations = 0; for (int i = bucketCount - 1; i >= 0; i--) { final long curStart = bucketStart[i]; @@ -295,14 +375,16 @@ public class NetworkStatsHistory implements Parcelable { // include full value for active buckets, otherwise only fractional final boolean activeBucket = curStart < now && curEnd > now; - final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); - if (activeBucket || overlap == bucketDuration) { - entry.rxBytes += rxBytes[i]; - entry.txBytes += txBytes[i]; - } else if (overlap > 0) { - entry.rxBytes += rxBytes[i] * overlap / bucketDuration; - entry.txBytes += txBytes[i] * overlap / bucketDuration; - } + final long overlap = activeBucket ? bucketDuration + : Math.min(curEnd, end) - Math.max(curStart, start); + 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; } return entry; @@ -315,17 +397,19 @@ public class NetworkStatsHistory implements Parcelable { public void generateRandom(long start, long end, long rx, long tx) { ensureBuckets(start, end); + final NetworkStats.Entry entry = new NetworkStats.Entry( + IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0); final Random r = new Random(); while (rx > 1024 && tx > 1024) { final long curStart = randomLong(r, start, end); final long curEnd = randomLong(r, curStart, end); - final long curRx = randomLong(r, 0, rx); - final long curTx = randomLong(r, 0, tx); + entry.rxBytes = randomLong(r, 0, rx); + entry.txBytes = randomLong(r, 0, tx); - recordData(curStart, curEnd, curRx, curTx); + recordData(curStart, curEnd, entry); - rx -= curRx; - tx -= curTx; + rx -= entry.rxBytes; + tx -= entry.txBytes; } } @@ -347,7 +431,10 @@ public class NetworkStatsHistory implements Parcelable { pw.print(prefix); pw.print(" bucketStart="); pw.print(bucketStart[i]); pw.print(" rxBytes="); pw.print(rxBytes[i]); - pw.print(" txBytes="); pw.println(txBytes[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]); } } @@ -368,41 +455,73 @@ public class NetworkStatsHistory implements Parcelable { } }; - private static long[] readLongArray(DataInputStream in) throws IOException { - final int size = in.readInt(); - final long[] values = new long[size]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readLong(); + /** + * 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 { + final int size = in.readInt(); + final long[] values = new long[size]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readLong(); + } + return values; } - return values; - } - private static void writeLongArray(DataOutputStream out, long[] values, int size) throws IOException { - 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]); + public static void writeLongArray(DataOutputStream out, long[] values, int size) + throws IOException { + 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]); + } } } - private static long[] readLongArray(Parcel in) { - final int size = in.readInt(); - final long[] values = new long[size]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readLong(); + /** + * Utility methods for interacting with {@link Parcel} structures, mostly + * dealing with writing partial arrays. + */ + public static class ParcelUtils { + public static long[] readLongArray(Parcel in) { + final int size = in.readInt(); + final long[] values = new long[size]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readLong(); + } + return values; } - return values; - } - private static void writeLongArray(Parcel out, long[] values, int size) { - if (size > values.length) { - throw new IllegalArgumentException("size larger than length"); + public static void writeLongArray(Parcel out, long[] 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.writeLong(values[i]); + } } - out.writeInt(size); - for (int i = 0; i < size; i++) { - 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/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index e054930684..f138e49140 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -20,14 +20,13 @@ import android.app.DownloadManager; import android.app.backup.BackupManager; import android.content.Context; import android.media.MediaPlayer; -import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.ServiceManager; import com.android.server.NetworkManagementSocketTagger; import dalvik.system.SocketTagger; + import java.net.Socket; import java.net.SocketException; @@ -172,7 +171,7 @@ public class TrafficStats { } // take snapshot in time; we calculate delta later - sActiveProfilingStart = getNetworkStatsForUid(context); + sActiveProfilingStart = getDataLayerSnapshotForUid(context); } } @@ -190,7 +189,7 @@ public class TrafficStats { } // subtract starting values and return delta - final NetworkStats profilingStop = getNetworkStatsForUid(context); + final NetworkStats profilingStop = getDataLayerSnapshotForUid(context); final NetworkStats profilingDelta = profilingStop.subtractClamped( sActiveProfilingStart); sActiveProfilingStart = null; @@ -198,6 +197,28 @@ public class TrafficStats { } } + /** + * Increment count of network operations performed under the given + * accounting tag. This can be used to derive bytes-per-operation. + * + * @param tag Accounting tag used in {@link #setThreadStatsTag(int)}. + * @param operationCount Number of operations to increment count by. + */ + public static void incrementOperationCount(int tag, int operationCount) { + if (operationCount < 0) { + throw new IllegalArgumentException("operation count can only be incremented"); + } + + final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + final int uid = android.os.Process.myUid(); + try { + statsService.incrementOperationCount(uid, tag, operationCount); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + /** * Get the total number of packets transmitted through the mobile interface. * @@ -461,14 +482,12 @@ public class TrafficStats { * Return detailed {@link NetworkStats} for the current UID. Requires no * special permission. */ - private static NetworkStats getNetworkStatsForUid(Context context) { - final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - final INetworkManagementService service = INetworkManagementService.Stub.asInterface( - binder); - + private static NetworkStats getDataLayerSnapshotForUid(Context context) { + final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); final int uid = android.os.Process.myUid(); try { - return service.getNetworkStatsUidDetail(uid); + return statsService.getDataLayerSnapshotForUid(uid); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 6cc01f40d9..24188cab76 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -17,6 +17,8 @@ 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.DUMP; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.content.Intent.ACTION_SHUTDOWN; @@ -56,6 +58,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; +import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -65,6 +68,7 @@ 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; @@ -150,9 +154,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Set of currently active ifaces. */ private HashMap mActiveIfaces = Maps.newHashMap(); - /** Set of historical stats for known networks. */ + /** Set of historical network layer stats for known networks. */ private HashMap mNetworkStats = Maps.newHashMap(); - /** Set of historical stats for known UIDs. */ + /** Set of historical network layer stats for known UIDs. */ private HashMap> mUidStats = Maps.newHashMap(); @@ -164,6 +168,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private NetworkStats mLastUidSnapshot; + /** Data layer operation counters for splicing into other structures. */ + private NetworkStats mOperations = new NetworkStats(0L, 10); + private NetworkStats mLastOperationsSnapshot; + private final HandlerThread mHandlerThread; private final Handler mHandler; @@ -381,9 +389,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { entry.uid = uid; entry.tag = tag; entry.rxBytes = historyEntry.rxBytes; + entry.rxPackets = historyEntry.rxPackets; entry.txBytes = historyEntry.txBytes; + entry.txPackets = historyEntry.txPackets; + entry.operations = historyEntry.operations; - if (entry.rxBytes > 0 || entry.txBytes > 0) { + if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0 + || entry.txPackets > 0 || entry.operations > 0) { stats.combineValues(entry); } } @@ -395,6 +407,41 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + @Override + public NetworkStats getDataLayerSnapshotForUid(int uid) throws RemoteException { + if (Binder.getCallingUid() != uid) { + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); + } + + // TODO: switch to data layer stats once kernel exports + // for now, read network layer stats and flatten across all ifaces + final NetworkStats networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid); + final NetworkStats dataLayer = new NetworkStats( + networkLayer.getElapsedRealtime(), networkLayer.size()); + + NetworkStats.Entry entry = null; + for (int i = 0; i < networkLayer.size(); i++) { + entry = networkLayer.getValues(i, entry); + entry.iface = IFACE_ALL; + dataLayer.combineValues(entry); + } + + // splice in operation counts + dataLayer.spliceOperationsFrom(mOperations); + return dataLayer; + } + + @Override + public void incrementOperationCount(int uid, int tag, int operationCount) { + if (Binder.getCallingUid() != uid) { + mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG); + } + + synchronized (mStatsLock) { + mOperations.combineValues(IFACE_ALL, uid, tag, 0L, 0L, 0L, 0L, operationCount); + } + } + @Override public void forceUpdate() { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); @@ -533,7 +580,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStats uidSnapshot; try { networkSnapshot = mNetworkManager.getNetworkStatsSummary(); - uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null; + uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsUidDetail(UID_ALL) : null; } catch (IllegalStateException e) { Slog.w(TAG, "problem reading network stats: " + e); return; @@ -592,7 +639,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident); - history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes); + history.recordData(timeStart, currentTime, entry); } // trim any history beyond max @@ -615,9 +662,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { ensureUidStatsLoadedLocked(); final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot); + final NetworkStats operationsDelta = computeStatsDelta( + mLastOperationsSnapshot, mOperations); final long timeStart = currentTime - delta.getElapsedRealtime(); NetworkStats.Entry entry = null; + NetworkStats.Entry operationsEntry = null; for (int i = 0; i < delta.size(); i++) { entry = delta.getValues(i, entry); final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface); @@ -625,9 +675,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { continue; } + // splice in operation counts since last poll + final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.tag); + if (j != -1) { + operationsEntry = operationsDelta.getValues(j, operationsEntry); + entry.operations = operationsEntry.operations; + } + final NetworkStatsHistory history = findOrCreateUidStatsLocked( ident, entry.uid, entry.tag); - history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes); + history.recordData(timeStart, currentTime, entry); } // trim any history beyond max @@ -648,6 +705,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } mLastUidSnapshot = uidSnapshot; + mLastOperationsSnapshot = mOperations; + mOperations = new NetworkStats(0L, 10); } /** @@ -980,7 +1039,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final int tag = unpackTag(packed); final NetworkStatsHistory history = uidStats.valueAt(i); pw.print(" UID="); pw.print(uid); - pw.print(" tag="); pw.println(tag); + pw.print(" tag=0x"); pw.println(Integer.toHexString(tag)); history.dump(" ", pw, fullHistory); } } @@ -1028,7 +1087,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { if (before != null) { return current.subtractClamped(before); } else { - return current; + // this is first snapshot; to prevent from double-counting we only + // observe traffic occuring between known snapshots. + return new NetworkStats(0L, 10); } } @@ -1114,5 +1175,4 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return DAY_IN_MILLIS; } } - }