From 558a23200697d306b75750cf4612cf0717e73537 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 24 Aug 2011 15:42:09 -0700 Subject: [PATCH] Data usage buckets active time, parsing ISE. When recording data usage, measure the actual active time, since buckets can be quite long. Offer incrementOperationCount() version that reads thread stats tag for caller. Rethrow any NPE as ISE during stats parsing, which callers already handle. Bug: 5171812, 5184508, 5180659 Change-Id: I6da80ccc0162be68bee279529e3a23b6f98ebd87 --- api/current.txt | 1 + .../java/android/net/NetworkStatsHistory.java | 40 +++++++++---- core/java/android/net/TrafficStats.java | 12 ++++ .../android/net/NetworkStatsHistoryTest.java | 58 +++++++++++-------- .../server/NetworkManagementService.java | 12 +++- .../com/android/server/ThrottleService.java | 5 +- 6 files changed, 90 insertions(+), 38 deletions(-) diff --git a/api/current.txt b/api/current.txt index cd85eea7db6e5..d25ff13af63a6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11662,6 +11662,7 @@ package android.net { method public static long getUidUdpRxPackets(int); method public static long getUidUdpTxBytes(int); method public static long getUidUdpTxPackets(int); + method public static void incrementOperationCount(int); method public static void incrementOperationCount(int, int); method public static void setThreadStatsTag(int); method public static deprecated void setThreadStatsTag(java.lang.String); diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index b4f15acff1c9d..b19949e39d5b3 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -53,18 +53,21 @@ import java.util.Random; public class NetworkStatsHistory implements Parcelable { private static final int VERSION_INIT = 1; private static final int VERSION_ADD_PACKETS = 2; + private static final int VERSION_ADD_ACTIVE = 3; - 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; + public static final int FIELD_ACTIVE_TIME = 0x01; + public static final int FIELD_RX_BYTES = 0x02; + public static final int FIELD_RX_PACKETS = 0x04; + public static final int FIELD_TX_BYTES = 0x08; + public static final int FIELD_TX_PACKETS = 0x10; + public static final int FIELD_OPERATIONS = 0x20; public static final int FIELD_ALL = 0xFFFFFFFF; private long bucketDuration; private int bucketCount; private long[] bucketStart; + private long[] activeTime; private long[] rxBytes; private long[] rxPackets; private long[] txBytes; @@ -74,8 +77,9 @@ public class NetworkStatsHistory implements Parcelable { public static class Entry { public static final long UNKNOWN = -1; - public long bucketStart; public long bucketDuration; + public long bucketStart; + public long activeTime; public long rxBytes; public long rxPackets; public long txBytes; @@ -94,6 +98,7 @@ public class NetworkStatsHistory implements Parcelable { public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) { this.bucketDuration = bucketDuration; bucketStart = new long[initialSize]; + if ((fields & FIELD_ACTIVE_TIME) != 0) activeTime = new long[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]; @@ -105,6 +110,7 @@ public class NetworkStatsHistory implements Parcelable { public NetworkStatsHistory(Parcel in) { bucketDuration = in.readLong(); bucketStart = readLongArray(in); + activeTime = readLongArray(in); rxBytes = readLongArray(in); rxPackets = readLongArray(in); txBytes = readLongArray(in); @@ -117,6 +123,7 @@ public class NetworkStatsHistory implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); + writeLongArray(out, activeTime, bucketCount); writeLongArray(out, rxBytes, bucketCount); writeLongArray(out, rxPackets, bucketCount); writeLongArray(out, txBytes, bucketCount); @@ -138,9 +145,12 @@ public class NetworkStatsHistory implements Parcelable { bucketCount = bucketStart.length; break; } - case VERSION_ADD_PACKETS: { + case VERSION_ADD_PACKETS: + case VERSION_ADD_ACTIVE: { bucketDuration = in.readLong(); bucketStart = readVarLongArray(in); + activeTime = (version >= VERSION_ADD_ACTIVE) ? readVarLongArray(in) + : new long[bucketStart.length]; rxBytes = readVarLongArray(in); rxPackets = readVarLongArray(in); txBytes = readVarLongArray(in); @@ -156,9 +166,10 @@ public class NetworkStatsHistory implements Parcelable { } public void writeToStream(DataOutputStream out) throws IOException { - out.writeInt(VERSION_ADD_PACKETS); + out.writeInt(VERSION_ADD_ACTIVE); out.writeLong(bucketDuration); writeVarLongArray(out, bucketStart, bucketCount); + writeVarLongArray(out, activeTime, bucketCount); writeVarLongArray(out, rxBytes, bucketCount); writeVarLongArray(out, rxPackets, bucketCount); writeVarLongArray(out, txBytes, bucketCount); @@ -202,6 +213,7 @@ public class NetworkStatsHistory implements Parcelable { final Entry entry = recycle != null ? recycle : new Entry(); entry.bucketStart = bucketStart[i]; entry.bucketDuration = bucketDuration; + entry.activeTime = getLong(activeTime, i, UNKNOWN); entry.rxBytes = getLong(rxBytes, i, UNKNOWN); entry.rxPackets = getLong(rxPackets, i, UNKNOWN); entry.txBytes = getLong(txBytes, i, UNKNOWN); @@ -252,8 +264,9 @@ public class NetworkStatsHistory implements Parcelable { 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); + final long fracOperations = entry.operations * overlap / duration; + addLong(activeTime, i, overlap); addLong(rxBytes, i, fracRxBytes); entry.rxBytes -= fracRxBytes; addLong(rxPackets, i, fracRxPackets); entry.rxPackets -= fracRxPackets; addLong(txBytes, i, fracTxBytes); entry.txBytes -= fracTxBytes; @@ -311,6 +324,7 @@ public class NetworkStatsHistory implements Parcelable { if (bucketCount >= bucketStart.length) { final int newLength = Math.max(bucketStart.length, 10) * 3 / 2; bucketStart = Arrays.copyOf(bucketStart, newLength); + if (activeTime != null) activeTime = Arrays.copyOf(activeTime, 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); @@ -324,6 +338,7 @@ public class NetworkStatsHistory implements Parcelable { final int length = bucketCount - index; System.arraycopy(bucketStart, index, bucketStart, dstPos, length); + if (activeTime != null) System.arraycopy(activeTime, index, activeTime, 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); @@ -332,6 +347,7 @@ public class NetworkStatsHistory implements Parcelable { } bucketStart[index] = start; + setLong(activeTime, index, 0L); setLong(rxBytes, index, 0L); setLong(rxPackets, index, 0L); setLong(txBytes, index, 0L); @@ -357,6 +373,7 @@ public class NetworkStatsHistory implements Parcelable { if (i > 0) { final int length = bucketStart.length; bucketStart = Arrays.copyOfRange(bucketStart, i, length); + if (activeTime != null) activeTime = Arrays.copyOfRange(activeTime, 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); @@ -380,8 +397,9 @@ public class NetworkStatsHistory implements Parcelable { */ public Entry getValues(long start, long end, long now, Entry recycle) { final Entry entry = recycle != null ? recycle : new Entry(); - entry.bucketStart = start; entry.bucketDuration = end - start; + entry.bucketStart = start; + entry.activeTime = activeTime != null ? 0 : UNKNOWN; entry.rxBytes = rxBytes != null ? 0 : UNKNOWN; entry.rxPackets = rxPackets != null ? 0 : UNKNOWN; entry.txBytes = txBytes != null ? 0 : UNKNOWN; @@ -404,6 +422,7 @@ public class NetworkStatsHistory implements Parcelable { if (overlap <= 0) continue; // integer math each time is faster than floating point + if (activeTime != null) entry.activeTime += activeTime[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; @@ -463,6 +482,7 @@ public class NetworkStatsHistory implements Parcelable { for (int i = start; i < bucketCount; i++) { pw.print(prefix); pw.print(" bucketStart="); pw.print(bucketStart[i]); + if (activeTime != null) pw.print(" activeTime="); pw.print(activeTime[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]); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index c2c5c183d337e..ec3b1e1477814 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -197,6 +197,18 @@ public class TrafficStats { } } + /** + * Increment count of network operations performed under the accounting tag + * currently active on the calling thread. This can be used to derive + * bytes-per-operation. + * + * @param operationCount Number of operations to increment count by. + */ + public static void incrementOperationCount(int operationCount) { + final int tag = getThreadStatsTag(); + incrementOperationCount(tag, operationCount); + } + /** * Increment count of network operations performed under the given * accounting tag. This can be used to derive bytes-per-operation. diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java index 4db4ea52d5272..b888d9a223160 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java @@ -99,7 +99,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); assertEquals(1, stats.size()); - assertValues(stats, 0, 1024L, 10L, 2048L, 20L, 2L); + assertValues(stats, 0, SECOND_IN_MILLIS, 1024L, 10L, 2048L, 20L, 2L); } public void testRecordEqualBuckets() throws Exception { @@ -112,8 +112,8 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { new NetworkStats.Entry(1024L, 10L, 128L, 2L, 2L)); assertEquals(2, stats.size()); - assertValues(stats, 0, 512L, 5L, 64L, 1L, 1L); - assertValues(stats, 1, 512L, 5L, 64L, 1L, 1L); + assertValues(stats, 0, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L); + assertValues(stats, 1, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L); } public void testRecordTouchingBuckets() throws Exception { @@ -129,11 +129,11 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertEquals(3, stats.size()); // first bucket should have (1/20 of value) - assertValues(stats, 0, 50L, 100L, 250L, 500L, 5L); + assertValues(stats, 0, MINUTE_IN_MILLIS, 50L, 100L, 250L, 500L, 5L); // second bucket should have (15/20 of value) - assertValues(stats, 1, 750L, 1500L, 3750L, 7500L, 75L); + assertValues(stats, 1, 15 * MINUTE_IN_MILLIS, 750L, 1500L, 3750L, 7500L, 75L); // final bucket should have (4/20 of value) - assertValues(stats, 2, 200L, 400L, 1000L, 2000L, 20L); + assertValues(stats, 2, 4 * MINUTE_IN_MILLIS, 200L, 400L, 1000L, 2000L, 20L); } public void testRecordGapBuckets() throws Exception { @@ -150,8 +150,8 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // we should have two buckets, far apart from each other assertEquals(2, stats.size()); - assertValues(stats, 0, 128L, 2L, 256L, 4L, 1L); - assertValues(stats, 1, 64L, 1L, 512L, 8L, 2L); + assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L); + assertValues(stats, 1, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L); // now record something in middle, spread across two buckets final long middleStart = TEST_START + DAY_IN_MILLIS; @@ -161,10 +161,10 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // now should have four buckets, with new record in middle two buckets assertEquals(4, stats.size()); - 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); + assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L); + assertValues(stats, 1, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L); + assertValues(stats, 2, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L); + assertValues(stats, 3, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L); } public void testRecordOverlapBuckets() throws Exception { @@ -180,8 +180,8 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // should have two buckets, with some data mixed together assertEquals(2, stats.size()); - assertValues(stats, 0, 768L, 7L, 768L, 7L, 6L); - assertValues(stats, 1, 512L, 5L, 512L, 5L, 5L); + assertValues(stats, 0, SECOND_IN_MILLIS + (HOUR_IN_MILLIS / 2), 768L, 7L, 768L, 7L, 6L); + assertValues(stats, 1, (HOUR_IN_MILLIS / 2), 512L, 5L, 512L, 5L, 5L); } public void testRecordEntireGapIdentical() throws Exception { @@ -345,11 +345,10 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { history.recordData(0, MINUTE_IN_MILLIS, new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); - history.recordData(0, MINUTE_IN_MILLIS * 2, + history.recordData(0, 2 * MINUTE_IN_MILLIS, new NetworkStats.Entry(2L, 2L, 2L, 2L, 2L)); - assertValues( - history, Long.MIN_VALUE, Long.MAX_VALUE, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN); + assertFullValues(history, UNKNOWN, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN); } public void testIgnoreFieldsRecordIn() throws Exception { @@ -361,7 +360,7 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); partial.recordEntireHistory(full); - assertValues(partial, Long.MIN_VALUE, Long.MAX_VALUE, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L); + assertFullValues(partial, UNKNOWN, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L); } public void testIgnoreFieldsRecordOut() throws Exception { @@ -373,12 +372,12 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); full.recordEntireHistory(partial); - assertValues(full, Long.MIN_VALUE, Long.MAX_VALUE, 0L, 10L, 0L, 0L, 4L); + assertFullValues(full, MINUTE_IN_MILLIS, 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, + before.recordData(0, 4 * MINUTE_IN_MILLIS, 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)); @@ -391,8 +390,8 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { 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); + assertFullValues(before, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L); + assertFullValues(after, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L); } public void testVarLong() throws Exception { @@ -441,9 +440,10 @@ 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) { + private static void assertValues(NetworkStatsHistory stats, int index, long activeTime, + long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { final NetworkStatsHistory.Entry entry = stats.getValues(index, null); + assertEquals("unexpected activeTime", activeTime, entry.activeTime); assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); assertEquals("unexpected txBytes", txBytes, entry.txBytes); @@ -451,9 +451,17 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertEquals("unexpected operations", operations, entry.operations); } - private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes, + private static void assertFullValues(NetworkStatsHistory stats, long activeTime, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { + assertValues(stats, Long.MIN_VALUE, Long.MAX_VALUE, activeTime, rxBytes, rxPackets, txBytes, + txPackets, operations); + } + + private static void assertValues(NetworkStatsHistory stats, long start, long end, + long activeTime, long rxBytes, long rxPackets, long txBytes, long txPackets, + long operations) { final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); + assertEquals("unexpected activeTime", activeTime, entry.activeTime); assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); assertEquals("unexpected txBytes", txBytes, entry.txBytes); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index c679dcf9ac7d0..e72d09fe00988 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -1063,8 +1063,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); } } + } catch (NullPointerException e) { + throw new IllegalStateException("problem parsing stats: " + e); + } catch (NumberFormatException e) { + throw new IllegalStateException("problem parsing stats: " + e); } catch (IOException e) { - Slog.w(TAG, "problem parsing stats: " + e); + throw new IllegalStateException("problem parsing stats: " + e); } finally { IoUtils.closeQuietly(reader); } @@ -1345,8 +1349,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); } } + } catch (NullPointerException e) { + throw new IllegalStateException("problem parsing stats: " + e); + } catch (NumberFormatException e) { + throw new IllegalStateException("problem parsing stats: " + e); } catch (IOException e) { - Slog.w(TAG, "problem parsing stats: " + e); + throw new IllegalStateException("problem parsing stats: " + e); } finally { IoUtils.closeQuietly(reader); } diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index 24b6ac3c37747..2155147854253 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -532,9 +532,12 @@ public class ThrottleService extends IThrottleManager.Stub { mLastRead = 0; mLastWrite = 0; } + } catch (IllegalStateException e) { + Slog.e(TAG, "problem during onPollAlarm: " + e); } catch (RemoteException e) { - Slog.e(TAG, "got remoteException in onPollAlarm:" + e); + Slog.e(TAG, "problem during onPollAlarm: " + e); } + // don't count this data if we're roaming. boolean roaming = "true".equals( SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));